Inheritance
Inheritance is an object-oriented concept without a direct database equivalent, so an ORM has to emulate it. Propulsion supports two strategies:
- Single Table Inheritance — the cheapest at query time, but limited to a small, fixed set of inherited columns.
- Class Table Inheritance — one table per class, joined together. Propulsion doesn’t implement this directly; the
delegatebehavior gives you the same result.
Propulsion doesn’t have a dedicated Concrete Table Inheritance feature — a concrete_inheritance behavior did this in 2.x, but it’s gone in 3.0 (see Upgrading from 2.x to 3.0). Model the relationship with a foreign key to the parent table instead, or use single-table inheritance if the columns fit.
Single table inheritance
Section titled “Single table inheritance”One table backs every subclass, so the table needs every column any subclass might use. Propulsion generates a stub subclass for each.
Consider Book, with two subclasses, Essay and Comic. A discriminator column (conventionally class_key, but any name works) needs the inheritance="single" attribute and one <inheritance> child per subclass:
<table name="book"> <column name="id" type="integer" primaryKey="true" autoIncrement="true"/> <column name="title" type="varchar" size="100"/> <column name="class_key" type="integer" inheritance="single"> <inheritance key="1" class="Book"/> <inheritance key="2" class="Essay" extends="Book"/> <inheritance key="3" class="Comic" extends="Book"/> </column></table>Rebuilding generates Book, Essay, Comic (each extending Book), and matching BookQuery, EssayQuery, ComicQuery classes (each extending BookQuery). An <inheritance> element can itself extends another inherited class, so a Manga type could extend Comic instead of Book directly.
Use the generated classes exactly like any other model class — Propulsion sets the discriminator column for you:
<?php$book = new Book();$book->setTitle('War And Peace');$book->save();
$essay = new Essay();$essay->setTitle('On the Duty of Civil Disobedience');$essay->save();id | title | class_key---|-----------------------------------|----------1 | War And Peace | Book2 | On the Duty of Civil Disobedience | EssayQuerying the parent class hydrates the correct subclass automatically:
<?php$books = BookQuery::create()->find();foreach ($books as $book) { echo get_class($book) . ': ' . $book->getTitle() . "\n";}// Book: War And Peace// Essay: On the Duty of Civil DisobedienceQuery with a subclass’s own Query class (ComicQuery::create()->find()) to restrict results to just that subclass. Mark a table abstract="true" to prevent instantiating the base class directly, forcing callers through a subclass:
<table name="book" abstract="true"> <!-- ... --></table>Class table inheritance via delegate
Section titled “Class table inheritance via delegate”Propulsion has no dedicated class-table-inheritance feature; the delegate behavior gets you the same result by proxying method calls from a child table to a “parent” table it doesn’t actually inherit from in PHP terms.
<table name="player"> <column name="id" type="integer" primaryKey="true" autoIncrement="true"/> <column name="first_name" type="varchar" size="100"/> <column name="last_name" type="varchar" size="100"/></table><table name="basketballer"> <column name="id" required="true" primaryKey="true" autoIncrement="true" type="integer"/> <column name="points" type="integer"/> <column name="player_id" type="integer"/> <foreign-key foreignTable="player"> <reference local="player_id" foreign="id"/> </foreign-key> <behavior name="delegate"> <parameter name="to" value="player"/> </behavior></table>Basketballer delegates unknown method calls to its related Player — setting Player columns directly on a Basketballer instance:
<?php$basketballer = new Basketballer();$basketballer->setPoints(101);$basketballer->setFirstName('Michael'); // delegated to a Player instance$basketballer->setLastName('Giordano');$basketballer->save(); // saves both the basketballer and the player rowIf no Player is already attached, delegate creates one automatically. Delegation is single-level — a deeper hierarchy needs delegation to every ancestor (to="basketballer, player"), simulating multiple inheritance. The delegated methods are real, typed methods on the delegating class, so IDEs and PHPStan resolve them the same as any other generated method.