rapidwebltd/improved-polymorphic-eloquent-builder
Improve polymorphic relationship querying in Laravel with a cleaner Eloquent builder API. This package helps you filter and chain queries across morph types more expressively, reducing boilerplate when working with polymorphic models.
What the package is. rapidwebltd/improved-polymorphic-eloquent-builder is a thin extension of Laravel's Eloquent query builder targeting polymorphic relations. It swaps a model's builder (via a trait / newEloquentBuilder override) so that queries against the child side of a morphTo/morphMany relationship are automatically constrained by the parent's morph type, and adds helper methods for scoping by morphable class. In practice it is a few hundred lines of where-clause composition around morph_type / morph_id columns.
Architecture fit. Structurally it is a low-coupling, model-layer concern. It does not touch the container, routing, or service providers, and it does not impose a persistence model. That is architecturally clean — the concern is real (polymorphic queries silently returning cross-type rows is a genuine correctness and data-isolation hazard), and the placement is correct.
Integration feasibility. Weak on any current stack. The last release is 2018-01-30, which places it in the Laravel 5.5/5.6 and PHP 7.0/7.1 era. On Laravel 9/10/11/12 and PHP 8.1+, composer require will fail on platform constraints; forcing it with --ignore-platform-reqs will likely produce runtime breakage, because the Eloquent Builder has accumulated new methods, changed return types, and internal call paths. Adoption is also negligible (11 stars), so there is no implicit field-testing from a user base.
Technical risk.
| Risk | Severity | Notes |
|---|---|---|
| Abandonment | High | No release in 7+ years; assume unmaintained from day one. |
| Framework drift | High | Builder subclassing is one of the more brittle extension points across Laravel majors. |
| Builder collision | Medium | Only one custom builder per model. Conflicts with Scout, search packages, soft-delete extensions, or our own base builder. |
| Licensing | Medium | LGPL-3.0 is unusual for PHP libraries; the relinking analysis is murky for interpreted, source-distributed code. Requires legal sign-off, not an engineering call. |
| Morph map mismatch | Medium | If we use Relation::morphMap(), stored types are aliases, not FQCNs. Code written pre-morph-map may compare against class names and silently match nothing — a null-result bug, not an error. |
| Feature erosion | High | Laravel 5.8+ shipped whereHasMorph, whereMorphedTo, and morphWith, which cover much of the original gap. |
Key questions to resolve before any spend.
whereHasMorph / whereMorphedTo / morphWith already solve it? If yes, the package is unnecessary.morphMap, and if so does the package's type comparison survive it?Stack fit. Viable only on a legacy Laravel 5.x application, and even there only if we accept that we are forking. On Laravel 8+ the honest answer is: not compatible, do not attempt.
Recommended path (in order of preference).
README explaining why the fork exists and who owns it, and add a regression suite covering the exact polymorphic queries we rely on.Migration and compatibility notes.
newEloquentBuilder overrides across the codebase first.(morph_type_column, morph_id_column) exists on every polymorphic table before turning on type-scoped queries; the package changes query shape and will expose missing indexes.Relation::morphMap() at the same time, otherwise the type comparison it performs may be based on the wrong value.Sequencing.
Total realistic cost: under two weeks. That is the number to put against the "just composer require it" instinct.
Maintenance. The real cost is not integration, it is ownership. There is no upstream to patch a PHP 9 or Laravel 13 incompatibility, no security response, and issue triage is effectively dead. If we adopt it, we are the maintainers from day one — budget accordingly, or avoid the transfer of liability entirely by reimplementing.
Supportability. No vendor support, negligible community. A new engineer encountering a custom builder subclass will lose time determining whether a query's constraints come from the framework or from a 2018 package. That is a recurring, low-grade tax on every polymorphic query debug session — worse than a one-time integration cost.
Scaling. The package itself has no meaningful scaling profile: it emits WHERE predicates. Two things matter instead. First, index coverage — a missing (type, id) composite index turns type-scoped polymorphic lookups into table scans, and polymorphic tables are exactly the ones that grow fastest (comments, activities, attachments, audit rows). Second, query shape stability: if the constraint is ever not applied, the query silently widens. That is a correctness and performance cliff at once.
Failure modes to monitor.
newEloquentBuilder race; behaviour becomes load-order dependent.Mitigation: assert the constraint in tests at the query level (assert generated SQL / toSql()), not just at the result level; alert on unexpected row volumes from polymorphic endpoints.
Ramp-up. The concept is learnable in an hour for anyone who knows Eloquent; the institutional ramp-up is the problem. Engineers must learn (a) that the package exists, (b) why it was added, and (c) that it is unforked and unpatched. That knowledge lives in one or two heads unless it is written down. If we proceed, an ADR plus a short inline comment at the trait's usage sites is mandatory, not optional.
Recommendation. Treat this as a reference implementation, not a dependency. The problem it addresses is legitimate; the artifact is seven years stale, low-adoption, copyleft-licensed, and largely superseded by first-party Laravel APIs. Reimplement the constraint logic in-house in under a sprint, and close the ticket with an ADR recording why we did not adopt it.
How can I help you explore Laravel packages today?