aichadigital / lara100
Laravel cast for handling decimal values as base-100 integers (cents/centesimals)
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/aichadigital/lara100
Requires
- php: ^8.3|^8.4
- illuminate/contracts: ^11.0|^12.0
- illuminate/database: ^11.0|^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.25
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.6
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- pestphp/pest-plugin-mutate: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
This package is auto-updated.
Last update: 2025-10-11 14:22:54 UTC
README
A Laravel package that provides a custom Eloquent cast for handling monetary/decimal values by storing them as integers (cents) in the database while working with decimals in your PHP code, eliminating floating-point precision errors.
Why Lara100?
Floating-point arithmetic in PHP (and most programming languages) can lead to precision errors:
0.1 + 0.2 === 0.3 // false! 😱 // Result: 0.30000000000000004
This is particularly problematic when dealing with:
- 💰 Monetary values (prices, amounts, balances)
- 📊 Percentages (tax rates, discounts)
- 📏 Any centesimal measurements
Lara100 solves this by storing values as integers (cents) in the database, while letting you work with familiar decimal values in your code.
// In your database: 1999 (integer - cents) // In your application: 19.99 (decimal - dollars/euros)
Installation
You can install the package via Composer:
composer require aichadigital/lara100
Configuration (Optional)
You can optionally publish the configuration file:
php artisan vendor:publish --tag="lara100-config"
This will create a config/lara100.php
file where you can configure:
- Rounding mode (default:
PHP_ROUND_HALF_UP
) - BCMath usage (default:
false
)
Alternatively, you can set these via environment variables in your .env
:
# Rounding mode (default: 2 = PHP_ROUND_HALF_UP) # 1 = PHP_ROUND_HALF_UP (standard for Spain/EU) # 2 = PHP_ROUND_HALF_DOWN # 3 = PHP_ROUND_HALF_EVEN (Banker's rounding for accounting) # 4 = PHP_ROUND_HALF_ODD LARA100_ROUNDING_MODE=1 # Enable BCMath for arbitrary precision (requires bcmath extension) LARA100_USE_BCMATH=false
Requirements
- PHP 8.3 or 8.4
- Laravel 11.x or 12.x
- Optional: BCMath extension (for high-precision calculations)
Usage
Basic Usage (Cast)
Apply the Base100
cast to your model attributes:
use AichaDigital\Lara100\Casts\Base100; use Illuminate\Database\Eloquent\Model; class Product extends Model { protected function casts(): array { return [ 'price' => Base100::class, 'cost' => Base100::class, 'tax' => Base100::class, ]; } }
Now you can work with decimals in your application while storing integers in the database:
$product = new Product; $product->price = 19.99; // You set: 19.99 (decimal) $product->save(); // DB stores: 1999 (integer cents) echo $product->price; // You get: 19.99 (decimal) // Arithmetic operations work perfectly with decimals $total = $product->price + $product->tax; // 19.99 + 2.50 = 22.49 ✅
Advanced Usage (Trait)
For convenience, you can use the HasBase100
trait to apply the cast to multiple attributes at once:
use AichaDigital\Lara100\Concerns\HasBase100; use Illuminate\Database\Eloquent\Model; class Product extends Model { use HasBase100; protected function base100Attributes(): array { return ['price', 'cost', 'tax', 'discount']; } }
The trait automatically applies the Base100
cast to all specified attributes.
How It Works
Database → Application (GET)
// Database stores: 1999 (INTEGER cents) // Cast converts to: 19.99 (DECIMAL) $product->price; // 19.99
Application → Database (SET)
// Application receives: 19.99 (DECIMAL) // Cast converts to: 1999 (INTEGER cents) $product->price = 19.99; $product->save(); // Stores 1999 in DB
Rounding Behavior
By default, the cast uses Round Half Up (PHP_ROUND_HALF_UP):
0.555
→0.56
(rounds up when exactly halfway)0.554
→0.55
This is the standard rounding mode used in Spain, the EU, and most countries.
Configuring Rounding Mode
You can configure the rounding mode globally via config or per-attribute:
Global configuration (affects all casts):
# In .env LARA100_ROUNDING_MODE=1 # PHP_ROUND_HALF_UP (default)
Per-attribute override (in your model):
use AichaDigital\Lara100\Casts\Base100; protected function casts(): array { return [ 'price' => Base100::class, // Uses config default 'tax' => new Base100(PHP_ROUND_HALF_EVEN), // Banker's rounding 'discount' => new Base100(PHP_ROUND_HALF_DOWN), // Always round down ]; }
Available Rounding Modes
Mode | Constant | Behavior | Use Case |
---|---|---|---|
Half Up | PHP_ROUND_HALF_UP (1) |
0.555→0.56, 0.545→0.55 | Spain/EU standard |
Half Even | PHP_ROUND_HALF_EVEN (3) |
0.555→0.56, 0.545→0.54 | Accounting (Banker's) |
Half Down | PHP_ROUND_HALF_DOWN (2) |
0.555→0.55, 0.545→0.54 | Conservative rounding |
Half Odd | PHP_ROUND_HALF_ODD (4) |
0.555→0.55, 0.545→0.55 | Specialized cases |
BCMath Support
For maximum precision with very large amounts, enable BCMath:
LARA100_USE_BCMATH=true
Or per-attribute:
'balance' => new Base100(useBcmath: true),
Note: Requires the bcmath
PHP extension to be installed.
Examples
Working with Monetary Values
$invoice = new Invoice; $invoice->subtotal = 100.00; // You set: $100.00 (decimal) $invoice->tax = 13.00; // You set: $13.00 (decimal) $invoice->total = 113.00; // You set: $113.00 (decimal) $invoice->save(); // DB stores: 10000, 1300, 11300 (integers) // Calculate percentage (works naturally with decimals) $taxRate = ($invoice->tax / $invoice->subtotal) * 100; // 13% // Display to user (already a decimal!) $formatted = '$' . number_format($invoice->total, 2); // "$113.00"
Performing Calculations
$product = Product::find(1); // price = 19.99 (DB has 1999) $quantity = 3; $lineTotal = $product->price * $quantity; // 59.97 (19.99 × 3) $discount = 5.00; // $5.00 discount $finalTotal = $lineTotal - $discount; // 54.97 ✅ // Works naturally with decimal arithmetic!
Handling Edge Cases
// Zero values $product->price = 0.00; // Stores 0 in DB // Negative values (refunds, discounts) $refund->amount = -25.00; // Stores -2500 in DB (negative cents) // Large numbers $property->price = 500000.00; // Stores 50000000 in DB ($500,000.00)
Database Schema
Your database columns should be defined as INTEGER
(to store cents):
Schema::create('products', function (Blueprint $table) { $table->id(); $table->integer('price')->default(0); // Stores cents: 1999 = $19.99 $table->integer('cost')->default(0); // Stores cents: 1500 = $15.00 $table->integer('tax')->default(0); // Stores cents: 250 = $2.50 $table->timestamps(); });
Why INTEGER instead of DECIMAL?
- ✅ Better performance (integer operations are faster)
- ✅ Less storage space
- ✅ No floating-point precision issues at database level
- ✅ Compatible with all database engines
Comparison with Alternatives
Solution | DB Column Type | PHP Value Type | Precision | Package Size |
---|---|---|---|---|
Lara100 | INTEGER (cents) |
float (19.99) |
✅ Perfect | Lightweight cast |
moneyphp/money | INTEGER (cents) |
Money object |
✅ Perfect | Full-featured library |
brick/money | INTEGER (cents) |
Money object |
✅ Perfect | Full-featured library |
Native DECIMAL | DECIMAL(10,2) |
float (19.99) |
⚠️ Precision issues | No package needed |
Key Differences:
- Lara100: Lightweight cast that stores integers in DB (efficient), but lets you work with decimals in PHP (natural)
- Money libraries: Full-featured libraries with objects, currency conversion, formatting, etc.
- Native DECIMAL: Traditional approach, works with floats in PHP (precision issues)
Choose Lara100 when:
- ✅ You want a simple, Laravel-native solution
- ✅ You prefer working with familiar decimal values (19.99)
- ✅ You want efficient integer storage in the database
- ✅ You don't need currency conversions or complex money operations
- ✅ You want to avoid float precision errors without heavy dependencies
Consider alternatives when:
- ❌ You need multi-currency support
- ❌ You need complex monetary operations (allocation, distribution, rounding strategies)
- ❌ You prefer working with Money objects instead of scalar decimals
- ❌ You need advanced formatting and localization features
Testing
The package includes comprehensive tests for both the cast and trait:
composer test
Run tests with coverage:
composer test-coverage
Run tests in parallel:
composer test-parallel
Code Quality
Run PHPStan static analysis:
composer phpstan
Run Laravel Pint code formatter:
composer format
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Security Vulnerabilities
If you discover a security vulnerability, please send an e-mail to Abdelkarim Mateos Sanchez via abdelkarim.mateos@castris.com.
Credits
License
The MIT License (MIT). Please see License File for more information.
About AichaDigital
AichaDigital is a ITt company focused on IT services.