ZendFramework2でServiceLocatorを使わずにDBアクセス


今のプロジェクトではphp&ZendFramework2を使っています。
だけど、Controllerに全ての処理が書いてあるため、以下の3つにクラス分けを検討

  • Controllerクラス
  • Serviceクラス(ビジネスロジック処理)
  • Modelクラス(DAO)

Zend3になるとControllerクラスでServiceLocator($this->getServiceLocator())が使えなくなるようなのでどうやってDBのAdapterを渡そうか悩んだ&調べた結果以下のようにしてみました。

  1. Serviceクラスの生成時にDB Adapter&Modelを注入
  2. Conrollerの生成時にServiceを注入

ModelクラスはZendのModelを使用
http://qiita.com/ngyuki/items/19143bc00187c74389db を参考に作成

Test.php
<?php

namespace Application\Model;

class Test
{
    public $id
    public $name;

    public function exchangeArray($data) {
        $this->id   = (isset($data['id']))   ? $data['id']   : null;
        $this->name = (isset($data['name'])) ? $data['name'] : null;
    }

}
TestTable.php
<?php

namespace Application\Model;

use Zend\Db\TableGateway\TableGateway;
use Zend\Db\Adapter\AdapterAwareInterface;
use Zend\Db\Adapter\Adapter;

class TestTable implements AdapterAwareInterface
{
    protected $tableGateway;

    /**
     * {@inheritDoc}
     * @see \Zend\Db\Adapter\AdapterAwareInterface::setDbAdapter()
     */
    public function setDbAdapter(Adapter $adapter) {
        $resultSetPrototype = new ResultSet();
        $resultSetPrototype->setArrayObjectPrototype(new Test());
        $this->tableGateway = new TableGateway('test', $adapter, null, $resultSetPrototype);
    }

    public function insert(array $data) {
        $record = array(
            'id'   => $data['id'],
            'name' => $data['name'],
        );
        // データ挿入
        $this->tableGateway->insert($record);
    }

}
AdapterInitializer.php
<?php

namespace Application\Db\Adapter;

use Zend\ServiceManager\InitializerInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Db\Adapter\AdapterAwareInterface;

class AdapterInitializer implements InitializerInterface
{
    /**
     * {@inheritDoc}
     * @see \Zend\ServiceManager\InitializerInterface::initialize()
     */
    public function initialize($instance, ServiceLocatorInterface $serviceLocator) {
        if ($instance instanceof AdapterAwareInterface) {
            $instance->setDbAdapter($serviceLocator->get('Zend\Db\Adapter\Adapter'));
        }
    }

}

Serviceクラスは以下のようにしてDB AdapterとModelインスタンスを持つ。
ModelにもDB Adapter持つのでなんか冗長な感じがしてますが、トランザクション張る用に保持。
あと、この書き方だと複数のテーブルにアクセスすることになるとそれだけの数のModelをコンストラクタの引数で渡すことになるので引数がどんどん増える。。

TestService.php
<?php

namespace Application\Service;

use Application\Model\TestTable;
use Zend\Db\Adapter\Adapter;

class TestService {

    protected $adapter;

    protected $testTable;

    public function __construct(Adapter $adapter, TestTable $testTable) {
        $this->adapter = $adapter;
        $this->testTable = $testTable;
    }

    public function xxx() {
        // ビジネスロジック処理
    }
}

最後にModule.phpで注入の設定方法
getServiceConfig()でServiceクラスやModelクラスの注入を実施。
getControllerConfig()でControllerクラスの注入を実施。

Module.php
public function getServiceConfig() {
    return array(
        'factories' => array(
            // TestService生成時にDB AdapterとTestテーブルにアクセスするためのModelをDI
            'Application\Service\TestService' => function ($sm) {
                $adapter = $sm->get('Zend\Db\Adapter\Adapter');
                $testTable = $sm->get('Application\Model\TestTable');
                return new \Application\Service\TestService($adapter, $testTable);
            }
        }
        'invokables' => array(
            'Application\Model\TestTable' => 'Application\Model\TestTable',
        ),
        'initializers' => array(
            'Application\Db\Adapter\AdapterInitializer',
        ),
    );
}

public function getControllerConfig() {
    return array(
        'factories' => array(
       // TestController生成時にTestServiceをDI
            'Application\Controller\Test' => function ($sm) {
                $testService = $sm->getServiceLocator()->get('Application\Service\TestService');
                return new \Application\Controller\TestController($testService);
            },
        )
    );
}
TestController.php
<?php

namespace Application\Controller;

use Application\Service\TestService;

class TestController extends AbstractActionController {

    protected $testService;

    public function __construct(TestService $testService) {
        $this->testService = $testService;
    }

    pubclic function xxxAction() {
        // 処理
        $this->testService->xxx();
    }
}