Skip to content

Add inheritance mapping analyzers #70

Description

@ahmed-bhs

Summary

Doctrine's inheritance mapping (Single Table Inheritance, Class Table Inheritance, Mapped Superclasses) introduces subtle configuration pitfalls and silent performance traps that are hard to catch during development. Doctrine Doctor currently has very limited coverage in this area (only DuplicatePrivateFieldInHierarchyAnalyzer and FinalEntityAnalyzer).

This issue proposes 10 new analyzers dedicated to inheritance mapping problems, organized by category.


Integrity Analyzers

1. MissingDiscriminatorMapEntryAnalyzer

Detect concrete subclasses that are not listed in the #[DiscriminatorMap]. When omitted, Doctrine auto-generates the map at runtime, which is explicitly documented as "very expensive computation-wise" since the mapping driver has to scan and load all known classes. Beyond performance, a missing entry can cause silent bugs where persisted entities cannot be hydrated back to the correct type.

2. SingleTableInheritanceNullableColumnAnalyzer

In Single Table Inheritance, columns belonging to subclasses must be nullable because rows of sibling types will not populate them. A NOT NULL constraint on a subclass-specific column will cause insert failures for other subtypes. This is a common schema mistake that only surfaces at runtime with the right data.

3. InheritanceTypeOnNonRootEntityAnalyzer

#[InheritanceType], #[DiscriminatorColumn], and #[DiscriminatorMap] must be declared on the root entity of the hierarchy. Placing them on a subclass is a misconfiguration that Doctrine may silently ignore or that causes undefined behavior. This analyzer would flag the mistake early.

4. MappedSuperclassAsTargetEntityAnalyzer

A Mapped Superclass cannot be used as targetEntity in associations because it has no table of its own and is not queryable. This is a hard constraint in Doctrine that produces a runtime MappingException. Catching it statically saves debugging time.

5. MappedSuperclassOneToManyAnalyzer

OneToMany associations on a Mapped Superclass are not supported because the "many" side needs to hold a foreign key pointing to a concrete table. This only works with ResolveTargetEntityListener and a single subclass. The analyzer would flag OneToMany on mapped superclasses and suggest using entity inheritance (STI/CTI) or restructuring the relationship.

6. MissingDiscriminatorColumnAnalyzer

Detect an entity with #[InheritanceType] but no #[DiscriminatorColumn]. While Doctrine falls back to a default column name, relying on implicit defaults makes the schema harder to understand and can conflict with existing columns.

7. OverrideOnInheritanceEntityAnalyzer

#[AttributeOverride] and #[AssociationOverride] are only supported on entities extending a Mapped Superclass. Using them within an entity inheritance hierarchy (STI/CTI) is explicitly not supported and can produce incorrect mappings silently.


Performance Analyzers

8. ClassTableInheritanceDepthAnalyzer

Class Table Inheritance requires a JOIN per level in the hierarchy for every polymorphic query. A hierarchy deeper than 2-3 levels can cause significant query performance degradation. This analyzer would warn when the hierarchy depth exceeds a configurable threshold.

9. PolymorphicAssociationEagerLoadAnalyzer

When a ManyToOne or OneToOne association targets an entity that is the root of an inheritance hierarchy (not a leaf), Doctrine cannot create a proxy because the concrete type is unknown until query time. This forces eager loading of the associated entity, which can propagate transitively to its own relationships. This is a common and silent performance trap that should be flagged with a recommendation to target leaf entities when possible.

10. SingleTableInheritanceTooManySubclassesAnalyzer

When a STI hierarchy grows to many subclasses with distinct columns, the single table becomes wide and sparse (most columns NULL for most rows). This degrades storage efficiency, index effectiveness, and query readability. The analyzer would warn above a configurable threshold of subclasses or nullable columns.


Priority

Ordered by impact and frequency of the underlying problem:

Priority Analyzer Why
1 PolymorphicAssociationEagerLoadAnalyzer Silent perf trap, very common, hard to diagnose
2 MappedSuperclassAsTargetEntityAnalyzer Guaranteed runtime error, easy to catch statically
3 SingleTableInheritanceNullableColumnAnalyzer Frequent schema bug, only surfaces with specific data
4 MissingDiscriminatorMapEntryAnalyzer Hidden perf cost + potential hydration bugs
5 MappedSuperclassOneToManyAnalyzer Hard Doctrine limitation, confusing error message
6 InheritanceTypeOnNonRootEntityAnalyzer Silent misconfiguration
7 OverrideOnInheritanceEntityAnalyzer Explicitly unsupported, silently broken
8 MissingDiscriminatorColumnAnalyzer Implicit default, low severity
9 ClassTableInheritanceDepthAnalyzer Perf degrades gradually, threshold-based
10 SingleTableInheritanceTooManySubclassesAnalyzer Perf degrades gradually, threshold-based

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions