somemo programming etc.

プログラマ、雑記、プログラミング関係はLinkから、数式はこっちでまとめていることが多い

【Zendframework】データベースの接続とモデル

前回で、MVCのM以外を設定完了したので残りのモデルを作成します。その後、モデルを通してデータベースに接続します。

データベース作成

今回は、データベースにMySQLとライブラリにPDOを使用します。

以下、データベースおよびユーザー作成スクリプトです。

CREATE DATABASE `zendframework` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE USER 'zenduser'@'localhost' IDENTIFIED BY '***';

GRANT USAGE ON * . * TO 'zenduser'@'localhost' IDENTIFIED BY '***' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0 ;

GRANT ALL PRIVILEGES ON `zendframework` . * TO 'zenduser'@'localhost';

テーブル作成

以下、テーブルおよびデータ作成スクリプトです。

CREATE TABLE `zendframework`.`guestbook` (
`id` INT NOT NULL AUTO_INCREMENT ,
`email` VARCHAR( 32 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT 'noemail@test.com',
`comment` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL ,
`created` DATETIME NOT NULL ,
PRIMARY KEY ( `id` )
) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

INSERT INTO guestbook (email, comment, created) VALUES 
('ralph.schindler@zend.com', 'Hello! Hope you enjoy this sample zf application!', now()),
('foo@bar.com',              'Baz baz baz, baz baz Baz baz baz - baz baz baz.',   now());

データベース設定

application.iniにデータベースの設定をリソースとして記述します。アダプタとして使用するドライバーと各種データベース設定、最後にドライバーのデフォルト使用を決定します。

[production]
resources.db.adapter               = "pdo_mysql"
resources.db.params.host           = "localhost"
resources.db.params.username       = "zenduser"
resources.db.params.password       = "xxx"
resources.db.params.dbname         = "zendframework"
resources.db.isDefaultTableAdapter = true

Zend_toolを使用して、[testing : production]と[development : production]にも設定を追加します。

zf.bat configure db-adapter "adapter=pdo_mysql&host=localhost&username=zenduser&password=xxx&dbname=zendframework" testing

application.iniの指定したセクション?にあるresources.db.paramsに、指定したキーとその値が追加されているはずです。余談ですが、[testing : production]は、productionの設定を継承しているようです。

ブートストラップ設定

application.iniにデータベースの設定をリソースとして記述します。記述されたデータベースの設定は、bootstorapを通して取得します。

protected function _initDb()
{
    $resource = $this->getPluginResource('db');

    $db = Zend_Db::factory($resource->getAdapter(), $resource->getParams());
    Zend_Registry::set('database', $db);
    Zend_Db_Table::setDefaultAdapter($db);
}

モデル作成

データベースに作成したテーブルに対応するモデルを作成していきます。作成するモデルは以下の3つです。

  • Zend_Db_Table_Abstractのサブクラス(TableDataGateway)
  • ドメインモデルに対応するマッパー
  • ドメインモデル

これらをZend_toolを使用して作成します。

#Zend_Db_Table_Abstractのサブクラス
zf.bat create db-table Guestbook guestbook
Creating a DbTable at C:\work\programming\php\zend/application/models/DbTable/Guestbook.php
Updating project profile 'C:\work\programming\php\zend/.zfproject.xml'

#作成されるファイル
<?php
class Application_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
{
    protected $_name = 'guestbook';
}
#ドメインモデルに対応するマッパー
zf.bat create model GuestbookMapper
Creating a model at C:\work\programming\php\zend/application/models/GuestbookMapper.php
Updating project profile 'C:\work\programming\php\zend/.zfproject.xml'

#作成されるファイル(クラス定義だけ)に、修正を加えたもの
<?php

class Application_Model_GuestbookMapper
{
    protected $_dbTable;

    public function setDbTable($dbTable)
    {
        if (is_string($dbTable)) {
            $dbTable = new $dbTable();
        }
        if (!$dbTable instanceof Zend_Db_Table_Abstract) {
            throw new Exception('Invalid table data gateway provided');
        }
        $this->_dbTable = $dbTable;
        return $this;
    }

    public function getDbTable()
    {
        if (null === $this->_dbTable) {
            $this->setDbTable('Application_Model_DbTable_Guestbook');
        }
        return $this->_dbTable;
    }

    public function save(Application_Model_Guestbook $guestbook)
    {
        $data = array(
            'email'   => $guestbook->getEmail(),
            'comment' => $guestbook->getComment(),
            'created' => date('Y-m-d H:i:s'),
        );

        if (null === ($id = $guestbook->getId())) {
            unset($data['id']);
            $this->getDbTable()->insert($data);
        } else {
            $this->getDbTable()->update($data, array('id = ?' => $id));
        }
    }

    public function find($id, Application_Model_Guestbook $guestbook)
    {
        $result = $this->getDbTable()->find($id);
        if (0 == count($result)) {
            return;
        }
        $row = $result->current();
        $guestbook->setId($row->id)
                  ->setEmail($row->email)
                  ->setComment($row->comment)
                  ->setCreated($row->created);
    }

    public function fetchAll()
    {
        $resultSet = $this->getDbTable()->fetchAll();
        $entries   = array();
        foreach ($resultSet as $row) {
            $entry = new Application_Model_Guestbook();
            $entry->setId($row->id)
                  ->setEmail($row->email)
                  ->setComment($row->comment)
                  ->setCreated($row->created);
            $entries[] = $entry;
        }
        return $entries;
    }
}

#ドメインモデル
zf.bat create model Guestbook
Creating a model at C:\work\programming\php\zend/application/models/Guestbook.php
Updating project profile 'C:\work\programming\php\zend/.zfproject.xml'

#作成されるファイル(クラス定義だけ)に、修正を加えたもの
<?php
class Application_Model_Guestbook
{
<?php

class Application_Model_Guestbook
{
    protected $_comment;
    protected $_created;
    protected $_email;
    protected $_id;
 
    public function __construct(array $options = null)
    {
        if (is_array($options)) {
            $this->setOptions($options);
        }
    }
 
    public function __set($name, $value)
    {
        $method = 'set' . $name;
        if *1 {
            throw new Exception('Invalid guestbook property');
        }
        $this->$method($value);
    }
 
    public function __get($name)
    {
        $method = 'get' . $name;
        if *2 {
            throw new Exception('Invalid guestbook property');
        }
        return $this->$method();
    }
 
    public function setOptions(array $options)
    {
        $methods = get_class_methods($this);
        foreach ($options as $key => $value) {
            $method = 'set' . ucfirst($key);
            if (in_array($method, $methods)) {
                $this->$method($value);
            }
        }
        return $this;
    }
 
    public function setComment($text)
    {
        $this->_comment = (string) $text;
        return $this;
    }
 
    public function getComment()
    {
        return $this->_comment;
    }
 
    public function setEmail($email)
    {
        $this->_email = (string) $email;
        return $this;
    }
 
    public function getEmail()
    {
        return $this->_email;
    }
 
    public function setCreated($ts)
    {
        $this->_created = $ts;
        return $this;
    }
 
    public function getCreated()
    {
        return $this->_created;
    }
 
    public function setId($id)
    {
        $this->_id = (int) $id;
        return $this;
    }
 
    public function getId()
    {
        return $this->_id;
    }
}

}

コントローラ作成

コントローラを作成します。InexActionとそれに対応するVIEWおよびテストコードが作成されます。

zf.bat create controller Guestbook
Creating a controller at C:\work\programming\php\zend/application/controllers/GuestbookController.php
Creating an index action method in controller Guestbook
Creating a view script for the index action method at C:\work\programming\php\zend/application/views/scripts/guestbook/index.phtml
Creating a controller test file at C:\work\programming\php\zend/tests/application/controllers/GuestbookControllerTest.php
Updating project profile 'C:\work\programming\php\zend/.zfproject.xml'

コントローラはIndexと変わりないです。まず、コントローラを修正します。

<?php

class GuestbookController extends Zend_Controller_Action
{

    public function init()
    {
        /* Initialize action controller here */
    }

    public function indexAction()
    {
        // action body
        $guestbook = new Application_Model_GuestbookMapper();
        $this->view->entries = $guestbook->fetchAll();
    }
}

次にビューの修正をします。

// toolによる作成
<br /><br />
<div id="view-content">
	<p>View script for controller <b>Guestbook</b> and script/action name <b>index</b></p>
</div>

// 修正後
<!-- application/views/scripts/guestbook/index.phtml -->
 
<p><a href="<?php echo $this->url(
    array(
        'controller' => 'guestbook',
        'action'     => 'sign'
    ),
    'default',
    true) ?>">Sign Our Guestbook</a></p>
 
Guestbook Entries: <br />
<dl>
    <?php foreach ($this->entries as $entry): ?>
    <dt><?php echo $this->escape($entry->email) ?></dt>
    <dd><?php echo $this->escape($entry->comment) ?></dd>
    <?php endforeach ?>
</dl>

テストコードは以下のとおりです(修正はしていません)。

<?php

class GuestbookControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{

    public function setUp()
    {
        $this->bootstrap = new Zend_Application(APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini');
        parent::setUp();
    }

    public function testIndexAction()
    {
        $params = array('action' => 'index', 'controller' => 'Guestbook', 'module' => 'default');
        $urlParams = $this->urlizeOptions($params);
        $url = $this->url($urlParams);
        $this->dispatch($url);
        
        // assertions
        $this->assertModule($urlParams['module']);
        $this->assertController($urlParams['controller']);
        $this->assertAction($urlParams['action']);
        $this->assertQueryContentContains(
            'div#view-content p',
            'View script for controller <b>' . $params['controller'] . '</b> and script/action name <b>' . $params['action'] . '</b>'
            );
    }
}

自分の場合、http://localhost/zend/Guestbook/にアクセスします。insertしたテストデータが表示されれば成功です(urlのため、.htaccessのRewriteBaseを書き換え忘れでちょっとはまりました・・・)。

参考

*1:'mapper' == $name) || !method_exists($this, $method

*2:'mapper' == $name) || !method_exists($this, $method