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 Custom Fields Laravel Package

plin-code/laravel-custom-fields

Laravel Custom Fields is a package for adding configurable, model-specific custom fields to Laravel applications. Define field types and validation, assign fields to models, and manage extra metadata or form inputs without schema changes—useful for CMS, CRM, and admin interfaces.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. The package occupies the Eloquent persistence layer: typed custom-field definitions attached to models, plus a filter layer that translates query input into SQL predicates against the stored values. That is a sound fit for Laravel monoliths and modular monoliths where field shape is operator-controlled rather than developer-controlled — CMS content types, per-tenant CRM records, configurable intake forms, admin-built schemas. It is a poor fit for services that need schema-migrated, statically-typed columns, or for anything where another service must query the same data: the filter vocabulary becomes a de facto API contract that only this package understands.

Both v1.0.1 fixes land in the two highest-risk areas of any dynamic-schema package — input coercion on the read path and concurrent writes on the persistence path. That v1.0.0 shipped with a coercion bug that silently returned wrong rows tells us the package is still early on its hardening curve; treat 1.0.x as "usable with tests" rather than "boring."

Integration feasibility. High. composer update, no migration, no config change, patch-level version. The work is not in the install, it is in the behavioral delta for existing clients.

Technical risk.

  • Read path (behavioral break for consumers). A number filter that previously accepted true/false and matched value 1, and a contains that searched for "1", now throw InvalidFilterValueException (400). Previously-silent, previously-wrong results become hard client-visible failures. Clients that were sending boolean-ish strings to numeric filters will start erroring on deploy — this is a correctness win but an availability regression for those callers, and it should be treated as a contract change, not a bugfix, in the changelog we surface.
  • Write path (new concurrency semantics). The insert now runs in a savepoint and, on losing the unique-index race, converts to an update of the row the winner stored — explicitly last-write-wins. That removes a UniqueConstraintViolationException leak, which is good, but it also means concurrent edits silently clobber with no version guard, no 409, no lost-update signal. If any caller treats a write as "create only," that assumption is now false.
  • Exception contract unknown. Whether InvalidFilterValueException renders as a 400 out of the box (e.g. extends ValidationException) or needs an explicit handler registration determines whether consumers see 400 or 500. This is the single highest-impact unknown.
  • Other filters may share the coercion flaw. The notes describe two filters specifically; the underlying cause (Spatie's boolean casting meeting Laravel's integer rule, which accepts booleans) is a pattern, not an incident.

Key questions.

  1. What does InvalidFilterValueException extend, and what status/body shape do API consumers actually receive without a custom handler?
  2. Does the number filter still accept "1", "1.0", 1.5, and locale-formatted decimals? What is the exact accepted-value grammar post-fix?
  3. Which filters other than number and contains consume coerced values, and were they audited in #28/#29/#30?
  4. Is the savepoint-conflict path correct on PostgreSQL specifically — i.e. does it ROLLBACK TO SAVEPOINT before continuing, given that a failed statement aborts the subtransaction on PG?
  5. Does the update path fire model events/observers, bump updated_at, and trigger any queued or broadcast side effects that the insert path did not?
  6. What is the conflict target — host + field only, or the full unique index? What happens with three or more simultaneous writers?
  7. What is the upstream relationship (this appears to be a maintained fork of a Spatie package)? Is upstream active, and does this fork track it?

Integration Approach

Stack fit. Assumes Laravel 10/11, PHP 8.1+, and MySQL/PostgreSQL/SQLite. The savepoint-plus-upsert pattern is the one place where DB engine differences matter; validate on the engine we actually run, not just the one in CI.

Migration path. No database or config migration. The only migration is at the client boundary: audit request logs for boolean-ish values (true, false, possibly "TRUE", "on", "yes") sent to number filters. Those callers need to send numbers or nothing. If we cannot enumerate them, ship the version and watch the 4xx rate by route and filter name — the errors are now explicit, which makes this cheap.

Compatibility. Pin the version and verify exception rendering in a staging environment against a real HTTP request before rollout. If the exception does not render as 400 by default, register a handler in the same deploy — otherwise a correctness fix presents as 500s.

Sequencing.

  1. Read #28, #29, #30 diffs; confirm the savepoint handling and exception base class.
  2. Add regression tests: boolean input to number/contains → 400; numeric strings still accepted; concurrent write of the same field on the same host → single row, later value, no exception surfaced.
  3. Register/verify exception → 400 mapping.
  4. Deploy to staging with production-shaped data; run a short concurrency soak against the write path.
  5. Update API docs and changelog: filter type table plus an explicit "last write wins" statement.
  6. Roll out to production; monitor 4xx by filter and DB error rates for one release cycle.

Rollback is trivial at the code level (composer require the prior patch), but only if we catch the issue before clients adapt to the stricter filters — after that, reverting reintroduces silent wrong-row reads, which is worse.

Operational Impact

Maintenance. Small surface, patch-sized diffs, good release hygiene (linked PRs, explicit "no migration, no config change"). The governance question is the fork: a single-maintainer package in the query layer of a core record type is a bus-factor risk. Budget for the possibility of pinning at a known-good patch and carrying our own backports.

Support. Expect a short, sharp support bump at deploy: previously working (if incorrect) requests now return 400. Pre-brief support with the error message, the accepted value grammar, and the list of affected filters. The upside is that future field-data bugs surface as errors instead of as quietly wrong reports — which is the failure mode that actually costs money.

Scaling. The unique index constrains writes per (host, field) key, so a hot field on a hot host serializes on that row; the savepoint-and-update conversion trades an exception for lock contention rather than removing contention. Under sustained concurrent writes to the same key, watch for lock waits and deadlocks. Last-write-wins means no lost-update detection — acceptable for "first value wins on create," risky if any consumer treats these records as collaboratively edited.

Failure modes.

  • Exception not mapped → 500s instead of 400s on malformed filter input.
  • PostgreSQL: failed statement inside a savepoint aborts the subtransaction; if rollback-to-savepoint is not handled, subsequent statements in the same transaction fail.
  • Three-way races: "later write wins" is well-defined for two writers, less so beyond that; the outcome is whatever commits last, with no ordering guarantee from the caller's perspective.
  • Callers relying on the old coercion get a hard failure rather than a soft one — by design, but it is a behavior change to communicate.

Ramp-up. Low for developers — the API is idiomatic Laravel and the diff is small. The real onboarding cost is documenting two things that are invisible in the code: the filter type matrix (what each filter accepts and rejects) and the write-conflict semantics. Put both in the internal package notes; neither is discoverable from reading a controller.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin