petrknap/eloquent
A collection of enhancements and helper classes for Laravel Eloquent: UTC/local datetime casts, Optional wrappers that delegate missing-model handling, and repositories that provide a clean, expressive interface for persistence logic.
petrknap/eloquent is not a framework or an ORM replacement; it is a thin utility layer over Laravel's Eloquent with three concerns: casts (UTC/timezone-aware datetime handling, "private" field handling), optionals (likely nullable/optional value-object semantics), and repositories (a data-access abstraction over Eloquent models). For a TPM the relevant framing is that this is a pattern library, not infrastructure — value comes from codifying conventions (store UTC, keep timezone alongside, hide sensitive fields, isolate query logic) that teams often reimplement badly.
Architecture fit. The casts are the strongest fit. Laravel 10+ introduced casts() as a method and Attribute-based composite accessors; the README example (AsUtc::dateTime(), AsUtc::withTimezone(...), AsPrivate::class) uses those idioms correctly, meaning the package operates at a supported extension point rather than monkey-patching Eloquent internals. That is the lowest-risk class of dependency: it composes with the framework instead of competing with it. The repositories concern is the opposite — it introduces an abstraction layer that Eloquent deliberately avoids, and any repository implementation sitting over Eloquent risks bypassing model events, observers, global scopes, and relationship eager-loading. If a team adopts it, they inherit an architectural opinion (active record vs. repository) that will be hard to unwind. Optionals are the least legible from the excerpt and should be treated as unknown until source review.
Integration feasibility. Mechanically trivial: one Composer require, no service provider bootstrapping implied, no migrations, no config publishing. The real cost is not installation but validation — confirming the illuminate/database and PHP version constraints cover your target framework version, and confirming behaviour across your actual database drivers and timezone data.
Technical risk. The risk profile is dominated by project health rather than code quality:
| Risk | Signal | TPM read |
|---|---|---|
| Bus factor | 0 stars, 0 dependents, single author | Effectively a personal library; no community backstop |
| Supply chain | Release dated 2026-10-28 (future-dated) | Versioning/provenance anomaly; verify tags and Composer metadata before pinning |
| Licensing | LGPL-3.0 | Uncommon in PHP; expect a legal review cycle, especially for distributed/on-prem products |
| Test surface | README + releases only, no CI/test evidence in excerpt | Cast edge cases (DST, tz rename, nulls) are exactly where bugs hide |
| Coupling | Repositories + casts together | All-or-nothing temptation; adopt casts only if repositories don't fit |
The mitigating factor is that the highest-value piece (the datetime cast) is small, well-scoped, and replaceable. The failure mode of a bad dependency here is wrong timestamps and leaked private fields, not a broken application — serious but bounded and detectable with tests.
Key questions to resolve before approval.
illuminate/database, PHP, and Carbon version constraints, and which Laravel majors are actually exercised?AsPrivate do — encryption at rest, redaction on serialization, or access control? This determines whether there is a key-management and data-recovery obligation.Position: conditionally viable for the cast layer in greenfield or low-blast-radius code; not viable as a platform-wide dependency at 0 stars and 0 dependents without an internal fork.
Stack fit. Target Laravel 10/11+ on PHP 8.1+, where casts() and Attribute are first-class. Below Laravel 10 the composite accessor pattern in the README does not apply and the package's value drops sharply. Database-agnostic in principle, but datetime round-tripping must be verified per driver — MySQL DATETIME vs. Postgres timestamptz behave differently with respect to implicit timezone conversion, which is precisely the class of bug the cast is meant to eliminate. Audit for conflicts with any existing date handling ($dateFormat, custom Carbon macros, AsDateTime overrides) before layering this on.
Migration path. Do not do a big-bang adoption:
composer.lock; do not rely on ^ ranges from a 0-star package.AsUtc on one model with genuinely timezone-sensitive data. Keep repositories out of scope entirely for the first iteration.local_datetime_utc + local_datetime_timezone pattern requires new columns and a backfill: parse existing local timestamps with their assumed zone, write UTC plus zone, then make the derived accessor authoritative. This is a data migration with a verification step, not a code change — budget for it accordingly.AsPrivate requires a forward-only plan. If it means encryption, existing plaintext rows need a backfill and a rollback strategy; if it means serialization redaction, verify every API resource and toArray() consumer.Compatibility. Two contract surfaces to test: storage (what lands in the column) and serialization (what toArray()/toJson() emits). Casts change both. Any mobile client, export job, or webhook consuming model JSON is downstream of this change — treat the cast rollout as an API contract change, not an internal refactor.
Sequencing.
Maintenance. Single maintainer with no visible downstream consumers means the team effectively owns this code whether or not it forks it. The pragmatic operating posture is: assume no upstream support, pin hard, and treat upgrades as internal work. LGPL adds a standing compliance obligation — a one-time legal sign-off plus a note in the dependency register that this is copyleft-adjacent, so nobody is surprised during an acquisition or on-prem distribution.
Support. No vendor, no community, no dependents to consult. Internal support load is small but real: onboarding questions will route to whoever did the source read-through, so that person should write a short internal ADR plus a "how we handle timezones" note. That documentation is worth more than the dependency.
Scaling. The cast layer is CPU-negligible per attribute and does not affect query plans. Two scaling caveats: (a) if AsPrivate encrypts a column, that column becomes unindexable and unqueryable — any search/filter/report requirement on it must be designed around before adoption; (b) if the repository layer is used, be alert to N+1 regressions where eager-loading intent is lost behind an abstraction. Neither is a throughput problem at typical SaaS volumes; both are architectural constraints that surface late.
Failure modes to instrument.
AsPrivate gates field visibility, a config or version change can expose or blank fields. Mitigation: contract tests asserting the exact JSON shape of affected resources.Ramp-up. The API surface is small enough that a mid-level Laravel developer can be productive in half a day for the casts, provided they already understand Eloquent accessors and Carbon timezones. The real ramp-up cost is conceptual, not syntactic: the team must internalise the UTC-plus-zone storage convention and stop hand-rolling Carbonize logic in controllers. For the repositories layer, expect a genuine architectural debate and a multi-day design conversation rather than a training session. Budget one senior engineer for a week for the spike, the ADR, and the test scaffolding — and expect the honest recommendation to be "adopt the timezone pattern, fork or reimplement the cast, decline the repository layer."
How can I help you explore Laravel packages today?