You are currently reading the 1.2 documentation. Switch to 2.2  2.1  2.0 

Inheritance

Doctrineは3種類の継承戦略をサポートします。これらの戦略は混ぜることができます。三種類は単一、具象とカラム集約です。これらの異なる継承の使い方をこの章で学びます。

この章では前の章で利用してきたテスト環境の既存のすべてのスキーマとモデルを削除してください。

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

単一継承

単一継承は最も簡単でシンプルです。単一継承においてすべての子クラスは同じカラムを親として共有しすべての情報は親テーブルに保存されます。

// 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');
    }
}

Entityを継承するUserモデルを作りましょう:

// models/User.php

class User extends Entity
{ }

Groupモデルに対しても同じことを行いましょう:

// models/Group.php

class Group extends Entity
{ }

YAMLフォーマットでの例は次の通りです。YAML Schema Filesの章でYAMLの詳細を読むことができます:

---
# schema.yml

# ...
Entity:
  columns:
    name: string(30)
    username: string(20)
    password: string(16)
    created_at: timestamp
    updated_at: timestamp

User:
  inheritance:
    extends: Entity
    type: simple

Group:
  inheritance:
    extends: Entity
    type: simple

上記のコードによって生成されたSQLを確認してみましょう:

// test.php

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

上記のコードは次のSQLクエリを出力します:

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

YAMLスキーマファイルを使うとき子クラスでカラムを定義できますがYAMLが解析されるときカラムは自動的に親に移動します。これはカラムを簡単に組織できるようにするためだけです。

具象継承

具象継承は子クラス用の独立したテーブルを作成します。しかしながら具象継承ではそれぞれのクラスはすべてのカラムを含むテーブルを生成します(継承されたカラムを含む)。具象継承を使うには下記で示されるように子クラスへの明示的なparent::setTableDefinition()を追加する必要があります。

// models/TextItem.php

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

TextItemを継承するCommentという名前のモデルを作りcontentという名前のカラムを追加してみましょう:

// models/Comment.php

class Comment extends TextItem
{
    public function setTableDefinition()
    {
        parent::setTableDefinition();

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

YAMLフォーマットでの例は次の通りです。YAML Schema Filesの章でYAMLの詳細を読むことができます:

---
# schema.yml

# ...
TextItem:
  columns:
    topic: string(100)

Comment:
  inheritance:
    extends: TextItem
    type: concrete
  columns:
    content: string(300)

上記のモデルによって生成されたSQLをチェックしてみましょう:

// test.php

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

上記のコードは次のSQLクエリを出力します:

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

具象クラスにおいて追加のカラムを定義する必要はありませんが、それぞれのクラス用に個別のテーブルを作るにはsetTableDefinition()の呼び出しを繰り返し書かなければなりません。

次の例ではentityusergroupと呼ばれるデータベーステーブルがあります。Usersgroupsは両方ともentitiesです。行わなければならないことは3つのクラス(EntityGroupUser)を書きsetTableDefinition()メソッドの呼び出しを繰り返し記述することです。

// 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()
    {
        // 次のメソッド呼び出しは
        // 具象継承で必要
        parent::setTableDefinition();
    }
}

// models/Group.php
class Group extends Entity
{
    public function setTableDefinition()
    {
        // 次のメソッド呼び出しは
        // 具象継承で必要
        parent::setTableDefinition();
    }
}

YAMLフォーマットでの例は次の通りです。YAML Schema Filesの章でYAMLの詳細を読むことができます:

---
Entity:
  columns:
    name: string(30)
    username: string(20)
    password: string(16)
    created: integer(11)

User:
  inheritance:
    extends: Entity
    type: concrete

Group:
  tableName: groups
  inheritance:
    extends: Entity
    type: concrete

上記のモデルによって生成されたSQLをチェックしてみましょう:

// test.php

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

上記のコードは次のSQLクエリを出力します:

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

カラム集約

次の例においてentityという名前の1つのデータベーステーブルがあります。Usersgroupsは両方ともentitiesでこれらは同じデータベーステーブルを共有します。

entityテーブルはtypeと呼ばれる1つのカラムを持ちます。このカラムはgroupもしくはuserであることを伝えます。usersはタイプ1でグループはタイプ2であると決めます。

行わなければならない唯一の作業は3のレコード(以前と同じ)を作成し親クラスからのDoctrine_Table::setSubclasses()メソッド呼び出しを追加することです。

// 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
{ }

YAMLフォーマットでの例は次の通りです。YAML Schema Filesの章でYAMLの詳細を読むことができます:

---
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

上記のモデルによって生成されたSQLをチェックしてみましょう:

// test.php

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

上記のコードは次のSQLクエリを出力します:

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

typeカラムが自動的に追加されたことに注目してください。データベースのそれぞれのレコードが所属するモデルを知っているカラム集約継承です。

この機能によってEntityテーブルにクエリを行い変えされたオブジェクトが親クラスで設定された制約にマッチする場合UserもしくはGroupオブジェクトを戻します。

具体的な内容は下記のコードで見てみましょう。最初に新しいUserオブジェクトを保存しましょう:

// test.php

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

新しいGroupオブジェクトを保存しましょう:

// test.php

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

作成したUserのid用のEntityモデルにクエリを行うと、Doctrine_QueryUserのインスタンスを返します。

// test.php

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

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

echo get_class($user); // User

Groupレコードに対して同じようなことを行うと、Groupのインスタンスが戻されます。

// test.php

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

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

echo get_class($group); // Group

上記の内容はtypeカラムであるから可能です。Doctrineはどのクラスによってそれぞれのレコードが作成されたのか知っているので、データは適切なサブクラスにハイドレイトされます。

個別のUserもしくはGroupモデルにクエリを行うこともできます:

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

echo $q->getSqlQuery();

上記のgetSql()の呼び出しは次のSQLクエリを出力します:

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

User型であるレコードのみが返されるようにtypeの条件が自動的に追加されたことに注目してください。

まとめ

モデルでPHPの継承機能を利用する方法を学んだのでBehaviorsの章に移動します。これは複雑なモデルを小さくて簡単なコードで維持するためのもっとも洗練された便利な機能の1つです。


Questions and Feedback

If you find a problem with the documentation or have a suggestion, please register and open a ticket.

If you need support or have a technical question, you can post to the user mailing-list.