Explores four approaches to represent selectively loaded entity relations in PHP's type system using PHPStan. Starting from a simple ecommerce Product entity, the post evaluates: (1) separate classes per use-case, (2) interfaces with property hooks, (3) PHPStan generics with covariant template parameters, and (4) object shape intersections. Generics allow a single class to express which relations are loaded via PHPDoc annotations, but suffer from positional-only template parameters. Object shape intersections offer a structural typing alternative that narrows property types at the PHPStan level without requiring new classes.
Table of contents
The Problem: Selectively loading relationsApproach 1: Class per use-caseApproach 2: InterfacesApproach 3: Generics all the wayApproach 4: Object shapes to the rescueSummary and outlookSort: