| This project is no longer maintained and has been archived.

master

# Inheritance

Doctrine supports three types of inheritance strategies which can be mixed together. The three types are simple, concrete and column aggregation. You will learn about these three different types of inheritance and how to use them in this chapter.

For this chapter lets delete all our existing schemas and models from our test environment we created and have been using in the earlier chapters:

    $ rm schema.yml$ touch schema.yml$ rm -rf models/*

## Simple

Simple inheritance is the easiest and simplest inheritance to use. In simple inheritance all the child classes share the same columns as the parent and all information is stored in the parent table.

    // models/Entity.php
    class Entity extends Doctrine_Record
    {
        public function setTableDefinition()
        {
            $this->hasColumn('name', 'string', 30);
            $this->hasColumn('username', 'string', 20);
            $this->hasColumn('password', 'string', 16);
            $this->hasColumn('created_at', 'timestamp');
            $this->hasColumn('update_at', 'timestamp');
        }
    }

Now lets create a `User` model that extends `Entity`:

    // models/User.php
    class User extends Entity
    { }

Do the same thing for the `Group` model:

    // models/Group.php
    class Group extends Entity
    { }

Here is the same example in YAML format. You can read more about YAML in the YAML Schema Files chapter:

    ---# schema.yml# ...Entity:  columns:    name: string(30)    username: string(20)    password: string(16)    created_at: timestamp    updated_at: timestampUser:  inheritance:    extends: Entity    type: simpleGroup:  inheritance:    extends: Entity    type: simple

Lets check the SQL that is generated by the above models:

    // test.php

    // ...
    $sql = Doctrine_Core::generateSqlFromArray(array('Entity', 'User', 'Group'));
    echo $sql[0];

The above code would output the following SQL query:

    CREATE TABLE entity (id BIGINT AUTO_INCREMENT,
    username VARCHAR(20),
    password VARCHAR(16),
    created_at DATETIME,
    updated_at DATETIME,
    name VARCHAR(30),
    PRIMARY KEY(id)) ENGINE = INNODB

| When using YAML schema files you are able to define columns in the child classes but when the YAML is parsed the columns are moved to the parent for you automatically. This is only a convenience to you so that you can organize your columns easier.

## Concrete

Concrete inheritance creates separate tables for child classes. However in concrete inheritance each class generates a table which contains all columns (including inherited columns). In order to use concrete inheritance you'll need to add explicit `parent::setTableDefinition()` calls to child classes as shown below.

    // models/TextItem.php
    class TextItem extends Doctrine_Record
    {
        public function setTableDefinition()
        {
            $this->hasColumn('topic', 'string', 100);
        }
    }

Now lets create a model named `Comment` that extends `TextItem` and add an extra column named `content`:

    // models/Comment.php
    class Comment extends TextItem
    {
        public function setTableDefinition()
        {
            parent::setTableDefinition();

            $this->hasColumn('content', 'string', 300);
        }
    }

Here is the same example in YAML format. You can read more about YAML in the YAML Schema Files chapter:

    ---# schema.ymlTextItem:  columns:    topic: string(100)Comment:  inheritance:    extends: TextItem    type: concrete  columns:    content: string(300)

Lets check the SQL that is generated by the above models:

    // test.php

    // ...
    $sql = Doctrine_Core::generateSqlFromArray(array('TextItem', 'Comment'));
    echo $sql[0] . "";
    echo $sql[1];

The above code would output the following SQL query:

    CREATE TABLE text_item (id BIGINT AUTO_INCREMENT,
    topic VARCHAR(100),
    PRIMARY KEY(id)) ENGINE = INNODB
    CREATE TABLE comment (id BIGINT AUTO_INCREMENT,
    topic VARCHAR(100),
    content TEXT,
    PRIMARY KEY(id)) ENGINE = INNODB

In concrete inheritance you don't necessarily have to define additional columns, but in order to make Doctrine create separate tables for each class you'll have to make iterative `setTableDefinition()` calls.

In the following example we have three database tables called `entity`, `user` and `group`. `Users` and `groups` are both `entities`. The only thing we have to do is write 3 classes (`Entity`, `Group` and `User`) and make iterative `setTableDefinition()` method calls.

    // models/Entity.php
    class Entity extends Doctrine_Record
    {
        public function setTableDefinition()
        {
            $this->hasColumn('name', 'string', 30);
            $this->hasColumn('username', 'string', 20);
            $this->hasColumn('password', 'string', 16);
            $this->hasColumn('created', 'integer', 11);
        }
    }

    // models/User.php
    class User extends Entity
    {
        public function setTableDefinition()
        {
            // the following method call is needed in
            // concrete inheritance
            parent::setTableDefinition();
        }
    }

    // models/Group.php
    class Group extends Entity
    {
        public function setTableDefinition()
        {
            // the following method call is needed in
            // concrete inheritance
            parent::setTableDefinition();
        }
    }

Here is the same example in YAML format. You can read more about YAML in the YAML Schema Files chapter:

    ---Entity:  columns:    name: string(30)    username: string(20)    password: string(16)    created: integer(11)User:  inheritance:    extends: Entity    type: concreteGroup:  tableName: groups  inheritance:    extends: Entity    type: concrete

Lets check the SQL that is generated by the above models:

    // test.php

    // ...
    $sql = Doctrine_Core::generateSqlFromArray(array('Entity', 'User', 'Group'));
    echo $sql[0] . "";
    echo $sql[1] . "";
    echo $sql[2] . "";

The above code would output the following SQL query:

    CREATE TABLE user (id BIGINT AUTO_INCREMENT,
    name VARCHAR(30),
    username VARCHAR(20),
    password VARCHAR(16),
    created BIGINT,
    PRIMARY KEY(id)) ENGINE = INNODB
    CREATE TABLE groups (id BIGINT AUTO_INCREMENT,
    name VARCHAR(30),
    username VARCHAR(20),
    password VARCHAR(16),
    created BIGINT,
    PRIMARY KEY(id)) ENGINE = INNODB
    CREATE TABLE entity (id BIGINT AUTO_INCREMENT,
    name VARCHAR(30),
    username VARCHAR(20),
    password VARCHAR(16),
    created BIGINT,
    PRIMARY KEY(id)) ENGINE = INNODB

## Column Aggregation

In the following example we have one database table called `entity`. `Users` and `groups` are both `entities` and they share the same database table.

The `entity` table has a column called `type` which tells whether an `entity` is a `group` or a `user`. Then we decide that `users` are type 1 and groups type 2.

The only thing we have to do is to create 3 records (the same as before) and add the call to the `Doctrine_Table::setSubclasses()` method from the parent class.

    // models/Entity.php
    class Entity extends Doctrine_Record
    {
        public function setTableDefinition()
        {
            $this->hasColumn('name', 'string', 30);
            $this->hasColumn('username', 'string', 20);
            $this->hasColumn('password', 'string', 16);
            $this->hasColumn('created_at', 'timestamp');
            $this->hasColumn('update_at', 'timestamp');

            $this->setSubclasses(array(
                    'User'  => array('type' => 1),
                    'Group' => array('type' => 2)
                )
            );
        }
    }

    // models/User.php
    class User extends Entity
    { }

    // models/Group.php
    class Group extends Entity
    { }

Here is the same example in YAML format. You can read more about YAML in the YAML Schema Files chapter:

    ---Entity:  columns:    username: string(20)    password: string(16)    created_at: timestamp    updated_at: timestamp    User:        inheritance:          extends: Entity          type: column_aggregation          keyField: type          keyValue: 1    Group:      inheritance:        extends: Entity        type: column_aggregation        keyField: type        keyValue: 2

Lets check the SQL that is generated by the above models:

    // test.php

    // ...
    $sql = Doctrine_Core::generateSqlFromArray(array('Entity', 'User', 'Group'));
    echo $sql[0];

The above code would output the following SQL query:

    CREATE TABLE entity (id BIGINT AUTO_INCREMENT,
    username VARCHAR(20),
    password VARCHAR(16),
    created_at DATETIME,
    updated_at DATETIME,
    type VARCHAR(255),
    PRIMARY KEY(id)) ENGINE = INNODB

.. notice::

Notice how the `type` column was automatically added. This is how column aggregation inheritance knows which model each record in the database belongs to.

This feature also enable us to query the `Entity` table and get a `User` or `Group` object back if the returned object matches the constraints set in the parent class.

See the code example below for an example of this. First lets save a new `User` object:

    // test.php

    // ...
    $user           = new User();
    $user->name     = 'Bjarte S. Karlsen';
    $user->username = 'meus';
    $user->password = 'rat';
    $user->save();

Now lets save a new `Group` object:

    // test.php

    // ...
    $group           = new Group();
    $group->name     = 'Users';
    $group->username = 'users';
    $group->password = 'password';
    $group->save();

Now if we query the `Entity` model for the id of the `User` we created, the Doctrine_Query`will return an instance of` `User`.

    // test.php

    // ...
    $q = Doctrine_Query::create()
        ->from('Entity e')
        ->where('e.id = ?');

    $user = $q->fetchOne(array($user->id));

    echo get_class($user); // User

If we do the same thing as above but for the `Group` record, it will return an instance of `Group`.

    // test.php

    // ...
    $q = Doctrine_Query::create()
        ->from('Entity e')
        ->where('e.id = ?');

    $group = $q->fetchOne(array($group->id));

    echo get_class($group); // Group

.. notice::

The above is possible because of the `type` column. Doctrine knows which class each record was created by, so when data is being hydrated it can be hydrated in to the appropriate sub-class.

We can also query the individual `User` or `Group` models:

    $q = Doctrine_Query::create()
        ->select('u.id')
        ->from('User u');

    echo $q->getSqlQuery();

The above call to `getSql()` would output the following SQL query:

    SELECT e.id AS
    e_id FROM entity e WHERE (e.type = '1')

.. notice::

Notice how the `type` condition was automatically added to the query so that it will only return records that are of type `User`.

## Conclusion

Now that we've learned about how to take advantage of PHPs inheritance features with our models we can move on to learning about Doctrine Behaviors. This is one of the most sophisticated and useful features in Doctrine for accomplishing complex models with small and easy to maintain code.