t4web/infrastructure

DDD 的基础设施层实现。

2.0.1 2018-03-03 08:22 UTC

README

主分支: 构建状态 codecov.io Scrutinizer 代码质量 SensioLabsInsight 依赖状态

领域基础设施,由t4web\domain-interface实现

内容

安装

将此项目添加到您的 composer.json 中

"require": {
    "t4web/infrastructure": "~1.3.0"
}

现在运行以下命令告诉 composer 下载 Domain

$ php composer.phar update

快速开始

您可以使用具有 Domain 实现的 Repository t4web\domain。此实现基于 Zend\DbZend\EventManager

组件

  • Criteria - 用于创建获取表达式

    $criteria = new T4webInfrastructure\Criteria('Task');
    $criteria->equalTo('id', 2);
    $criteria->in('type', [1,2,3]);
    $criteria->limit(20);
    $criteria->offset(10);
    $criteria->relation('Photos')
        ->equalTo('status', 3)
        ->greaterThan('created_dt', '2015-10-30');
  • CriteriaFactory - 用于从数组创建复杂标准

    $criteriaFactory = new T4webInfrastructure\CriteriaFactory();
    $criteria = $criteriaFactory->build(
        'Task',
        [
            'status.equalTo' => 2,
            'dateCreate.greaterThan' => '2015-10-30',
    
            'relations' => [
                'User' => [
                    'status.in' => [2, 3, 4],
                    'name.like' => 'gor'
                ]
            ]
        ]
    );
  • Mapper - 用于将 Entity 转换为表行(数组),并根据 columnsAsAttributesMap 过滤表行

    $columnsAsAttributesMap = [
        'id' => 'id',
        'project_id' => 'projectId',
        'name' => 'name',
        'assignee_id' => 'assigneeId',
        'status' => 'status',
        'type' => 'type',
    ];
    $tableRow = [
        'id' => 22,
        'project_id' => 33,
        'name' => 'Some name',
        'assignee_id' => 44,
        'status' => 2,
        'type' => 1,
    ];
    $mapper = new T4webInfrastructure\Mapper($columnsAsAttributesMap);
    $filteredTableRow = $mapper->fromTableRow($tableRow);
    $tableRow = $mapper->toTableRow($entity);
  • QueryBuilder - 用于构建 SQL 查询

    $queryBuilder = new T4webInfrastructure\QueryBuilder();
    
    $criteria = new T4webInfrastructure\Criteria('Task');
    $criteria->equalTo('id', 2);
    $criteria->relation('Photos')
        ->equalTo('status', 3);
        
    /** @var Zend\Db\Sql\Select $select */
    $select = $queryBuilder->getSelect($criteria);
    
    $tableGateway = new Zend\Db\TableGateway\TableGateway('tasks', $dbAdapter);
    $rows = $this->tableGateway->selectWith($select);
    
    $sql = $select->getSqlString($this->dbAdapter->getPlatform());
    // $sql = SELECT `tasks`.*
    //        FROM `tasks`
    //        INNER JOIN `photos` ON `photos`.`task_id` = `tasks`.`id`
    //        WHERE `tasks`.id = 2 AND `photos`.`status` = 3
  • Repository - 用于存储实体并从存储中恢复

    $repository = $serviceLocator->get('Task\Infrastructure\Repository');
    /** @var Tasks\Task\Task $task */
    $task = $repository->findById(123);
    
    $repository = $serviceLocator->get('Task\Infrastructure\FinderAggregateRepository');
    $task = $repository->findWith('User')->findById(123);
    /** @var Users\User\User $assignee */
    $assignee = $task->getAssignee();

从数组构建构建标准

您可以使用 CriteriaFactory::build() 从数组构建标准(例如:从输入过滤器、post\get 请求)

$inputData = $_GET;

$criteriaFactory = new T4webInfrastructure\CriteriaFactory();
$criteria = $criteriaFactory->build(
    'Task',
    $inputData
);

$inputData 必须具有以下结构

$inputData = [
     'status.equalTo' => 2,
     'dateCreate.greaterThan' => '2015-10-30',
     // ...
     'ATTRIBUTE.METHOD' => VALUE
 ]

其中 ATTRIBUTE - 标准字段,METHOD - equalTonotEqualTolessThangreaterThangreaterThanOrEqualTolessThanOrEqualTolikein 之一

对于 isNullisNotNull 使用

$inputData = [
  'ATTRIBUTE.isNull' => TRUE_EXPRESSION,
  'ATTRIBUTE.isNotNull' => TRUE_EXPRESSION,
  
  // example
  'status.isNull' => true,
  'dateCreate.isNotNull' => 1,
]

其中 TRUE_EXPRESSION 可以是任何真表达式:true1'a' 等。

对于 between 使用数组作为值

$inputData = [
   'ATTRIBUTE.between' => [MIN_VALUE, MAX_VALUE],
   
   // example
   'dateCreate.between' => ['2015-10-01', '2015-11-01'],
]

对于 limitoffset 使用

$inputData = [
   'limit' => VALUE,
   'offset' => VALUE,
   
   // example
   'limit' => 20,
   'offset' => 10,
]

对于 order 使用类似 SQL 的排序表达式

$inputData = [
   'order' => EXPRESSION,
   
   // example
   'order' => 'dateCreate DESC',
   'order' => 'dateCreate DESC, status ASC',
]

自定义标准 - 分组和重用标准

$inputData = [
    'Users\User\Criteria\Active' => true,
]

Users\User\Criteria\Active - 必须是可调用的类(__invoke(CriteriaInterface $criteria, $value)

配置

对于配置 Repository,您必须指定配置,并使用 Config 对象来解析配置。QueryBuilder 使用 Config 来构建 SQL 查询。

$entityMapConfig = [
    // Entity name
    'Task' => [
        
        // table name
        'table' => 'tasks',

        // use for short namespace
        'entityClass' => 'Tasks\Task\Task',
        
        // map for entity attribute <=> table fields
        'columnsAsAttributesMap' => [
            
            // attribute => table field
            'id' => 'id',
            'project_id' => 'projectId',
            'name' => 'name',
            'assignee_id' => 'assigneeId',
            'status' => 'status',
            'type' => 'type',
            'extras' => 'extras',
        ],
        
        // foreign relation
        'relations' => [
        
            // relation entity name + table.field for building JOIN
            // name => [FK in cur. entity, PK in related entity]
            'User' => ['tasks.assignee_id', 'user.id'],
            
            // relation entity name + table.field for building JOIN
            // name => [link-tabe, link-table field for cur. entity, link-table field for related entity]
            'Tag' => ['tasks_tags_link', 'task_id', 'tag_id'],
        ],

        // for aliasing long\ugly criterias
        'criteriaMap' => [
            // alias => criteria
            'date_more' => 'dateCreate.greaterThan',
        ],
        
        // for serializing persisting data 
        'serializedColumns' => [
            'extras' => 'json',
        ],
    ],
]

relations 参数顺序 - 非常重要,Task['relations']['User'][0] - 必须是当前实体的字段,Task['relations']['User'][1] - 必须是相关实体的字段。

对于多对多关系的相关实体必须包含3个参数。例如,我们有表tasks(见配置),表tags包含字段idname和表tasks_tags_link包含字段task_idtag_id。对于关系 Task <=> Tag,我们必须描述多对多关系:Task['relations']['Tag'][0] - 链接表名称,Task['relations']['Tag'][1] - 当前实体在链接表中的主键字段,以及Task['relations']['Tag'][2] - 链接表中相关实体的主键字段。

事件

Repository在实体创建或更新时触发事件。

$eventManager = new EventManager();
$eventManager->getSharedManager()->attach(
     REPOSITORY_IDENTIFIER,
     'entity:ENTITY_CLASS:changed',
     function(T4webInfrastructure\Event\EntityChangedEvent $e){
        $changedEntity = $e->getChangedEntity();
        $originalEntity = $e->getOriginalEntity();
        // ...
     },
     $priority
);

其中REPOSITORY_IDENTIFIER - 根据实体确定,构建:EntityName\Infrastructure\Repository
ENTITY_CLASS - 从你的$enity对象调用get_class()

现在Repository可以触发

  • entity:ModuleName\EntityName\EntityName:created - 在实体刚在数据库中创建后触发。在上下文中,事件订阅者接收Zend\EventManager\Event。
  • entity:ModuleName\EntityName\EntityName:changed:pre - 在实体更新数据库之前触发。在上下文中,事件订阅者接收T4webInfrastructure\Event\EntityChangedEvent。
  • entity:ModuleName\EntityName\EntityName:changed - 在实体刚在数据库中更新后触发。在上下文中,事件订阅者接收T4webInfrastructure\Event\EntityChangedEvent。
  • attribute:ModuleName\EntityName\EntityName:attribute:changed - 在实体属性更新数据库后触发。在上下文中,事件订阅者接收T4webInfrastructure\Event\EntityChangedEvent。