/ZFSDomainModel

Alternative DB implementation for Zend Framework 2

Primary LanguagePHPMIT LicenseMIT

zfs-domain-model

Альтернативная реализация слоя доступа к реляционным БД на основе Zend\Db для ZF2/ZFStarter проектов.

Подключение

  • Добавляем в конфиг фабрику сервиса автоматического создания Gateway'ев:
'service_manager' => array(
    'factories' => array(
        'ZFS\DomainModel\Service' => 'ZFS\DomainModel\Service\Factory'
    )
)
  • Добавляем алиас на дефолтное подключение к БД:
'service_manager' => array(
    'aliases' => array(
        // допустим, у нас есть подключение к БД как 'Db\Adapter'
        // чтоб ZFS\DomainModel могла его использовать по-умолчанию, делаем алиас:
        'ZFS\DomainModel\Adapter' => 'Db\Adapter'
    )
)

Использование

Единственное необходимые действия:

  • Создать абстрактную фабрику для создания Gateway'ев:
<?php
namespace Application\Model\Gateway;

use ZFS\DomainModel\Gateway\AbstractFactory as BaseAbstractFactory;

class AbstractFactory extends BaseAbstractFactory
{
    protected $provides = array(
        'UsersGateway' => array(
            'tableName' => 'users'
        )
    );
}
  • И добавить эту фабрику в настройки ServiceManager'а:
'service_manager' => array(
    'abstract_factories' => array(
        'Application\Model\Gateway\AbstractFactory'
    )
)

Теперь мы можем получить доступ к Gateway через сервис локатор (на примере контроллера):

/** @var \ZFS\DomainModel\Gateway\TableGateway $gateway */
$gateway = $this->getServiceLocator()->get('UsersGateway');

Библиотека создала ZFS\DomainModel\Gateway\TableGateway, подключенный к таблице 'users', использующую ZFS\DomainModel\ResultSet\ResultSet как прототип результата операции select и ZFS\DomainModel\Object\ObjectMagic как прототип абстракции строчки таблицы. Последний содержит магические __get и __set для доступа к данным строчки.

Расширенное использование

Абстрактная фабрика для каждого Gateway может определить множество опций, определенных в ZFS\DomainModel\Service\Options:

const OPTION_TABLE_GATEWAY        = 'tableGateway';         // имя класса, создается через new
const OPTION_TABLE_NAME           = 'tableName';            // имя таблицы, единственное обязательное поле
const OPTION_ADAPTER              = 'adapter';              // имя, доступное через ServiceLocator
const OPTION_TABLE_FEATURES       = 'tableFeatures';        // массив из обьектов ***Feature
const OPTION_RESULT_SET_PROTOTYPE = 'resultSetPrototype';   // имя класса, создается через new
const OPTION_OBJECT_PROTOTYPE     = 'objectPrototype';      // имя класса, создается через new
const OPTION_SQL                  = 'sql';                  // имя класса, создается через new

Каждый класс должен наследоваться от соответствующего класса из ZFS\DomainModel OPTION_OBJECT_PROTOTYPE должен как минимум реализовывать ZFS\DomainModel\Object\ObjectInterface. Помимо интерфейса, в распоряжении программиста есть ZFS\DomainModel\Object\Object и ZFS\DomainModel\Object\ObjectMagic. Первый будет полезен для любителей писать геттеры и сеттеры через методы:

<?php
namespace Application\Model\Object;

use ZFS\DomainModel\Object\Object;

class User extends Object
{
    protected $primaryColumns = array(
        'id'
    );

    public function getId()
    {
        return $this->data['id']; // напрямую из массива данных
    }

    public function getName()
    {
        return $this->get('name'); // использует настройку $fieldToColumnMap, а так же возвращает null если isset($this->data[$name]) == false 
    }
}

Второй более минималистичен. Испольузет __get и __set для доступа к полям, внутри которых используется $this->get() (из примера выше) и $this->set():

<?php
namespace Application\Model\Object;

use ZFS\DomainModel\Object\ObjectMagic;

/**
 * @property int    id
 * @property string name
 */
class User extends ObjectMagic
{
    protected $primaryColumns = array(
        'id'
    );
}

PHPDoc @property желательно использовать как минимум для автокомплита.

В помощь camelCase стилю программирования и underscore именования колонок в БД можно использовать параметр $fieldToColumnMap:

<?php
namespace Application\Model\Object;

use ZFS\DomainModel\Object\ObjectMagic;

/**
 * @property int    id
 * @property string name
 * @property string dateOfBirth
 */
class User extends ObjectMagic
{
    protected $primaryColumns = array(
        'id'
    );

    protected $fieldToColumnMap = array(
        'dateOfBirth' => 'date_of_birth'
    );
}

Отсеять лишние поля в массиве $data (полезно для ObjectMagic), которых нет в исходной таблице, можно использовать ZFS\DomainModel\Feature\FilterColumnsFeature, просто добавить ее в описание Gateway:

<?php
namespace Application\Model\Gateway;

use ZFS\DomainModel\Feature\FilterColumnsFeature;
use ZFS\DomainModel\Gateway\AbstractFactory as BaseAbstractFactory;
use ZFS\DomainModel\Service\Options;

class AbstractFactory extends BaseAbstractFactory
{
    public function __construct()
    {
        $this->provides['UsersGateway'] = array(
            Options::OPTION_TABLE_NAME       => 'users',
            Options::OPTION_TABLE_FEATURES   => array(new FilterColumnsFeature()),
            Options::OPTION_OBJECT_PROTOTYPE => 'Application\Model\Object\User'
        );
    }
}

Здесь, как видно, ключи опций используются из Options, определение перенесено в конструктор из-за необходимости создать инстанс FilterColumnsFeature и используется Application\Model\Object\User из примера выше.

Лицензия

MIT