Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Bcmath Cast Laravel Package

yuwuhsien/laravel-bcmath-cast

Eloquent casts for PHP 8.4's BCMath\Number object API. Turn model attributes into arbitrary-precision decimals and do natural operator-based arithmetic—add, subtract, multiply, divide—without bcadd/bcmul boilerplate. Requires PHP 8.4+ and Laravel 12+.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • The dependency surface is one Eloquent cast class (AsDecimal) with no service provider, config file, migration, container binding, or runtime dependency. It operates purely at the persistence boundary — normalizing on write, hydrating on read — and never intercepts requests, queue jobs, or domain services. This is the lowest-cost integration profile available in the Laravel ecosystem: no bootstrap ordering concerns, no package-discovery interactions, no instrumentation to reconcile.
  • The hydrated value is a PHP language built-in (BCMath\Number), not a package-owned class. Domain code receiving these values depends on the PHP runtime, not on yuwuhsien/*. That decouples vendor lock-in from type lock-in and is the most favorable single property in this evaluation: if upstream is abandoned, models still hydrate native objects and only the casts() wiring needs reimplementation (roughly one class).
  • Fit is good in layered codebases only if domain services do not consume Eloquent models directly. Where they do, the attribute contract silently shifts from string to Number inside code that no cast declaration makes visible. That invisible type change is the primary architectural coupling to manage.
  • Scope is deliberately narrow: no currency, no minor-unit representation, no allocation/splitting, no FX, no formatting. Correct scoping for a cast — but it creates an organizational risk, because the package is easy to mis-sell internally as "our money layer." It is a precision primitive, not a monetary policy. An ADR should say so before anyone adopts it.

Integration feasibility

  • Mechanically trivial: composer require plus one line per attribute in casts(). A single engineer completes a pilot in under a day. The cost lives entirely in the audit and boundary work that must surround it, not in installation.
  • Two hard gates must be confirmed before any planning commitment: PHP 8.4+ and Laravel 12+. Laravel 11 support was dropped in v1.2.2 with no shim and no feature flag; there is no degraded mode — Composer simply refuses to install. Every application, internal tool, export worker, and reporting service that hydrates these models must independently clear both gates.
  • The cast delivers zero correctness benefit on FLOAT/DOUBLE columns. The schema audit is a prerequisite deliverable with its own owner and estimate, not a follow-on task. Treating it as incidental is the most likely way this initiative ships effort without shipping value.
  • Only one cast can be attached per attribute, so adoption replaces existing 'price' => 'decimal:2' declarations rather than augmenting them. Mechanical to write, but it changes the return type for every reader of that attribute across the codebase.

Technical risk

  • Vendor maturity is the headline risk and should be stated without softening. Two stars, zero dependents, one maintainer, no published security policy, no LTS or deprecation policy, no support SLA. We would be among the first production consumers. The mitigating factor is real: MIT-licensed, ~1 class of surface area, forkable or reimplementable in hours. The honest classification is "low blast radius, unproven upstream" — not "safe." Positively, the maintainer has already cut compatibility releases for Laravel 13 and PHP 8.5 ahead of need, which is better hygiene than the star count implies.
  • The serialization contract changes. Decimal attributes emit as JSON strings. Semantically correct; it breaks any client with numeric-typed fields — mobile apps, TypeScript interfaces, downstream consumers doing client-side arithmetic. That is an API versioning decision, not a side effect of a Composer update, and must be sequenced and communicated as such.
  • Object semantics leak into existing code. BCMath\Number is an object: === becomes identity comparison, in_array/array_search without strict flags behave unexpectedly, is_numeric() returns false, string interpolation and number_format() need explicit conversion, and collection sortBy on these attributes needs a comparator. None of this is a package defect — all of it is migration work that is invisible in a diff.
  • Round-trip fidelity has already been a defect class. v1.2.2's changelog lists "align set() input validation with what get() can parse back" — exactly the failure mode where a write succeeds and a later read throws or truncates. Assume field semantics are pre-stable relative to our edge cases (precision overflow, empty string, null, scientific notation) and gate acceptance on our own round-trip suite.
  • Scale and rounding are not governed by the package. The cast yields a value; it imposes no rounding mode, scale, or intermediate-precision policy. For monetary fields those are consequential and remain entirely ours to define and enforce.
  • Float inputs are accepted with a warning. A warning is not a guardrail. An unvalidated 29.99 arriving in a JSON payload can silently defeat the precision guarantee the package exists to provide. Enforcement belongs in FormRequest validation.
  • SQL vs PHP aggregate divergence is a real, low-visibility risk. SUM() over DECIMAL in MySQL/Postgres and a PHP-side reduction over Number objects can disagree at scale boundaries. Divergence surfaces during a finance reconciliation, not in a test suite.

Key questions to resolve before committing

  1. What are the exact PHP and Laravel versions of every application, internal tool, export job, and reporting service that will hydrate these models?
  2. Has the schema audit been completed? How many FLOAT/DOUBLE columns hold currency, rate, or quantity values, and who owns converting them?
  3. Which consumers read these attributes today — PDF generators, CSV/Excel exporters, search indexers, analytics pipelines, SDKs, mobile clients — and what type does each expect?
  4. What is our per-field rounding and scale policy, where is it documented, and where is it enforced?
  5. Where is the source of truth for aggregates — SQL or PHP — and has parity been verified on representative datasets?
  6. Do these models cross serialization boundaries (cache, session, queue payloads, Octane requests)? Do Number attributes survive them intact?
  7. What is the exit trigger? Proposed: upstream inactive for 12 months, a breaking change to BCMath\Number in PHP, or any unresolvable round-trip defect.

Integration Approach

Stack fit

  • Confirmed target matrix is Laravel 12/13 on PHP 8.4/8.5, matching project CI. If our stack is not already there, this is an upgrade program with the package as a downstream beneficiary — never a justification to start an upgrade on its own merits.
  • Zero runtime dependencies and no service provider mean no interaction with container bootstrapping, package discovery, Octane warm-up, or existing instrumentation. Coexistence risk is low; rollback is a one-line-per-attribute revert.
  • Adopt a policy split, enforceable in code review and codified in the ADR: currency, tax rates, unit prices, quantities, and any field with contractual precision use AsDecimal; counters, IDs, sequence numbers, and analytical aggregates stay native. Without that rule the cast spreads into fields where it adds cost without adding correctness.

Migration path

  1. Upgrade PHP to 8.4 and Laravel to 12 as an independent, separately justified milestone. Do not bundle this package into the upgrade PR — it muddies attribution if something regresses.
  2. Schema audit and conversion — inventory every FLOAT/DOUBLE column in correctness-sensitive domains, convert to DECIMAL with explicit precision and scale, and verify existing rows survive without rounding drift. Backfill and verification scripts are part of the deliverable, not cleanup.
  3. Pilot on one low-risk, high-visibility model (e.g., catalog price) with a full acceptance suite: write string → read Number; write Number → read back; assert scale and equality; assert null; assert 0; assert empty string; assert precision overflow fails loudly rather than silently truncating.
  4. Roll out model by model — orders, invoices, line items, tax rates — each as an independent, revertible PR. Avoid one sweeping PR; it makes bisecting a regression expensive.
  5. Fix the boundaries last — API resources, FormRequest rules (decimal, numeric, string-aware max), client-side types, CSV/PDF exporters, search index mappings, and any ===/is_numeric/number_format call sites.

Compatibility

  • Replacing decimal:2 changes the returned type from string to Number for every read path. Any code doing strict comparison, type juggling, sorting, or implicit string conversion on that attribute is a candidate break and must be enumerated during the pilot, not discovered in production.
  • JSON output flips from numbers to strings. Version the affected endpoints or coordinate a client release alongside the rollout; do not let a backend deploy silently change a mobile contract.
  • Validation is the enforcement layer, not the cast: reject floats and malformed numerics at the FormRequest boundary and align the accepted input grammar with what get() can round-trip.
  • PHPUnit/Pest fixtures and factories that set decimal attributes must be checked — string literals are safe, numeric float literals are not.

Sequencing

  • Milestone 0: stack gates verified across all consumers (go/no-go). Milestone 1: PHP/Laravel upgrade shipped independently. Milestone 2: schema audit complete with owner sign-off. Milestone 3: pilot model merged behind acceptance tests. Milestone 4: model-by-model rollout. Milestone 5: boundary fixes and client communication. Each milestone is a checkpoint at which the program can stop without leaving partial state.

Operational Impact

Maintenance

  • Runtime maintenance is near zero: no config to drift, no migrations published, no service provider to keep registered. The real recurring cost is the policy layer we own — rounding mode, scale, validation rules, and the round-trip test suite.
  • Plan for a fork contingency now, while it is cheap: vendor the single cast class or keep an internal patch branch if upstream stalls. Assign a named owner and record the reimplementation estimate (hours, not weeks) in the ADR so the exit option is credible rather than theoretical.
  • Track upstream releases as a low-cadence review item (quarterly), with a specific trigger on any BCMath\Number change in a new PHP minor.

Support

  • Bus factor is one, there is no SLA, and we have no leverage as a two-star consumer — except that the project demonstrably accepts external PRs (Laravel 13 support arrived from an outside contributor). Upstream contribution is a viable support strategy for a codebase this small.
  • Internal support burden concentrates in two questions the team will ask repeatedly: "why is this field a string in JSON?" and "why doesn't === work?" Answer both once in an ADR plus a short engineering note; otherwise they are re-asked in every code review.

Scaling

  • Every hydrated decimal attribute allocates an object. Benchmark the slowest path that materializes many decimal-bearing rows (report exports, invoice batch jobs) rather than trusting a microbenchmark on a single model.
  • Arithmetic inside loops over large result sets moves work from the database into PHP. Where an aggregate is needed at scale, prefer the SQL SUM() over DECIMAL and verify parity against the PHP reduction on a representative dataset.
  • Confirm that models with Number attributes survive cache, session, and queue serialization intact; if they do not, define a serialization policy before those models reach a job payload.

Failure modes

  • Silent precision loss: a float reaches the attribute without validation — no exception, just wrong numbers downstream.
  • Precision loss in storage: the schema audit misses a FLOAT column, making the cast cosmetic.
  • Truncation at DECIMAL overflow: a value exceeding column precision is silently rounded by the database rather than rejected.
  • Client breakage: a numeric-typed consumer receives a string and either errors or coerces incorrectly.
  • Aggregate divergence: SQL and PHP disagree at a scale boundary and the discrepancy appears in reconciliation, not in CI.
  • Type errors at the seams: existing strict comparisons and formatting calls fail on Number objects after rollout.
  • Upstream abandonment or a PHP-level change to BCMath\Number, mitigated by the fork plan.
  • Regression on cast removal: the revert path reintroduces string returns and any code written against Number breaks — so revert must be treated as a coordinated change, not a hotfix.

Ramp-up

  • Engineering ramp is short but not zero: one brown-bag covering object semantics, operator arithmetic, validation-at-the-boundary, and the formatting/comparison gotchas. Expect a half-day of confusion per team if skipped.
  • Reviewer checklist: attribute uses AsDecimal only where the policy allows; column is DECIMAL with explicit precision/scale; input validated as string-safe; no strict comparison or number_format on the raw attribute; round-trip test present.
  • Product/analytics ramp matters too: anyone querying these fields outside the application sees strings in JSON and exact decimals in SQL, and needs to know which one is authoritative for reporting.

Recommendation for the TPM Adopt, but as a one-class accelerator with a fork plan, not as a dependency of record. The value is real and bounded; the risk is concentrated in vendor immaturity (mitigable) and in the schema/boundary work (not mitigable by the package). Gate the commitment on the stack gates and the schema audit, run the pilot with the acceptance suite described above, and treat the exit criteria as part of the design rather than an afterthought.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics
robbiep/afterthedeadline
frankkessler/incontact-laravel-oauth2-rest
jlxh/laravel-userlog
davidmgilo/timetables
qylinfly/role-permission
reflect-digital/conduit
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr