joschi127/doctrine-entity-override-bundle
A Symfony bundle that lets you override Doctrine entities and their mappings from third-party bundles without touching vendor code. Extend or replace entity classes, customize mappings, and keep your changes clean and upgrade-safe—no more forking vendor packages.
Architecture fit. The first architectural finding is a stack mismatch that should be resolved before any deeper analysis: this is a Symfony bundle, not a Laravel package. It integrates via Symfony's DI container (compiler passes, services.yaml, bundle registration in Kernel/bundles.php) and hooks Doctrine ORM metadata loading to substitute one entity class for another at runtime. In a Laravel context it is only relevant if the project runs Doctrine ORM through laravel-doctrine/orm; on a pure Eloquent stack the package has effectively zero applicability. The underlying capability — overriding a vendor-owned entity with an application-owned subclass — is a genuine and recurring problem in monoliths and package-heavy codebases, so the problem is worth solving even if this specific artifact is not.
Integration feasibility. Low to moderate. The bundle is roughly a thin orchestration layer over three things we can reproduce ourselves:
original => override class pairs,loadClassMetadata or target-entity resolution,Points two and three are the portability boundary. The subscriber logic is portable PHP; the container wiring is not and would need to be rebuilt as a Laravel service provider (or resolved manually in a Doctrine bootstrap). The closest supported Doctrine primitive is ResolveTargetEntityListener, which achieves similar substitution against interfaces rather than concrete parents — a safer contract, and one we could adopt without inheriting this dependency.
Technical risk. High, concentrated in three areas:
targetEntity is hardcoded to the original FQCN, hardcoded class names in DQL / query builders / repositories, schema-tool and migration diffs when the mapping strategy (single-table vs. joined vs. class-table) changes, and identity/instanceof assumptions in vendor code that type-hints the original class. These are correctness risks that surface at runtime and in production data, not at install time.Key questions.
Stack fit. If the target is Laravel + Eloquent: do not integrate. There is no Doctrine metadata pipeline to hook, and the Symfony container assumptions are inert. If the target is Laravel + laravel-doctrine/orm: the bundle itself will not install or boot, but its substitution concept is reproducible in a service provider. Recommend treating the repository as reference material, not a dependency.
Migration path. Three viable routes, in order of preference:
ResolveTargetEntityListener, or a mapped superclass). Cleanest, upgrade-safe, no metadata surgery.Compatibility. Assume incompatibility until proven otherwise. Verification must cover: PHP version and attribute-based mapping, Doctrine ORM major version, Symfony major (if applicable), and schema-tool output equivalence (the migration diff must be empty after the override is applied — any diff means the mapping strategy changed and production tables are at risk).
Sequencing.
doctrine:schema:validate / migration diff clean) and association integrity (load entity through a relation, through a repository, and through a DQL query hardcoding the original class).Maintenance. This is the dominant cost. An abandoned 2019 bundle on a live persistence path means we own every future breakage, with no upstream to escalate to and no community patch velocity to lean on. Budget for a fork-and-vendor arrangement or an in-house reimplementation; do not budget for "install and forget." The in-house route (route 2 above) is more code but strictly less operational risk, because the code lives inside our upgrade cycle.
Support. No vendor SLA, no security response process, single maintainer, zero dependents. Security exposure is low in absolute terms (no network surface, no deserialization of untrusted input), but the dependency-graph exposure is real: a stale package on the ORM path can block PHP or Doctrine security upgrades, which is a worse outcome than a single CVE.
Scaling. Neutral on throughput — metadata substitution happens at bootstrap and is cached alongside Doctrine's metadata cache. The scaling concern is organizational, not runtime: every additional overridden entity increases the surface where vendor and application assumptions can diverge, and that surface grows with the number of vendor packages we consume. Set an explicit ceiling (e.g., override only for entities with no associations and no vendor repositories) and enforce it in review.
Failure modes. Ranked by likelihood and blast radius:
ClassNotFound. High likelihood, caught in integration tests only.instanceof checks reference the original class and bypass the override. Moderate likelihood, surfaces as subtle behavioral bugs.composer update breaks the bundle; if this lands in CI it's cheap, if it lands in a hotfix it is not.Ramp-up. Requires an engineer comfortable with Doctrine's metadata pipeline (loadClassMetadata, target-entity resolution, inheritance strategies) and, for the Symfony wiring, compiler passes. That is a narrower skill set than typical Laravel/Eloquent work — plan for one experienced owner rather than distributing the work, and document the override map and its invariants so the knowledge does not sit with a single person.
Recommendation. Do not adopt the bundle. Resolve the stack question first; if Doctrine is in play, either negotiate a vendor extension point (preferred) or port the substitution logic in-house under our own tests and version constraints. If Eloquent is the stack, close the evaluation.
How can I help you explore Laravel packages today?