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

Constrained Morph To For Laravel Laravel Package

pindab0ter/constrained-morph-to-for-laravel

Type-constrained polymorphic relationships for Laravel Eloquent. Extend morphTo to accept only specified model types, ensuring type safety and data integrity while keeping the flexibility of a polymorphic database structure.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. The package is additive and shallow: a HasConstrainedMorphTo trait plus a MorphTo subclass, surfaced through constrainedMorphTo($types, $typeColumn, $idColumn). It reuses the existing *_type / *_id columns, so there is no schema change, no new table, and no change to how data is persisted. This matters because polymorphic columns are exactly where relational integrity cannot be enforced by the database — there is no foreign key to hang a rule on. A read-path allowlist is one of the few places a type contract can live without reshaping the schema. From an architecture standpoint, this is a narrowly scoped, well-bounded extension rather than a framework-level commitment.

Integration feasibility. High on the happy path. One composer require, one trait per model, one method swap per relation. No service provider to register, no configuration file, no global behavior change — meaning adoption is genuinely incremental and reversible, relation by relation. The real cost is not integration but verification: a TPM should budget for a spike that exercises the Eloquent surfaces the package has not visibly hardened.

Technical risk. Three items dominate the risk register.

  1. Null-on-mismatch is a silent failure contract. A disallowed type resolves to null, which is indistinguishable from "parent deleted," "record orphaned," or "query returned nothing." That is correct behavior for a filter and dangerous for anything where a missing relation must be loud — payments, entitlements, permissions, audit subjects, moderation state. Decide per-relation whether silence is acceptable, and record that decision.
  2. Vendor concentration. Three stars, zero dependents, single maintainer, MIT license, small code surface. The correct posture is "posable, forkable, ownable" — not "strategic dependency." Say so in the decision record so no one is surprised later.
  3. Query-builder integration is still being discovered in production. The v1.3.1 fix — whereMorphedTo() / orWhereMorphedTo() qualifying morph columns against the first constrained type instead of the parent table, producing an unknown-column error — is the tell. Young packages surface edge cases in patch releases. Any Eloquent surface not covered by the test suite should be assumed unverified, not assumed working.

Key questions to close before a go decision.

  • Does the constraint filter at the query level or post-hydration? This determines the cost profile of with(), load(), and whereHas() on mixed-type sets, and whether disallowed rows leak into eager-loaded collections.
  • What are the semantics of exists(), associate(), dissociate(), touch(), has(), and cascading deletes when the stored type is disallowed?
  • Does the write path reject commentable_type = Image::class? The README implies it does not — only the read path is constrained. If confirmed, plan a validation rule, observer, or check constraint to close the gap.
  • Is Laravel 13 support genuinely tested in CI, or listed aspirationally? Resolve before any framework upgrade plan depends on this package.
  • Does it compose cleanly with packages that intercept polymorphic relations — activity logging, auditing, media libraries, soft-delete wrappers, search indexers?
  • What is actual test coverage, and does it include morph maps plus mixed-type eager loading?

Integration Approach

Stack fit. Eloquent-only, PHP 8.2+, Laravel 10.48+/11/12/13. If the team is not already using morph maps, fix that first. Constraining to raw class strings without a morph map relocates the brittleness (namespace refactors still break stored data) rather than removing it; v1.3.0 added morph-map compatibility, which is the configuration worth standardizing on.

Migration path. No data migration, no schema change, no backfill. The unit of change is a single relation method, and rollback is reverting that method. Adopt first on low-blast-radius read paths — comment targets, attachments, reactions, taggables — and explicitly not on money, permissions, or anything carrying a compliance narrative. Where write-side integrity matters, ship the guard in the same release as the constraint so the claim becomes "this association is rejected on write and filtered on read," not "this association can never happen."

Compatibility. Inventory every package and internal abstraction that touches polymorphic behavior before rollout. Pay specific attention to existing whereMorphedTo() / orWhereMorphedTo() usage and to morph-map aliases, since those are the surfaces with the most recent defect history.

Sequencing.

  1. Pin an exact version (e.g. 1.3.1), not a caret range — the package is pre-2.0 and still correcting query-builder edge cases.
  2. Spike in staging with two relations: one single-type, one multi-type. Exercise eager loading, whereHas, exists(), soft deletes, and morph maps.
  3. Land the write-side guard alongside the first read-path constraint.
  4. Expand relation by relation, with each allowlist reviewed as code and owned by the domain team, not the platform team.
  5. Only then use the package for narrowing during deprecations: shrink the allowlist in one release, backfill/clean data next, delete last. That is a far cleaner rollout than a flag day — but only if the null contract is tolerable during the transition window.

Operational Impact

Maintenance. Low, with one caveat. The code is small, MIT, legible, and forkable; if the maintainer stops, a mid-level engineer can own it permanently. Budget for that contingency explicitly rather than assuming upstream support. Pin versions and treat upgrades as reviewed changes — the v1.3.1 column-qualification fix is precisely the kind of patch you want to take deliberately, not automatically.

Support. The package improves debuggability of type errors: instead of an unexpected model class appearing in a collection, an out-of-contract relation resolves to null, which narrows the search space. The counterweight is that "why is this relation null?" becomes a new question class — teams will need a quick way to distinguish "type disallowed" from "parent gone." Add a logging or assertion hook for relations where silence is unacceptable, and document the diagnostic path in the runbook.

Scaling. The performance story is entirely dependent on the unanswered query-level question. If the constraint is pushed into the SQL (where type in (...)), the cost is a marginally more selective index scan on the polymorphic type column and eager loading gets cheaper on mixed-type sets. If it is post-hydration filtering, eager loading mixed-type collections fetches rows it then discards, and the package becomes a small negative on hot read paths. Measure before scaling adoption to high-traffic endpoints.

Failure modes. Enumerate them in the runbook: (a) silent null on a relation the business treats as required; (b) allowlist drift — a new model type added to the schema but not to the constraint, producing invisible nulls; (c) query-builder regressions of the v1.3.1 class surfacing under new Eloquent usages; (d) whereHas returning incomplete result sets without error; (e) package abandonment leaving a pinned version blocking a future Laravel major.

Ramp-up. Roughly an afternoon for a senior Laravel engineer to read the source and internalize the semantics; the trait and subclass are small enough to audit in full. The larger ramp-up cost is organizational: getting teams to understand that this is a read-side filter with a write-side gap, and that the allowlist is now domain-owned code that must be updated whenever a model type joins or leaves the polymorphic family. That ownership discipline, not the package, is the thing most likely to fail.

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.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style