levelab/doctrine-discriminator-bundle
A Symfony bundle that simplifies Doctrine discriminator mapping for entity inheritance, reducing boilerplate when configuring discriminator maps. Useful for single-table inheritance setups and cleaner Doctrine configuration in Symfony applications.
Verdict: reject as a dependency, adopt as a design reference. The artifact is a Symfony bundle, not a Laravel package; it intercepts Doctrine ORM metadata to decentralize the discriminatorMap, and it has had no release since 2016-03-03 with 12 stars and zero dependents. The valuable asset is the ~200-line mechanism, not the distributable.
Architecture fit. The problem it solves is real and Doctrine-idiomatic. Single-table inheritance requires the root entity to declare one discriminatorMap array naming every subclass, which makes the base class a shared registry that every team adding a subtype must edit. The bundle inverts that: each subclass declares its own discriminator value, and a loadClassMetadata subscriber plus a Symfony compiler pass synthesizes the map at metadata-build time. Decoupling the root entity from its descendants is still the correct architectural intent in modern Doctrine. The packaging is the problem — the reusable core is a small fraction of the repo, welded to Symfony\Component\HttpKernel\Bundle\Bundle and a DI extension that has no meaning outside a Symfony kernel.
Integration feasibility — low, and not on the named stack. Three blockers stack:
| Dimension | State | Consequence |
|---|---|---|
| Framework | Symfony 2.x/3.x bundle registration | No service provider, no package discovery; does not boot in Laravel |
| PHP | ~5.5–7.0 era signatures | PHP 8.x deprecations and probable fatals in subscriber/compiler pass |
| Doctrine | ORM 2.5-era APIs, doctrine/annotations reader |
ClassMetadata listener surface has shifted; PHP 8 attributes unsupported |
| Ecosystem | 1 release, 0 dependents, no tests, no CI | No compatibility matrix, no triage, no security process |
Additionally, Laravel applications typically use doctrine/dbal for the query builder, not Doctrine ORM; where ORM is present it arrives via laravel-doctrine/orm, which consumes neither Symfony bundles nor this listener shape. This is not a configuration problem — it is a dependency-resolution dead end before any code executes.
Technical risk. High and asymmetric, concentrated in the worst possible layer. The cheap failure mode is a clean install error. The expensive one is a partial install on a legacy PHP 7.4 service where the subscriber intermittently fails to populate the map, surfacing as ClassNotFound or hydration errors only for subclasses loaded after the metadata cache warms — i.e., data-integrity bugs, not stack traces. There are no tests to establish behavioral guarantees, and metadata interception is the highest-blast-radius place to inherit unmaintained code.
Key questions for the requesting engineer.
Stack fit — poor as shipped, viable as a pattern. There is no supported installation path into Laravel, and forcing it with --ignore-platform-reqs against a symfony/http-kernel shim drags a parallel framework kernel in for no benefit. The defensible move is a clean-room reimplementation of the mechanism, scoped at 150–250 lines and 4–7 engineering days.
Doctrine\Common\EventSubscriber on loadClassMetadata. On the root entity, discover subclasses carrying a #[DiscriminatorValue('foo')] attribute from a declared namespace or cached class list, then assign $metadata->discriminatorMap and $metadata->subClasses. No kernel coupling.doctrine/annotations entirely. This removes the deprecated reader and the Symfony DI dependency in one step, and is the single highest-value deviation from the original.Doctrine\ORM\Events::loadClassMetadata on the EntityManager event manager in a service provider, or via the laravel-doctrine subscribers config key.discriminatorMap on a fixture hierarchy, and a subclass added in a separate package is picked up without touching the root entity.Sequencing.
Do not vendor the repository as a path or VCS dependency. Modernizing a 2016 Symfony bundle costs more than the rewrite and leaves the kernel baggage behind.
Maintenance. As-shipped, maintenance cost is effectively total and unquantifiable: every PHP or Doctrine upgrade becomes an unscheduled fork-modernization task, and no upstream fix will ever arrive. As-reimplemented, it is 150–250 lines of first-party code with a parity test suite, owned by a named engineer and sized to be understood in an afternoon. This is the entire argument for the rewrite — it converts an unbounded external dependency into a bounded internal utility.
Support. With the package, there is no support channel: no issue triage, no CVE monitoring, no release cadence, a single small maintainer org last active in 2016. With the internal version, support routes through normal team on-call and code review. The cost is a small addition to the owning team's surface area; the benefit is that failures page someone who can actually fix them.
Scaling. The pattern scales along the axis that matters — team count. Adding an entity subtype becomes a one-new-file change with zero merge conflict on a shared base class, which is the coordination-tax reduction that justifies the work. Two caveats to plan for: discovery scanning cost grows with the number of entity classes (mitigated by cache warming at deploy, not request), and decentralized maps reduce discoverability, so ship a build-time assertion that enumerates every resolved subtype so there remains exactly one place to read the full map even though no one must edit it.
Failure modes. Ranked by expected pain: silent metadata divergence where a subclass is not discovered and hydration fails only on cold-cache paths (mitigated by the resolved-map parity test and a deploy-time map dump); discriminator column drift from a careless migration (mitigated by documenting the column as a schema invariant); and fork drift if the internal implementation later diverges from Doctrine's own metadata semantics across a major upgrade (mitigated by pinning Doctrine and re-running the parity fixture on upgrade). None of these are exotic; all are cheap to test for and expensive to discover in production.
Ramp-up. Low for anyone who already knows Doctrine inheritance — the concepts are standard STI plus a metadata subscriber, roughly a half-day to read and a day to extend. Non-trivial for a team that has never run Doctrine ORM at all, which is the common Laravel case; that team is better served by keeping the explicit discriminatorMap until subclass count and cross-team edit frequency make the conflict cost concrete and measurable. Budget the investment in engineering days under developer-experience tooling, not against a product roadmap line; the payoff is reduced coordination friction as the entity model grows, not any user-facing capability.
How can I help you explore Laravel packages today?