ZF2 の AdapterAwareInterface で DbAdapter を注入

## Before

Zend Framework 2 のチュートリアルを進めていくと、AlbumTable クラスのコンストラクタで TableGateway を引数に取り、Module の ServiceConfig で ファクトリを使って AlbumTable に TableGateway を注入することになります。

 - [Database and models — Zend Framework 2 2.3.0 documentation - Zend Framework](http://framework.zend.com/manual/2.3/en/user-guide/database-and-models.html "Database and models — Zend Framework 2 2.3.0 documentation - Zend Framework")

*module/Album/src/Album/Model/AlbumTable.php*

```php
<?php
namespace Album\Model;

use Zend\Db\TableGateway\TableGateway;

class AlbumTable
{
    protected $tableGateway;

    public function __construct(TableGateway $tableGateway)
    {
        $this->tableGateway = $tableGateway;
    }

    // (snip)
}
```

*module/Album/Module.php*

```php
<?php
namespace Album;

use Album\Model\Album;
use Album\Model\AlbumTable;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\TableGateway\TableGateway;

class Module
{
    // (snip)

    public function getServiceConfig()
    {
        return array(
            'factories' => array(
                'Album\Model\AlbumTable' =>  function($sm) {
                    $tableGateway = $sm->get('AlbumTableGateway');
                    $table = new AlbumTable($tableGateway);
                    return $table;
                },
                'AlbumTableGateway' => function ($sm) {
                    $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                    $resultSetPrototype = new ResultSet();
                    $resultSetPrototype->setArrayObjectPrototype(new Album());
                    return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
                },
            ),
        );
    }
}
```

## Use initializer by closure

AlbumTableGateway がサービスとして定義されている意義をあまり感じないので、サービスのイニシャライザで AlbumTable に DbAdapter を直接注入し、TableGateway は AlbumTable の中で作るようにします。

イニシャライザについての説明は下記がわかりやすいです。

 - [Zend\ServiceManager その2 » のせのBlog][ref1]

AlbumTable に AdapterAwareInterface をインプリメントして AdapterAwareInterface::setDbAdapter の実装に Module の getServiceConfig に書いていた処理をごっそり移します。

また、AlbumTable のコンストラクタはもう不用なので削除します。

*module/Album/src/Album/Model/AlbumTable.php*

```php
<?php
namespace Album\Model;

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

class AlbumTable implements AdapterAwareInterface
{
    protected $tableGateway;

    public function setDbAdapter(Adapter $adapter)
    {
        $resultSetPrototype = new ResultSet();
        $resultSetPrototype->setArrayObjectPrototype(new Album());
        $this->tableGateway = new TableGateway('album', $adapter, null, $resultSetPrototype);
    }

    // (snip)
}
```

Module の getServiceConfig で initializers と AlbumTable を定義します。

AlbumTable にはもう特別なファクトリは必要ないので invokables で定義します。

initializers はサービスのインスタンス化の際に自動的に呼ばれる処理です。

この例ではサービスのインスタンスが AdapterAwareInterface をインプリメントしているかどうかを調べ、インプリメントしているなら DbAdapter をサービスマネージャから取り出してセットします。

*module/Album/Module.php*

```php
<?php
namespace Album;

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

class Module
{
    // (snip)

    public function getServiceConfig()
    {
        return array(
            'invokables' => array(
                'Album\Model\AlbumTable' => 'Album\Model\AlbumTable',
            ),
            'initializers' => array(
                function ($instance, ServiceLocatorInterface $sm) {
                    if ($instance instanceof AdapterAwareInterface)
                    {
                        $instance->setDbAdapter($sm->get('Zend\Db\Adapter\Adapter'));
                    }
                }
            ),
        );
    }
}
```

## implement InitializerInterface

上の例ではイニシャライザをクロージャーで実装しましたが、InitializerInterface をインプリメントしたクラスでも可能です。

次のように InitializerInterface を実装したクラスを作ります。やっていることは上の例のクロージャーと同じです。

*module/Album/src/Album/Db/Adapter/AdapterInitializer.php*

```php
<?php
namespace Album\Db\Adapter;

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

class AdapterInitializer implements InitializerInterface
{
    public function initialize($instance, ServiceLocatorInterface $serviceLocator)
    {
        if ($instance instanceof AdapterAwareInterface)
        {
            $instance->setDbAdapter($serviceLocator->get('Zend\Db\Adapter\Adapter'));
        }
    }
}
```

Module の getServiceConfig の initializers でイニシャライザのクラスを指定します。

*module/Album/Module.php*

```php
<?php
namespace Album;

class Module
{
    // (snip)

    public function getServiceConfig()
    {
        return array(
            'invokables' => array(
                'Album\Model\AlbumTable' => 'Album\Model\AlbumTable',
            ),
            'initializers' => array(
                'Album\Db\Adapter\AdapterInitializer',
            ),
        );
    }
}
```

getServiceConfig の中身がかなりスッキリしました。

## 参考

 - [Zend\ServiceManager その2 » のせのBlog][ref1]

[ref1]: http://www.nosenaoki.net/2013/01/08/zend-servicemanager2/ "Zend\ServiceManager その2 » のせのBlog"