netpositive/discriminatormap-bundle
Symfony bundle that simplifies Doctrine discriminator maps, helping you configure and use single-table inheritance with less boilerplate. It provides integration for mapping inherited entities to concrete classes in Symfony applications.
This bundle solves one specific problem: Doctrine's @DiscriminatorMap annotation is static, but in plugin/modular Symfony apps you often need bundles to contribute their own subclasses to a parent entity's map without editing the parent. If you're not doing single-table (or joined) inheritance with a shared base entity, this package won't help you.
Minimal steps:
composer require netpositive/discriminatormap-bundle
app/AppKernel.php:
new Netpositive\DiscriminatorMapBundle\NetpositiveDiscriminatorMapBundle(),
DependencyInjection/ folder and the service definitions in Resources/config/ — that's where you'll see how it hooks into Doctrine's loadClassMetadata event and what configuration key (typically something like netpositive_discriminator_map) it exposes.AbstractContent entity with @InheritanceType("SINGLE_TABLE") and @DiscriminatorColumn. A separate BlogBundle wants to add BlogPost as a subclass. Instead of editing the base entity every time, you register BlogPost under a discriminator value via this bundle, and the bundle merges it into the map at metadata-load time.Start by reproducing the simplest case from the README: one base entity, one dynamically-registered subclass, dumped via doctrine:schema:update --dump-sql to confirm the discriminator value is present in the generated SQL.
Configuration-driven registration. The canonical pattern is to declare subclasses in config.yml under the bundle's config key, mapping discriminator values to entity FQCNs. This is the pattern you want for app-level entities.
Tagged-service / bundle-extension registration. For reusable bundles, the cleaner pattern is a compiler pass or a tagged service so each bundle contributes its own entries at build time rather than requiring the host app to know about them. Check whether the bundle exposes a tag; if not, wrap your registration in your bundle's Extension::load().
Merging with an existing map. The usual failure mode of dynamic maps is clobbering the annotated map. The safe pattern is:
array_merge) rather than replaces. Inspect the listener code before relying on it.Metadata listener ordering. The bundle will register a loadClassMetadata listener. When other bundles also listen to that event (e.g., DoctrineExtensions, StofDoctrineExtensionsBundle, or your own ResolveTargetEntity listeners), ordering matters. Set explicit priorities in your service definition if you see subclasses being dropped.
Day-to-day workflow.
cache:clear → doctrine:schema:update --dump-sql → check the column adds a valid discriminator value → run migrations.$em->getClassMetadata(BaseEntity::class)->discriminatorMap.Integration tip. Pair with a repository that switches on instanceof rather than the raw discriminator value — cheaper to refactor when a subclass is renamed or removed.
ClassMetadata internals and loadClassMetadata signatures changed), verify compatibility before committing. Expect to fork for anything modern.doctrine:cache:clear-metadata, or your configured cache pool). A stale discriminator map is the #1 source of "unknown discriminator" errors, and the error message often points at the wrong entity.VARCHAR(255) but your values aren't FQCNs, you're fine. If the bundle defaults to FQCN-as-discriminator, tighten the column or override the value explicitly.--dump-sql to prove anything about subclasses beyond the column value.var/cache/ can mask a wrong map. Delete both metadata cache and generated proxies when debugging.loadClassMetadata listener of your own that runs after it and fills in the rest. Don't patch vendor code directly — with 0 dependents, upstream fixes aren't coming.How can I help you explore Laravel packages today?