Saya telah mewarisi tumpukan besar kode warisan yang ditulis dalam PHP di atas basis data MySQL. Hal yang saya perhatikan adalah aplikasi yang digunakan doubles
untuk penyimpanan dan manipulasi data.
Sekarang saya menemukan banyak posting yang menyebutkan bagaimana double
tidak cocok untuk operasi moneter karena kesalahan pembulatan. Namun, saya belum menemukan solusi lengkap tentang bagaimana nilai moneter harus ditangani dalam kode PHP dan disimpan dalam database MySQL.
Apakah ada praktik terbaik dalam menangani uang secara khusus dalam PHP?
Hal yang saya cari adalah:
- Bagaimana seharusnya data disimpan dalam database? jenis kolom? ukuran?
- Bagaimana seharusnya penanganan data dalam penambahan normal, pengurangan. perkalian atau pembagian?
- Kapan saya harus membulatkan nilainya? Berapa banyak pembulatan dapat diterima jika ada?
- Apakah ada perbedaan antara penanganan nilai moneter besar dan rendah?
Catatan: Sebuah SANGAT disederhanakan kode contoh dari bagaimana saya bisa menemukan nilai-nilai uang dalam kehidupan sehari-hari (berbagai masalah keamanan diabaikan untuk penyederhanaan Tentu saja dalam kehidupan nyata saya tidak akan menggunakan kode saya seperti ini.):
$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?
Saya harap Anda menggunakan kode sampel ini sebagai sarana untuk membawa lebih banyak kasus penggunaan dan tidak menerimanya secara harfiah.
Pertanyaan Bonus Jika saya menggunakan ORM seperti Doctrine atau PROPEL, betapa berbedanya menggunakan uang dalam kode saya.
decimal
tipe dalam C # memiliki presisi terbatas tetapi sangat cocok untuk nilai moneter.Jawaban:
Ini bisa sangat sulit untuk menangani angka dengan PHP / MySQL. Jika Anda menggunakan desimal (10,2) dan nomor Anda lebih panjang atau memiliki ketepatan yang lebih tinggi maka akan terpotong tanpa kesalahan (kecuali Anda mengatur mode yang tepat untuk server db Anda).
Untuk menangani nilai besar atau nilai presisi tinggi, Anda dapat menggunakan perpustakaan seperti BCMath sehingga Anda dapat melakukan operasi dasar pada angka besar dan menjaga presisi yang diperlukan.
Saya tidak yakin perhitungan persis apa yang akan Anda buat, tetapi Anda juga harus ingat bahwa (0,22 * 0,4576) + (0,78 * 0,4576) tidak akan sama dengan 0,4576 jika Anda tidak akan menggunakan presisi yang tepat melalui proses.
Ukuran maksimum DECIMAL di MySQL adalah 65 sehingga harus lebih dari cukup untuk tujuan apa pun. Jika Anda menggunakan jenis bidang DECIMAL itu akan dikembalikan sebagai string terlepas dari penggunaan ORM atau hanya PDO / mysql (i).
DECIMAL dengan presisi yang Anda butuhkan. Jika Anda menggunakan nilai tukar maka Anda akan membutuhkan setidaknya empat tempat desimal
Gunakan BCMath untuk berada di sisi yang aman dan mengapa menggunakan float mungkin bukan ide yang baik
Untuk nilai moneter normal dua tempat desimal dapat diterima tetapi Anda mungkin perlu lebih banyak jika misalnya Anda menggunakan nilai tukar.
Tergantung pada apa yang Anda maksud dengan besar. Jelas ada perbedaan antara penanganan angka dengan presisi tinggi.
sumber
Pekerjaan sederhana di sekitar adalah menyimpannya sebagai bilangan bulat. 99,99 disimpan sebagai 9999. Jika ini tidak berhasil (dan ada banyak alasan mengapa ini bisa menjadi pilihan yang buruk), Anda dapat menggunakan jenis Desimal. http://dev.mysql.com/doc/refman/5.0/en/precision-math-decimal-changes.html di sisi mysql. Di sisi php saya menemukan ini /programming/3244094/decimal-type-in-php yang mungkin apa yang Anda cari.
Pertanyaan bonus: Sulit dikatakan. Orm akan bekerja berdasarkan tipe data yang dipilih. Saya akan mengatakan bahwa Anda dapat melakukan beberapa hal dengan abstraksi untuk membantu tetapi masalah khusus ini tidak diatasi hanya dengan pindah ke ORM.
sumber
Saya akan mencoba memasukkan pengalaman saya dalam hal ini:
Saya telah menggunakan
DECIMAL(10,2)
untuk mysql tanpa masalah (8 selesai dan 2 desimal == 99,999,999,99 == jumlah besar), tetapi ini tergantung pada kisaran uang yang harus Anda tutupi. Jumlah besar harus diambil dengan hati-hati (misalnya nilai float OS max). Pada bagian desimal saya menggunakan 2 nilai untuk menghindari nilai truncate atau pembulatan. Mengambil uang ada beberapa kasus di mana Anda membutuhkan lebih banyak desimal (dalam hal ini Anda perlu memastikan pengguna akan bekerja dengan semuanya, jika tidak, ini adalah data yang tidak berguna)Bekerja dengan satu mata uang dan tabel pertukaran (dengan tanggal). Dengan cara ini Anda memastikan bahwa Anda akan selalu menyimpan jumlah yang benar. Ekstra: menyimpan nilai lengkap dan membuat tampilan dengan hasil perhitungan. Ini akan membantu Anda memperbaiki nilai dengan cepat
Sekali lagi, tergantung pada kisaran uang sistem Anda. Selalu berpikir dalam istilah KISS kecuali Anda harus jatuh dalam kekacauan pertukaran mata uang
Bergantung pada OS dan bahasa pemrograman Anda, Anda selalu perlu memeriksa nilai maks dan min
sumber
$valueToBeStored= $a * $b;
jika$a
dan$b
keduanya dibaca sebagai desimal dari database saya pikir mereka akan dilemparkan kedouble
dalam PHP, kan? apakah itu akan mempengaruhi angka?$a
dan$b
diambil dari db? jadi, dalam contoh saya Anda tidak perlu menyimpan$valueToBeStored
karena Anda akan selalu memiliki sumber$a
dan$b
data. Jadi, Anda dapat bekerja secara terprogram nilai dalam suatu fungsi atau lebih atau membuat tampilan mysql dengan hasil kolom. Dengan cara ini, jika ada nilai yang harus diubah, Anda tidak perlu khawatir mengubah beberapa tempat (rawan kesalahan)