Inheritance Mapping

Doctrine currently offers two supported methods of inheritance: single collection and collection per class inheritance.

Mapped Superclasses

A mapped superclass is an abstract or concrete class that provides mapping information for its subclasses, but is not itself a document. Typically, the purpose of such a mapped superclass is to define state and mapping information that is common to multiple document classes.

Just like non-mapped classes, mapped superclasses may appear in the middle of an otherwise mapped inheritance hierarchy (through single collection or collection per class) inheritance.

Note

A mapped superclass cannot be a document and is not queryable.

Example:

  • PHP
    <?php
    
    namespace Documents;
    
    /** @MappedSuperclass */
    abstract class BaseDocument
    {
    }
    
  • XML
    <?xml version="1.0" encoding="UTF-8"?>
    <doctrine-mongo-mapping xmlns="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping
                    http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping.xsd">
      <mapped-superclass name="Documents\BaseDocument">
      </mapped-superclass>
    </doctrine-mongo-mapping>
    
  • YAML
    Documents\BaseDocument:
        type: mappedSuperclass
    

Single Collection Inheritance

In single collection inheritance, each document is stored in a single collection and a discriminator field is used to distinguish one document type from another.

Simple example:

  • PHP
    <?php
    
    namespace Documents;
    
    /**
     * @Document
     * @InheritanceType("SINGLE_COLLECTION")
     * @DiscriminatorField("type")
     * @DiscriminatorMap({"person"="Person", "employee"="Employee"})
     */
    class Person
    {
        // ...
    }
    
    /**
     * @Document
     */
    class Employee extends Person
    {
        // ...
    }
    
  • XML
    <?xml version="1.0" encoding="UTF-8"?>
    <doctrine-mongo-mapping xmlns="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping
                    http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping.xsd">
      <document name="Documents\Person" inheritance-type="SINGLE_COLLECTION">
        <discriminator-field name="type" />
        <discriminator-map>
            <discriminator-mapping value="person" class="Person" />
            <discriminator-mapping value="employee" class="Employee" />
        </discriminator-map>
      </document>
    </doctrine-mongo-mapping>
    
    <?xml version="1.0" encoding="UTF-8"?>
    <doctrine-mongo-mapping xmlns="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping
                    http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping.xsd">
      <document name="Documents\Employee">
      </document>
    </doctrine-mongo-mapping>
    
  • YAML
    Documents\Person:
      type: document
      inheritanceType: SINGLE_COLLECTION
      discriminatorField: type
      discriminatorMap:
        person: Person
        employee: Employee
    

The discriminator value allows Doctrine to infer the class name to instantiate when hydrating a document. If a discriminator map is used, the discriminator value will be used to look up the class name in the map.

Now, if we query for a Person and its discriminator value is employee, we would get an Employee instance back:

<?php

$employee = new Employee();
// ...
$dm->persist($employee);
$dm->flush();

$employee = $dm->find('Person', $employee->getId()); // instanceof Employee

Even though we queried for a Person, Doctrine will know to return an Employee instance because of the discriminator map!

If your document structure has changed and you've added discriminators after already having a bunch of documents, you can specify a default value for the discriminator field:

  • PHP
    <?php
    
    namespace Documents;
    
    /**
     * @Document
     * @InheritanceType("SINGLE_COLLECTION")
     * @DiscriminatorField("type")
     * @DiscriminatorMap({"person"="Person", "employee"="Employee"})
     * @DefaultDiscriminatorValue("person")
     */
    class Person
    {
        // ...
    }
    
    /**
     * @Document
     */
    class Employee extends Person
    {
        // ...
    }
    
  • XML
    <?xml version="1.0" encoding="UTF-8"?>
    <doctrine-mongo-mapping xmlns="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping
                    http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping.xsd">
      <document name="Documents\Person" inheritance-type="SINGLE_COLLECTION">
        <discriminator-field name="type" />
        <discriminator-map>
            <discriminator-mapping value="person" class="Person" />
            <discriminator-mapping value="employee" class="Employee" />
        </discriminator-map>
        <default-discriminator-value value="person" />
      </document>
    </doctrine-mongo-mapping>
    
    <?xml version="1.0" encoding="UTF-8"?>
    <doctrine-mongo-mapping xmlns="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping
                    http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping.xsd">
      <document name="Documents\Employee">
      </document>
    </doctrine-mongo-mapping>
    
  • YAML
    Documents\Person:
      type: document
      inheritanceType: SINGLE_COLLECTION
      discriminatorField: type
      defaultDiscriminatorValue: person
      discriminatorMap:
        person: Person
        employee: Employee
    

Collection Per Class Inheritance

With collection per class inheritance, each document is stored in its own collection and contains all inherited fields:

  • PHP
    <?php
    
    namespace Documents;
    
    /**
     * @Document
     * @InheritanceType("COLLECTION_PER_CLASS")
     */
    class Person
    {
        // ...
    }
    
    /**
     * @Document
     */
    class Employee extends Person
    {
        // ...
    }
    
  • XML
    <?xml version="1.0" encoding="UTF-8"?>
    <doctrine-mongo-mapping xmlns="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping
                    http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping.xsd">
      <document name="Documents\Person" inheritance-type="COLLECTION_PER_CLASS">
      </document>
    </doctrine-mongo-mapping>
    
    <?xml version="1.0" encoding="UTF-8"?>
    <doctrine-mongo-mapping xmlns="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping
                    http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping.xsd">
      <document name="Documents\Employee">
      </document>
    </doctrine-mongo-mapping>
    
  • YAML
    Documents\Person:
      type: document
      inheritanceType: COLLECTION_PER_CLASS
    

A discriminator is not needed with this type of inheritance since the data is separated in different collections.

Fork me on GitHub