Zend Framework とともに Doctrine を使用する

Doctrine ORM ツールを Zend Framework によるアプリケーションに統合し、データへのアクセスやデータの操作を簡単に行えるようにする

Doctrine は PHP アプリケーションを開発するための ORM (オブジェクト・リレーショナル・マッピング) ツールです。若干の構成作業を行うことで、Zend Framework に Doctrine を組み合わせることができ、Doctrine のエンティティーを使用して簡単にアプリケーションのデータを操作できるようになります。この記事では、Doctrine 2.3 を Zend Framework 1.x または 2.x によるアプリケーションと統合するプロセスについて説明します。

PHP アプリケーションの開発で最もよく使用されているフレームワークの 1 つである Zend Framework は、容易に使い始めることができるだけでなく、豊富なコンポーネント・ライブラリーがあり、ドキュメント、コード・サンプル、活発なコミュニティーによって十分にサポートされています。また Zend Framework は、MVC (Model-View-Controller) パターンを完全に実装しているため、コードの再利用や関心の分離も促進されます。

この他にも Zend Framework を使用することで得られるメリットとして、サード・パーティーのライブラリーを Zend Framework によるアプリケーションに容易に統合できることが挙げられます。多くの開発者が好んで Zend Framework をサード・パーティーの ORM ツールと組み合わせ、オブジェクトを使用してデータベースの内容を容易に照会、操作できるようにしています。ORM ツールの中でも最もよく使われているものの 1 つが、データベースの抽象化や操作のためのオープンソースの PHP ライブラリーとしてよく知られている Doctrine です。

この記事では以下の内容について説明します。

  • Doctrine ORM と Zend Framework によるアプリケーションとを統合する方法
  • Doctrine のエンティティーを作成するための基本的な方法
  • Doctrine のクラスをロードできるように Zend Framework を構成する方法
  • Zend Framework のアクション・コントローラー内で Doctrine を使用するためのプロセス

始める前に

コードの詳細を説明する前に、いくつかの注意点と前提事項を説明しておきます。

この記事全体をとおして、以下のことを前提とします。

  • Zend Framework を使用したアプリケーション開発の基本原則を理解していること
  • アクションとコントローラーとの相互の関係を理解していること
  • PHP 5.3 での名前空間の実装について理解していること
  • 実際に動作する Apache/PHP/MySQL 開発環境があること
  • PHP の include_path に Zend Framework がインストールされていること
  • SQL の基本を理解していること
  • Apache HTTP Server が .htaccess ファイルによる仮想ホスティングと URL リライティングをサポートするように構成されていること

これらの内容がよくわからない場合は、「参考文献」で詳しい情報を参照してください。

この記事で概説する手法は、オンライン・ドキュメントや優れた PHP 開発者によるさまざまなブログ記事 (「参考文献」を参照) の情報やアイデアを基にしており、この後に記載する各ソフトウェアのバージョンでテストを行っています。しかし、それぞれのソフトウェアは開発や変更が継続的に行われているため、今後のバージョンでは、この記事の手法は正常に機能しなくなる可能性があります。

現在、Zend Framework には 2 つのバージョンがあります。どちらのバージョンの Zend Framework を使用しているかにより、Doctrine を統合する方法は大きく異なります。この記事では、バージョンごとに Doctrine を統合するプロセスを説明します。この記事で使用する各ソフトウェアのバージョンは以下のとおりです。

  • Zend Framework 1.11.11
  • Zend Framework 2.0.3
  • Doctrine 2.3.0

Zend Framework の 2 つのバージョンのシナリオでアプリケーション・データベースは共通なので、データベースのセットアップから始めます。

アプリケーション・データベースをセットアップする

MySQL でテーブルを作成することから始めます。これらのテーブルには、記事に関する情報と、その記事に関連する公開者の情報が格納されます (リスト 1)。

リスト 1. MySQL テーブルを作成する
CREATE TABLE IF NOT EXISTS article (
  id int(11) NOT NULL AUTO_INCREMENT,
  title text NOT NULL,
  url text NOT NULL,
  'date' date NOT NULL,
  publisher int(11) NOT NULL,
  PRIMARY KEY (id)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS publisher (
  id int(11) NOT NULL AUTO_INCREMENT,
  'name' varchar(255) NOT NULL,
  PRIMARY KEY (id)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

これらのテーブルにサンプル・レコードを追加します (リスト 2)。

リスト 2. テーブルにレコードを追加する
INSERT INTO publisher (id, name) VALUES(1, 'Zend Developer Zone');

INSERT INTO publisher (id, name) VALUES(2, 'IBM DeveloperWorks');

INSERT INTO publisher (id, name) VALUES(3, 'Developer.com');

INSERT INTO publisher (id, name) VALUES(4, 'PHP Architect Magazine');

INSERT INTO article (id, title, url, date, publisher) 
VALUES(1, 'Search and integrate Google+ activity streams with PHP applications', 
'http://www.ibm.com/developerworks/xml/library/x-googleplusphp/index.html', '2012-07-10', 
2);

INSERT INTO article (id, title, url, date, publisher) VALUES(2, 
'Getting Started with Zend Server CE', 
'http://devzone.zend.com/1389/getting-started-with-zend-server-ce/', '2009-03-02', 1);

INSERT INTO article (id, title, url, date, publisher) VALUES(3, 
'Integrating Advanced Spring Framework Features with Magnolia CMS', 
'http://www.developer.com/java/web/integrating-advanced-spring-framework-
features-with-magnolia-cms.html', '2010-12-13', 3);

後ほど、これらのデータベース・テーブルを表現する Doctrine オブジェクトを作成し、それらのオブジェクトを Zend Framework のコントローラーの中で使用してデータベースのレコードを操作します。


Zend Framework 1.x によるアプリケーションを初期化する

まず、Doctrine を Zend Framework 1.x によるアプリケーションに統合するプロセスについて説明します。最初に、この記事に示すコードのコンテキストとなる標準的な Zend Framework 1.x によるアプリケーションのセットアップを行います。以下のようにコマンド・プロンプトから Zend Framework のツール・スクリプト (Windows の場合は zf.bat、*NIX の場合は zf.sh) を実行して、新しいプロジェクトを初期化します。

zf.bat create project example

これで、このアプリケーション用の新しい仮想ホスト (http://example.localhost/ など) を Apache の構成の中で定義して、その仮想ホストのドキュメント・ルートをこのアプリケーションの public/ ディレクトリーに設定することができます。仮想ホストのドキュメント・ルートを設定したら、このホストにブラウザーでアクセスすると Zend Framework 1.x のデフォルトのウェルカム・ページが表示されるはずです (図 1)。

図 1. Zend Framework 1.x によるアプリケーションのデフォルトのスタート・ページ
Zend Framework 1.x によるアプリケーションのデフォルトのスタート・ページのスクリーン・キャプチャー

デフォルトで、アプリケーションの名前空間は自動的に「Application」と設定され、アプリケーション専用のクラス (この後で作成する Doctrine エンティティーなど) は $PROJECT/library/Application/ に保存されます。このディレクトリーを手作業で作成します。

次のステップは Doctrine の ORM ライブラリーを追加することです。これらのライブラリーを Doctrine プロジェクトの Web サイト (詳細は、「参考文献」を参照) からダウンロードし、手作業でインストールします。そして、このプロジェクトのアーカイブにある Doctrine/ ディレクトリーの中身を $PROJECT/library/Doctrine/ ディレクトリーにコピーします。

これで、同じ $PROJECT/library/ ディレクトリーに Doctrine のライブラリーと Zend Framework のライブラリーが格納されました。次のステップは、これらのライブラリーを統合することです。これらを統合する手段は Guilherme Blanco 氏によって提供されており、彼の Bisna プロジェクトには、既に Zend Framework 1.x と Doctrine 2.x が統合されたものが提供されています。Bisna プロジェクトのアーカイブをダウンロードして (詳細は、「参考文献」を参照)、アーカイブの library/ ディレクトリーの中身を $PROJECT/library/ ディレクトリーにコピーし、bin/ ディレクトリーの中身を $PROJECT/bin/ ディレクトリーにコピーします。このプロセスが終わると、$PROJECT/library/ フォルダーは図 2 に示すフォルダーと同様の構造になります。

図 2. アプリケーションの library フォルダー
現在、Bisna ディレクトリーと Doctrine ディレクトリーが含まれている、アプリケーションの library フォルダーのスクリーン・キャプチャー

Zend Framework 1.x によるアプリケーションを Doctrine 用に構成する

次のステップは Bisna とやりとりできるようにアプリケーションを構成することです。$PROJECT/configs/application.ini ファイルを開き、最初にいくつかの名前空間をオートローダーに追加します (リスト 3)。

リスト 3. オートローダーに複数の名前空間を追加する
autoloaderNamespaces[] = Bisna
autoloaderNamespaces[] = Doctrine
autoloaderNamespaces[] = Symfony
autoloaderNamespaces[] = Application

次に、リスト 4 に示す複数の構成ディレクティブを追加します。

リスト 4. 複数の構成ディレクティブを追加する
; Bisna
pluginPaths.Bisna\Application\Resource\ = "Bisna/Application/Resource"

; Doctrine
resources.doctrine.classLoader.loaderClass = "Doctrine\Common\ClassLoader"
resources.doctrine.classLoader.loaderFile  = 
  APPLICATION_PATH "/../library/Doctrine/Common/ClassLoader.php"
resources.doctrine.classLoader.loaders.doctrine_common.namespace   = "Doctrine\Common"
resources.doctrine.classLoader.loaders.doctrine_dbal.namespace   = "Doctrine\DBAL"
resources.doctrine.classLoader.loaders.doctrine_orm.namespace   = "Doctrine\ORM"
resources.doctrine.classLoader.loaders.symfony_console.namespace  = 
 "Symfony\Component\Console"

; Doctrine Cache
resources.doctrine.cache.defaultCacheInstance = default
; "default" cache
resources.doctrine.cache.instances.default.id = default
resources.doctrine.cache.instances.default.adapterClass = 
 "Doctrine\Common\Cache\ArrayCache"
resources.doctrine.cache.instances.default.namespace = "Application_"
resources.doctrine.cache.instances.default.options.servers.0.host = localhost
resources.doctrine.cache.instances.default.options.servers.0.port = 11211

; Doctrine DBAL
resources.doctrine.dbal.defaultConnection = default
; "default" connection
resources.doctrine.dbal.connections.default.id = default
resources.doctrine.dbal.connections.default.eventManagerClass = 
 "Doctrine\Common\EventManager"
resources.doctrine.dbal.connections.default.parameters.driver = "pdo_mysql"
resources.doctrine.dbal.connections.default.parameters.dbname = "appdata"
resources.doctrine.dbal.connections.default.parameters.host = "localhost"
resources.doctrine.dbal.connections.default.parameters.port = 3306
resources.doctrine.dbal.connections.default.parameters.user = "root"
resources.doctrine.dbal.connections.default.parameters.password = "guessme"

; Doctrine ORM
resources.doctrine.orm.defaultEntityManager = default
; "default" manager
resources.doctrine.orm.entityManagers.default.id = default
resources.doctrine.orm.entityManagers.default.entityManagerClass = 
 "Doctrine\ORM\EntityManager"
resources.doctrine.orm.entityManagers.default.configurationClass = 
 "Doctrine\ORM\Configuration"
resources.doctrine.orm.entityManagers.default.defaultRepositoryClass = 
 "Doctrine\ORM\EntityRepository"
resources.doctrine.orm.entityManagers.default.entityNamespaces.app = "Application\Entity"
resources.doctrine.orm.entityManagers.default.connection = default
resources.doctrine.orm.entityManagers.default.proxy.autoGenerateClasses = true
resources.doctrine.orm.entityManagers.default.proxy.namespace = 
 "Application\Entity\Proxy"
resources.doctrine.orm.entityManagers.default.proxy.dir = 
 APPLICATION_PATH "/../library/Application/Entity/Proxy"
resources.doctrine.orm.entityManagers.default.metadataDrivers.
 annotationRegistry.annotationFiles[] = 
 APPLICATION_PATH "/../library/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php"
resources.doctrine.orm.entityManagers.default.metadataDrivers.drivers.0.adapterClass =
 "Doctrine\ORM\Mapping\Driver\AnnotationDriver"
resources.doctrine.orm.entityManagers.default.metadataDrivers.drivers.0.
 mappingNamespace = "Application\Entity"
resources.doctrine.orm.entityManagers.default.metadataDrivers.drivers.0.
 mappingDirs[] = APPLICATION_PATH "/../library/Application/Entity"
resources.doctrine.orm.entityManagers.default.metadataDrivers.drivers.0.
 annotationReaderClass = "Doctrine\Common\Annotations\AnnotationReader"
resources.doctrine.orm.entityManagers.default.metadataDrivers.drivers.0.
 annotationReaderCache = default

上記ディレクティブの中で ”default” 接続用に記述されたデータベースのクレデンシャルを更新し、先ほどセットアップしたデータベース・サーバー・ホストのクレデンシャルを反映するようにします。

コマンドライン・スクリプトを使用して Doctrine のエンティティーを生成できるようにするには、アプリケーションのブートストラッパー ($PROJECT/application/Bootstrap.php) も更新する必要あります。リスト 5 は Symfony 名前空間のためのコードです。

リスト 5. アプリケーションのブートストラッパーを更新する
<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    public function _initAutoloaderNamespaces()
    {
        require_once APPLICATION_PATH . '/../library/Doctrine/Common/ClassLoader.php';
        $autoloader = \Zend_Loader_Autoloader::getInstance();
        $symfonyAutoloader = new \Doctrine\Common\ClassLoader('Symfony', 'Doctrine');
        $autoloader->pushAutoloader(array($symfonyAutoloader, 'loadClass'), 'Symfony');
    }
}

Doctrine のエンティティーを生成する

これで、アクション・コントローラー内で使用可能な Doctrine のエンティティーを生成する準備が整いました。多くの場合、エンティティーを手作業で生成した後 (詳細は、「参考文献」を参照)、Doctrine がそれらのエンティティーを使用してデータベースを作成するようにします。ここでは既にデータベースがあるので、Doctrine にスタブ・エンティティーを自動生成させて、後から内容を追加することで時間を節約することができます。この自動生成プロセスは、作業を開始する手っ取り早い方法ではありますが、すべてを自動で行ってくれるわけではありません。例えば、生成されたエンティティーは、手作業により、テーブルのリレーションシップに関する情報で更新する必要があります。

データベースからエンティティーを生成するには、コマンド・コンソールを開きます。カレント・ディレクトリーを $PROJECT/bin/ に変更し、リスト 6 のコマンドを実行します (必要に応じて、PHP インタープリターのパスを変更してください)。

リスト 6. データベースから Doctrine エンティティーを生成する
php doctrine.php orm:convert-mapping --namespace=Application\Entity\ 
          --from-database annotation ..\library\

php doctrine.php orm:generate-entities --generate-annotations="true" 
          --generate-methods="true" ..\library\

リスト 6 の最初のコマンドは、アノテーションが付けられた Article (記事) エンティティーと Publisher (公開者) エンティティーを既存のデータベース・スキーマに基づいて生成し、2 番目のコマンドは、これらのエンティティーを更新してセッター・メソッドとゲッター・メソッドを追加します。生成されたエンティティーは $PROJECT/library/Application/Entity/ ディレクトリーに保存されます (図 3)。

図 3. 自動生成された、データベースのエンティティー
Entity フォルダー内に自動生成された Doctrine エンティティーを表示する、アプリケーション・フォルダーの階層構造のスクリーン・キャプチャー

リレーションシップに関する情報を更新する

先ほど触れたように、これらのエンティティーは不完全であり、リレーションシップに関する情報で更新する必要があります。このサンプル・シナリオでは、1 つの Publisher (公開者) が多くの Article (記事) を公開している可能性があるため、ArticlePublisher と多対 1 の関係があります。この情報を手作業で $PROJECT/library/Application/Entity/Article.php クラスに追加するために、リレーションシップに関する情報をリスト 7 のアノテーションで更新します。

リスト 7. エンティティーのリレーションシップに関する情報を更新する
<?php

namespace Application\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Application\Entity\Article
 *
 * @ORM\Table(name="article")
 * @ORM\Entity
 */
class Article
{
    /* other annotations */

    /**
     * @var integer $publisher
     *
     * @ORM\ManyToOne(targetEntity="Publisher", fetch="EAGER")
     * @ORM\JoinColumn(name="publisher", referencedColumnName="id")
     */
    private $publisher;

    /* other annotations and methods */
}

これで、これらのエンティティーをアプリケーション・コードの中で使用できるようになりました。


Zend Framework 1.x コントローラーの中で Doctrine を使用する

Doctrine が構成され、必要なエンティティーがすべて用意できたので、これらのエンティティーをアクション・コントローラーの中で容易に使い始めることができます。まずは簡単な例として、記事の情報を公開者の情報と併せて一覧にすることから始めましょう。リスト 8 のコードを $PROJECT/application/controllers/ArticleController.php に保存します。

リスト 8. 目録作成アクション
<?php
class ArticleController extends Zend_Controller_Action
{

    public function init()
    {
        /* Initialize action controller here */
    }
    
    public function getDoctrineContainer()
    {
        return $this->getInvokeArg('bootstrap')->getResource('doctrine');
    }   

    public function indexAction()
    {
        $doctrine = $this->getDoctrineContainer();
        $em = $doctrine->getEntityManager();
        $articles = $em->getRepository('\Application\Entity\Article')
                    ->findAll();
        $this->view->articles = $articles;        
    }    

}

ArticleController::indexAction は、getDoctrineContainer() メソッドを呼び出して DoctrineContainer オブジェクトを取得することから始めます。続いて、このオブジェクトの getEntityManager() メソッドを呼び出すことでエンティティー・マネージャーを取得します。このエンティティー・マネージャーによってリポジトリーから Article (記事) エンティティーを取得し、そのエンティティーの findAll() メソッドを呼び出すことで、格納されているすべての記事のレコードを取得します。こうして取得されたコレクションはビューに転送され、ビューはこのコレクションに繰り返し処理を実行し、エンティティーのゲッターを使用してレコードの個々のフィールドを返します。リスト 9 のビュー・スクリプトを $PROJECT/application/views/scripts/article/index.phtml に保存します。

リスト 9. 目録作成アクションのビュー・スクリプト
<h2>Articles</h2>
<ol>
<?php foreach($this->articles as $article): ?>
<li>
<a href="<?php echo $article->getUrl(); ?>">
  <?php echo $article->getTitle(); ?></a> <br/>
<?php echo $article->getDate()->format('d M Y'); ?> | 
  <?php echo $article->getPublisher()->getName(); ?><br/>
</li>
<?php endforeach; ?>
</ol>

図 4 はコントローラーの URL (http://example.localhost/article/index) をブラウズすると表示される出力の例です。(この出力は、3 つの記事についての番号付きリストであり、各記事のタイトル、公開日、Web サイトでの公開者を表示しています。)

図 4. Doctrine を使用してレコードの一覧を表示する
Doctrine を使用して 3 つの記事のレコードを一覧表示するページが表示されたブラウザーのスクリーン・キャプチャー

同様に、図 5 に示すようなフォームからの入力を使用してデータベースにレコードを追加することもできます。

図 5. 新規レコードを作成するための Web フォーム
新規レコードを作成するための Web フォームが表示されたブラウザーのスクリーン・キャプチャー

リスト 10 に、そのようなフォームを使用するためのサンプル実装を示します。

リスト 10. アクションを作成する
<?php
class ArticleController extends Zend_Controller_Action
{

    public function init()
    {
        /* Initialize action controller here */
    }
    
    public function getDoctrineContainer()
    {
        return $this->getInvokeArg('bootstrap')->getResource('doctrine');
    }   

    public function createAction()
    {
        $form = new \Application_Form_ArticleCreate;
        $this->view->form = $form;
        if ($this->getRequest()->isPost()) {
          if ($form->isValid($this->getRequest()->getPost())) {
            $doctrine = $this->getDoctrineContainer();
            $em = $doctrine->getEntityManager();
            $article = new \Application\Entity\Article;
            $article->setTitle($form->getValue('title'));
            $article->setUrl($form->getValue('url'));
            $publisher = $em->getRepository('\Application\Entity\Publisher')
                         ->find($form->getValue('publisher_id'));
            $article->setPublisher($publisher);
            $date = new \DateTime($form->getValue('date'));
            $article->setDate($date);
            $em->persist($article); 
            $em->flush();
            $this->_helper->getHelper('FlashMessenger')
                 ->addMessage('Your submission has been accepted as 
                   item #' . $article->getId() . '.');
          }   
        } 
    }
}

リスト 10 のコードは Web フォームからの入力を読み取って検証します。そして新しい Article (記事) エンティティーを初期化し、そのエンティティーのセッター・メソッドを使用して Web フォームへの入力をエンティティーの各種プロパティーに設定します。こうして作成されたオブジェクトは、エンティティー・マネージャーの persist() メソッドと flush() メソッドを使用してデータベースに書き込まれます。またエンティティー・マネージャーは、(フォームに入力された公開者の識別情報を使用して) データベースから Publisher (公開者) エンティティーも取得し、setPublisher() メソッドを使用して Publisher (公開者) エンティティーを Article (記事) エンティティーに関連付けます。

これらのリストからわかるように、Bisna の統合成果物の助けを少し借りれば、Zend Framework 1.x によるアプリケーションの中で Doctrine 2.3 のエンティティーを使用するのは難しいことではありません。しかし最新のものを使いたく、Zend Framework 2.x によるアプリケーションに Doctrine を統合したい場合はどうすればよいのでしょう?その方法を知るには、この先を読んでください。


Zend Framework 2.x によるアプリケーションを初期化する

まず、この記事で示すコードのコンテキストとなる標準的な Zend Framework 2.x によるアプリケーションをセットアップします。この記事の執筆時点で、Zend Framework 2.x にはツール・スクリプトが含まれていません。新しいプロジェクトを初期化するには、Zend Framework のスケルトン・アプリケーション (詳細は、「参考文献」を参照) をダウンロードして、その内容物をお使いのシステムのディレクトリーに解凍します。

続いてこのアプリケーション用の新しい仮想ホスト (http://example.localhost/ など) を Apache の構成の中で定義し、この仮想ホストのドキュメント・ルートをスケルトン・アプリケーションの public/ ディレクトリーに設定します。このホストにブラウザーでアクセスすると Zend Framework 2.x のデフォルトのウェルカム・ページが表示されるはずです (図 6)。

図 6. Zend Framework 2.x によるアプリケーションのデフォルトのスタート・ページ
Zend Framework 2.x によるアプリケーションのデフォルトのスタート・ページが表示されたブラウザーのスクリーン・キャプチャー

Zend Framework 2.x アプリケーションに依存関係 (Doctrine など) を含めるお勧めの方法は、Composer を使用する方法です。$PROJECT/composer.json ファイルを編集し、リスト 11 のエントリーで表される Zend Framework 2.x 用の Doctrine ORM モジュールをプロジェクトに追加します。

リスト 11. Doctrine ORM モジュールをプロジェクトに追加する
{
    "minimum-stability": "alpha",
    "require": {
        "php": ">=5.3.3",
        "zendframework/zendframework": "2.*",
        "doctrine/doctrine-orm-module": "0.*"

    }
}

次に、以下のコマンドを実行することにより、Composer を実行します。

php composer.phar self-update
php composer.phar install

これにより、Composer は Zend Framework と Doctrine ORM モジュールのライブラリーのダウンロードを開始し、Composer のオートローダーを適切に設定します。このプロセスが完了すると、$PROJECT/vendor/ フォルダーは図 7 に示すフォルダーと同様の構造になります。

図 7. アプリケーションの vendor フォルダー
アプリケーションの vendor フォルダーのスクリーン・キャプチャー

Zend Framework 2.x によるアプリケーションを Doctrine 用に構成する

次のステップは、Doctrine を扱えるようにアプリケーションを構成することです。$PROJECT/config/application.config.php ファイルを開き、モジュール・リストに Doctrine を追加します (リスト 12)。

リスト 12. モジュール・リストに Doctrine を追加する
<?php
return array(
    'modules' => array(
        'Application',
        'DoctrineModule',
        'DoctrineORMModule'
    ),
    // other options
    ),
);

デフォルトで、スケルトン・アプリケーションには Application モジュールと名前空間が含まれています。このモジュールの構成ファイルに Doctrine ドライバーを追加するには、$PROJECT/module/Application/config/module.config.php ファイルを編集し、構成配列に doctrine キーを追加します (リスト 13)。

リスト 13. Doctrine ドライバーを追加する
<?php
return array(

    // other options
 
    'doctrine' => array(
        'driver' => array(
            'Application_driver' => array(
                'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
                'cache' => 'array',
                'paths' => array(__DIR__ . '/../src/Application/Entity')
            ),
            'orm_default' => array(
                'drivers' => array(
                    'Application\Entity' => 'Application_driver'
                )
            )    
        )
    )
);

最後に、$PROJECT/config/autoload/local.php 構成ファイルにデータベースのクレデンシャルを追加します (リスト 14)。

リスト 14. データベースのクレデンシャルを追加する
<?php
return array(
    // ...
    'doctrine' => array(
        'connection' => array(
            'orm_default' => array(
                'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
                'params' => array(


                    'host'     => 'localhost',
                    'port'     => '3306',
                    'user'     => 'root',
                    'password' => 'guessme',
                    'dbname'   => 'appdata',
                )
            )
        )
    )
);

必ず、データベース・サーバー・ホストのクレデンシャルを反映するように、リスト 14 に示すデータベースのクレデンシャルを更新してください。

Doctrine エンティティーを生成する

これで、アクション・コントローラー内で使用可能な Doctrine エンティティーを生成する準備が整いました。コマンドライン・ツールを使用すると、これらのエンティティーをデータベースから直接生成することができます。生成したエンティティーは、既存のテーブルのリレーションシップに注意して手作業で更新する必要があります。

データベースからエンティティーを生成するには、コマンド・コンソールを開きます。カレント・ディレクトリーを $PROJECT/vendor/bin/ に変更し、リスト 15 のコマンドを実行します (必要に応じて、PHP インタープリターのパスを変更してください)。

リスト 15. データベースから Doctrine エンティティーを生成する
doctrine-module orm:convert-mapping --namespace=Application\Entity\ 
          --from-database annotation module\Application\src

doctrine-module orm:generate-entities --generate-annotations="true" 
          --generate-methods="true" module\Application\src

リスト 15 の最初のコマンドは、アノテーションが付けられた Article (記事) エンティティーと Publisher (公開者) エンティティーを既存のデータベース・スキーマに基づいて生成し、2 番目のコマンドは、これらのエンティティーを更新してセッター・メソッドとゲッター・メソッドを追加します。生成されたエンティティーは $PROJECT/module/Application/src/Application/Entity/ ディレクトリーに保存されます (図 8)。

図 8. 自動生成された Doctrine エンティティー
Entity フォルダー内に自動生成された Doctrine エンティティーを表示する、アプリケーション・フォルダーの階層構造のスクリーン・キャプチャー

リレーションシップに関する情報を更新する

このサンプル・シナリオでは、1 つの Publisher (公開者) が多くの Article (記事) を公開している可能性があるため、ArticlePublisher と多対 1 の関係があります。そこで、Article (記事) と Publisher (公開者) との間の ManyToOne の関係を手作業で $PROJECT/module/Application/src/Application/Entity/Article.php クラスに追加するために、リスト 16 のアノテーションでこのクラスを更新します。

リスト 16. エンティティーのリレーションシップに関する情報を更新する
<?php

namespace Application\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Application\Entity\Article
 *
 * @ORM\Table(name="article")
 * @ORM\Entity
 */
class Article
{
    /* other annotations */

    /**
     * @var integer $publisher
     *
     * @ORM\ManyToOne(targetEntity="Publisher", fetch="EAGER")
     * @ORM\JoinColumn(name="publisher", referencedColumnName="id")
     */
    private $publisher;

    /* other annotations and methods */
}

これで、これらのエンティティーをアプリケーション・コードの中で使い始めることができます。


Zend Framework 2.x コントローラーの中で Doctrine を使用する

Doctrine を使用して記事と公開者を一覧表示する、リスト 17 のコードを $PROJECT/module/Application/src/Application/Controller/ArticleController.php に保存します。

リスト 17. 記事と公開者の一覧を生成する
namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

class ArticleController extends AbstractActionController
{
    public function indexAction()
    {
        $em = $this->getServiceLocator()
              ->get('doctrine.entitymanager.orm_default');
        $articles = $em->getRepository('\Application\Entity\Article')->findAll();   
        return new ViewModel(array('articles' => $articles));
    }

}

リスト 17 には ArticleController が含まれており、この ArticleControllerindexAction() によって記事と公開者の一覧が生成されます。この関数は、まず始めに getServiceLocator() メソッドによって Doctrine エンティティー・マネージャーのインスタンスを取得し、続いてそのエンティティー・マネージャーの findAll() メソッドによって Article (記事) のコレクションを取得します。このコレクションは $PROJECT/module/Application/src/view/application/article/index.phtml 内のビュー・スクリプトに渡され、そのビュー・スクリプトがゲッター・メソッドを使用して、渡されたコレクションから必要な情報を抽出します。リスト 18 に、このビュー・スクリプトを示します。

リスト 18. 目録作成アクションのビュー・スクリプト
<h2>Articles</h2>
<ol>
<?php foreach($articles as $article): ?>
<li>
<a href="<?php echo $article->getUrl(); ?>">
  <?php echo $article->getTitle(); ?></a> <br/>
<?php echo $article->getDate()->format('d M Y'); ?> | 
  <?php echo $article->getPublisher()->getName(); ?><br/>
</li>
<?php endforeach; ?>
</ol>

また、この新しいコントローラーを、$PROJECT/module/Application/config/module.config.php 内の invokabless 配列に追加する必要があります (リスト 19)。

リスト 19. 新しいコントローラーを invokables 配列に追加する
<?php
return array(
    // other options
    'controllers' => array(
        'invokables' => array(
            'Application\Controller\Index' => 
              'Application\Controller\IndexController',
            'Application\Controller\Article' => 
              'Application\Controller\ArticleController'
        ),
    ),
);

これで、http://example.localhost/application/article/index という URL にブラウザーでアクセスすると、図 9 のような出力が表示されるはずです。(この出力は、3 つの記事の番号付きリストになっており、各記事のタイトル、公開日、Web サイトでの公開者を表示しています。)

図 9. Doctrine を使用してレコードの一覧を表示する
Doctrine を使用してレコードを一覧表示するページが表示されたブラウザーのスクリーン・キャプチャー

同様の方法で、Web フォームから記事のデータを読み取ってデータベースの新しいレコードとして永続化する createAction() を作成することもできます。リスト 20 は、その実装の一例です。

リスト 20. アクションを作成する
<?php
namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

class ArticleController extends AbstractActionController
{
 
    public function createAction()

    {
      $form = new \Application\Form\ArticleCreate();
      $request = $this->getRequest();
      
      if ($request->isPost()) {
          $a = new \Application\InputFilter\Article();
          $form->setInputFilter($a->getInputFilter());
          $form->setData($request->getPost());
          if ($form->isValid()) {
            $data = $form->getData();
            $sl = $this->getServiceLocator();
            $em = $sl->get('doctrine.entitymanager.orm_default');
            $article = new \Application\Entity\Article;
            $article->setTitle($data['title']);
            $article->setUrl($data['url']);
            $publisher = $em->getRepository('\Application\Entity\Publisher')
                         ->find($data['publisher']);
            $article->setPublisher($publisher);
            $date = new \DateTime($data['date']);
            $article->setDate($date);
            $em->persist($article); 
            $em->flush();
            $this->flashMessenger()->addMessage('Your submission has 
              been accepted as item #' . $article->getId() . '.');
          }
      } 
      return new ViewModel(array('form' => $form));
    }

}

リスト 20 では Web フォームから提供される入力を読み取って検証します。次にサービス・ロケーターを使用して Doctrine エンティティー・マネージャーを取得し、新しい Article (記事) エンティティーを初期化します。初期化が完了すると、このエンティティーのセッター・メソッドによって Web フォームへの入力をエンティティーの各種プロパティーに設定します。こうして作成されるオブジェクトは、エンティティー・マネージャーの persist() メソッドと flush() メソッドを使用してデータベースに書き込まれます。

また、ここまでのリストに示したメソッドと同様の手法を使用して、記事を更新、削除、検索するメソッドを作成することもできます。


まとめ

この記事で説明したように、Zend Framework 1.x または 2.x によるアプリケーションには Doctrine の強力さを追加することができます。Doctrine と Zend Framework を互いに連携させるのは、多少複雑な作業ではありますが、その手間をかけるだけの価値があります。そうすることで、Zend Framework によるアプリケーションの中から高度なデータ操作をするために、Doctrine の ORM 機能のすべてを利用できるようになるからです。今すぐにでも Doctrine とお好みのバージョンの Zend Framework を入手し、それらを連携動作させてください。

参考文献

学ぶために

製品や技術を入手するために

  • Zend Framework: Zend Framework をダウンロードしてください。
  • Doctrine: Doctrine ORMツールをダウンロードしてください。
  • Bisna: Zend Framework 1.x のための Bisna による統合をダウンロードしてください。
  • Zend Framework 2.x のための Doctrine ORM モジュール: このモジュールをダウンロードしてください。
  • Composer: Composer 依存関係マネージャーをダウンロードしてください。
  • 皆さんの次のオープンソース開発プロジェクトを IBM ソフトウェアの評価版を使用して革新してください。ダウンロード、あるいは DVD で入手することができます。

議論するために

  • developerWorks コミュニティーに参加してください。ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者によるブログ、フォーラム、グループ、Wiki を調べることができます。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source, Web development
ArticleID=935133
ArticleTitle=Zend Framework とともに Doctrine を使用する
publish-date=03202014