Fork me on GitHub

Entity Single Table Inheritance

Orm supports single-table-inheritance to provide more advanced architecture. Although Orm supports this feature, we still see this design as quite rare and should not be overused or misused.

Definition#

  1. Create an abstract general predecessor of your entities that will define all shared properties. The child entities has to inherit from it.
  2. Register your abstract entity and all other entites in repository's getEntityClassNames(). The common abstract entity has to be registered as the first one.
  3. Overrie Repository::getEntityClassName(array $data) to detect the entity class name for the specific row.

Let's take a look at example:

/**
 * @property int $id {primary}
 * @property string $type {enum self::TYPE_*}
 */
abstract class Address extends Nextras\Orm\Entity\Entity
{
    const TYPE_PUBLIC = 'public';
    const TYPE_PRIVATE = 'private';
}

class PrivateAddress extends Address
{
}

/**
 * @property Maintainer $maintainer {m:1 Maintainer::$addressees}
 */
class PublicAddress extends Address
{
}

class AddressesRepository extends Nextras\Orm\Repository\Repository
{
    public static function getEntityClassNames(): array
    {
        return [Address::class, PrivateAddress::class, PublicAddress::class];
    }

    public function getEntityClassName(array $data): string
    {
        return $data['type'] === Address::TYPE_PUBLIC ? PublicAddress::class : PrivateAddress::class;
    }
}

Usage#

Collection calls will by default return a mixed result – with both types. You may filter these collection by the common properties defined on the abstract class. If you want to filter by property that is not shared between all entities, it's your responsibility to filter the proper entities fist. To access not shared properties, prepend a class name into the expression path.

$orm->addresses->findBy([
    'type' => Address::TYPE_PUBLIC,
    'Address->maintainer->id' => $maintainerId,
]);

The relationships itself point to specific entity name, so the filtering expression will be evaluated deterministically. Only the starting class name has to be defined, if the property is not shared.