Làm thế nào để xử lý các giá trị tiền tệ trong PHP và MySql?


16

Tôi đã thừa hưởng một đống mã kế thừa khổng lồ được viết bằng PHP trên cơ sở dữ liệu MySQL. Điều tôi nhận thấy là ứng dụng sử dụng doublesđể lưu trữ và thao tác dữ liệu.

Bây giờ tôi đã xem qua rất nhiều bài viết đề cập đến việc doublekhông phù hợp với hoạt động tiền tệ vì các lỗi làm tròn số. Tuy nhiên, tôi vẫn chưa bắt gặp một giải pháp hoàn chỉnh về cách xử lý các giá trị tiền tệ trong mã PHP và được lưu trữ trong cơ sở dữ liệu MySQL.

Có cách thực hành tốt nhất khi xử lý tiền cụ thể trong PHP không?

Những điều tôi đang tìm kiếm là:

  1. Dữ liệu nên được lưu trữ trong cơ sở dữ liệu như thế nào? loại cột? kích thước?
  2. Làm thế nào các dữ liệu nên được xử lý trong cộng, trừ bình thường. Nhân hay chia?
  3. Khi nào tôi nên làm tròn các giá trị? Bao nhiêu làm tròn là chấp nhận được nếu có?
  4. Có sự khác biệt giữa xử lý các giá trị tiền tệ lớn và giá trị thấp?

Lưu ý: Một RẤT đơn giản hóa mẫu mã như thế nào tôi có thể gặp các giá trị tiền trong cuộc sống hàng ngày (vấn đề an ninh khác nhau đã bị lờ đi cho đơn giản hóa Tất nhiên trong cuộc sống thực tôi sẽ không bao giờ sử dụng mã của tôi như thế này.):

$a= $_POST['price_in_dollars']; //-->(ex: 25.06) will be read as a string should it be cast to double?
$b= $_POST['discount_rate'];//-->(ex: 0.35) value will always be less than 1
$valueToBeStored= $a * $b; //--> any hint here is welcomed 

$valueFromDatabase= $row['price']; //--> price column in database could be double, decimal,...etc.

$priceToPrint=$valueFromDatabase * 0.25; //again cast needed or not?

Tôi hy vọng bạn sử dụng mã mẫu này như một phương tiện để đưa ra nhiều trường hợp sử dụng hơn và không lấy nó theo nghĩa đen tất nhiên.

Câu hỏi thưởng Nếu tôi sử dụng ORM như Học thuyết hoặc PROPEL, thì việc sử dụng tiền trong mã của tôi sẽ khác nhau như thế nào.


1
Tôi không biết PHP nhưng biết thuật ngữ là vô giá trong các tình huống này đối với google-fu, thuật ngữ bạn đang tìm kiếm là "độ chính xác tùy ý" mà google phản hồi với php.net/manual/en/book.bc.php
Jimmy Hoffa

1
@Jimmy Hoffa: độ chính xác tùy ý nói chung không phải là thứ bạn cần, thứ gì đó có thể biểu diễn chính xác và hoạt động với phân số thập phân là. Tất nhiên, bạn thường xuyên tìm thấy cả hai kết hợp, nhưng ví dụ, decimalloại trong C # có độ chính xác hạn chế nhưng hoàn toàn phù hợp với các giá trị tiền tệ.
Michael Borgwardt

1
@MichaelBorgwardt đúng, tôi quên các loại hợp lý vì rất nhiều ngôn ngữ không có chúng. Cuộc gọi tốt
Jimmy Hoffa

Đã làm việc rất nhiều với các loại tiền tệ và mã kế thừa, tôi muốn nói rằng hãy lưu trữ nó trong các số nguyên và sử dụng xu thay vì đô la. Mặc dù vậy, hãy cẩn thận, 32 số nguyên có thể giữ một lượng nhỏ đáng ngạc nhiên.4) số nguyên 32 bit chỉ có thể giữ số tiền 21 triệu nếu không dấu và sử dụng xu.
Pieter B

Bên cạnh đó, mã mẫu của bạn hiển thị giá và giảm giá đang được gửi đi làm tôi kinh hoàng. Hy vọng rằng nó đơn giản đến mức nó không phản ánh việc sử dụng thực sự, nhưng theo mệnh giá, có vẻ như bạn đang tin tưởng vào trình duyệt để cho bạn biết giá của một mặt hàng bằng cách gửi lại trường ẩn hoặc một số trường như vậy.
Carson63000

Câu trả lời:


6

Có thể khá khó khăn để xử lý các số với PHP / MySQL. Nếu bạn sử dụng số thập phân (10,2) và số của bạn dài hơn hoặc có độ chính xác cao hơn, nó sẽ bị cắt ngắn mà không có lỗi (trừ khi bạn đặt chế độ phù hợp cho máy chủ db của mình).

Để xử lý các giá trị lớn hoặc giá trị độ chính xác cao, bạn có thể sử dụng thư viện như BCMath, nó sẽ cho phép bạn thực hiện các thao tác cơ bản trên số lượng lớn và giữ độ chính xác cần thiết.

Tôi không chắc chính xác những gì bạn sẽ thực hiện nhưng bạn cũng phải nhớ rằng (0,22 * 0,4576) + (0,78 * 0,4576) sẽ không bằng 0,4576 nếu bạn không sử dụng độ chính xác phù hợp trong suốt quá trình.

Kích thước tối đa của DECIMAL trong MySQL là 65 vì vậy nó là quá đủ cho bất kỳ mục đích nào. Nếu bạn sử dụng loại trường DECIMAL, nó sẽ được trả về dưới dạng chuỗi bất kể việc sử dụng ORM hay chỉ đơn giản là PDO / mysql (i).

Dữ liệu nên được lưu trữ trong cơ sở dữ liệu như thế nào? loại cột? kích thước?

DECIMAL với độ chính xác bạn cần. Nếu bạn đang sử dụng tỷ giá hối đoái thì bạn sẽ cần ít nhất bốn chữ số thập phân

Làm thế nào các dữ liệu nên được xử lý trong cộng, trừ bình thường. Nhân hay chia?

Sử dụng BCMath để ở bên lưu và tại sao sử dụng float có thể không phải là ý tưởng hay

Khi nào tôi nên làm tròn các giá trị? Bao nhiêu làm tròn là chấp nhận được nếu có?

Đối với các giá trị tiền tệ, hai vị trí thập phân bình thường có thể chấp nhận được nhưng bạn có thể cần nhiều hơn nếu ví dụ bạn đang sử dụng tỷ giá hối đoái.

Có sự khác biệt giữa xử lý các giá trị tiền tệ lớn và giá trị thấp?

Phụ thuộc vào những gì bạn có nghĩa là lớn. Chắc chắn có một sự khác biệt giữa các số xử lý với độ chính xác cao.


9

Một công việc đơn giản xung quanh là lưu trữ chúng dưới dạng số nguyên. 99,99 được lưu trữ dưới dạng 9999. Nếu điều này không hoạt động (và có nhiều lý do tại sao điều này có thể là một lựa chọn tồi), bạn có thể sử dụng loại Decimal. http://dev.mysql.com/doc/refman/5.0/en/precision-math-decimal-changes.html về phía mysql. Về phía php tôi đã tìm thấy /programming/3244094/decimal-type-in-php này có thể là những gì bạn đang theo đuổi.

Câu hỏi thưởng: Khó nói. Orm sẽ hoạt động dựa trên các loại dữ liệu được chọn. Tôi sẽ nói rằng bạn có thể làm một số thứ với sự trừu tượng để giúp đỡ nhưng vấn đề cụ thể này không được giải quyết đơn giản bằng cách chuyển sang ORM.


1
FWIW, Drupal Commerce sử dụng thủ thuật 9999 để lưu trữ giá của nó.
Florian Margaine

1
"Và có nhiều lý do tại sao điều này có thể là một lựa chọn tồi" Bạn có thể chia sẻ một số vấn đề với thực tiễn này không?
Songo


@MichaelBorgwardt Cảm ơn thông tin, Songo xin lỗi vì sự chậm trễ, cơn bão đã đưa chúng tôi ra ngoài :)
Ominus

3

Tôi sẽ cố gắng đưa kinh nghiệm của mình vào việc này:

Những điều tôi đang tìm kiếm là:

Dữ liệu nên được lưu trữ trong cơ sở dữ liệu như thế nào? loại cột? kích thước?

Tôi đã sử dụng DECIMAL(10,2)cho mysql mà không gặp vấn đề gì (8 lần hoàn thành và 2 số thập phân == 99.999.999,99 == số tiền rất lớn), nhưng điều này phụ thuộc vào phạm vi tiền bạn sẽ phải trả. Số lượng lớn nên được thực hiện cẩn thận (ví dụ giá trị float tối đa của hệ điều hành). Trên phần thập phân, tôi sử dụng 2 giá trị để tránh cắt xén cũng không làm tròn giá trị. Lấy tiền có một vài trường hợp bạn cần nhiều số thập phân hơn (trong trường hợp đó bạn cần đảm bảo người dùng sẽ làm việc với tất cả chúng, nếu không thì là dữ liệu vô dụng)

Làm thế nào các dữ liệu nên được xử lý trong cộng, trừ bình thường. Nhân hay chia?

Làm việc với một loại tiền tệ và một bảng trao đổi (có ngày). Bằng cách này, bạn đảm bảo rằng bạn sẽ luôn có số tiền chính xác được lưu. Một bổ sung: lưu các giá trị hoàn chỉnh và tạo ra một khung nhìn với kết quả tính toán. Điều này sẽ giúp bạn sửa chữa các giá trị một cách nhanh chóng

Khi nào tôi nên làm tròn các giá trị? Bao nhiêu làm tròn là chấp nhận được nếu có?

Một lần nữa, phụ thuộc vào phạm vi tiền hệ thống của bạn. Luôn suy nghĩ theo thuật ngữ KISS trừ khi bạn cần rơi vào mớ hỗn độn của trao đổi tiền tệ

Có sự khác biệt giữa xử lý các giá trị tiền tệ lớn và giá trị thấp?

Tùy thuộc vào hệ điều hành và ngôn ngữ lập trình của bạn, bạn luôn cần kiểm tra giá trị tối đa và tối thiểu của mình


Cảm ơn câu trả lời, nhưng nếu tôi phải xử lý một cái gì đó như $valueToBeStored= $a * $b;nếu $a$bcả hai đều được đọc dưới dạng số thập phân từ cơ sở dữ liệu, tôi nghĩ rằng chúng sẽ được chuyển sang doubletrong PHP phải không? điều đó sẽ ảnh hưởng đến những con số?
Songo

$a$bđược lấy từ db? vì vậy, trong ví dụ của tôi, bạn không cần phải lưu trữ $valueToBeStoredvì bạn sẽ luôn có nguồn $a$bdữ liệu. Vì vậy, bạn có thể làm việc theo chương trình giá trị trong một hàm hoặc như vậy hoặc tạo một khung nhìn mysql với kết quả cột. Bằng cách này, nếu có bất kỳ giá trị nào phải thay đổi, bạn không phải lo lắng về việc sửa đổi nhiều vị trí (dễ bị lỗi)
Alwin Kesler
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.