icomefromthenet/dbal-gateway

Doctrine DBAL的表网关

v1.1.0 2018-08-17 06:09 UTC

This package is not auto-updated.

Last update: 2024-09-14 13:09:58 UTC


README

Build Status

DBALGateway - Doctrine DBAL的表网关。

Doctrine DBAL 是 PDO 的一个优秀扩展,但编写 CRUD 代码仍然是一个耗时的过程。此组件在 Doctrine DBAL 上实现了 Table Gateway,并深受 zf2 Table Gateway 的启发。

有什么好处?

  1. 使用元数据,网关将转换值,例如将 DateTime 转换为戳记,并将戳记转换回 DateTime。
  2. 事件系统,例如 pre_select、post_delete、pre_insert 等,基于 symfony2 事件调度器。
  3. 使用 Monolog 记录查询日志,你通常会自己编写这个。
  4. 构建器可以将记录映射到实体,你可以在网关之上构建一个活动记录。
  5. 提供集合类,并将其加载到其中。
  6. 用于运行选择、插入、更新和删除的流畅接口。
  7. 比手动 CRUD 更快。

有什么缺点?

  1. 在 IDE 中自动完成较为宽松,对于子类,只有基类才有。
  2. 开销稍微多一点内存和额外的方法调用。

安装

此组件可以通过 composer 安装。

{
    "require" : {
        "icomefromthenet/dbal-gateway" : "dev-master",
    }

}

用法。

每个表有三个组件。

  1. 元数据 instanceof DBALGateway\Metadata\Table
  2. 网关是 DBALGateway\Table\AbstractTable 的子类。
  3. 查询类是 DBALGateway\Query\AbstractQuery 的子类。

每个表有两个可选组件

  1. 自定义结果集实现 Doctrine\Common\Collections\Collection
  2. 实体构建器实现 DBALGateway\Builder\BuilderInterface

1. 元数据

假设有以下数据库表。

delimiter $$

CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
  `first_name` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
  `last_name` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
  `dte_created` datetime NOT NULL,
  `dte_updated` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci$$

你可以这样声明元数据。

<?php
namespace ExampleFile

use DBALGateway\Metadata\Table;

call_user_func(function(){

    # create the table object
    $table = new Table('users');
    
    # assign normal columns 
    $table->addColumn('id',"integer", array("unsigned" => true));
    $table->addColumn('username', "string", array("length" => 32));
    $table->addColumn('first_name', "string", array("length" => 45));
    $table->addColumn('last_name',"string", array("length" => 45));
    $table->addColumn('dte_created','datetime');
    $table->addColumn('dte_updated','datetime');
    $table->setPrimaryKey(array("id"));

    # assign virtual columns, these are perfect for calulated values.
    # these columns can not be inserted but will be converted if found in a result-set.
    $table->addVirtualColumn('uptime',"datetime");
    
    return $table;    
});

数据类型(addColumn 的第二个参数)不是 mysql 类型,而是 doctrine 类型,映射可以在 Doctrine\DBAL\Platforms\{MYPLATFORM} 下找到。

2. 表网关。

你需要声明一个 DBALGateway\Metadata\Table 的子类并重写 newQueryObject() 方法。

<?php
namespace DBALGateway\Tests\Base\Mock;

use DBALGateway\Table\AbstractTable;

class MockUserTableGateway extends AbstractTable
{
    /**
      *  Create a new instance of the querybuilder
      *
      *  @access public
      *  @return QueryBuilder
      */
    public function newQueryBuilder()
    {
        return new MockUserQuery($this->adapter,$this);
    }

}

你还可以在这个类中包含其他自定义代码。但请注意,所有方法都被标记为 protected,命名时要小心。

3. 查询类

你需要将 DBALGateway\Query\AbstractQuery 作为子类,它本身是 Doctrine\DBAL\Query\QueryBuilder 的子类。

<?php
namespace DBALGateway\Tests\Base\Mock;

use DBALGateway\Query\AbstractQuery;
use DateTime;

class MockUserQuery extends AbstractQuery
{
    
    public function filterByUser($id)
    {
        $this->where('id = :id')->setParameter('id', $id, $this->getGateway()->getMetaData()->getColumn('id')->getType());
        return $this;
    }
    
    public function filterByUsername($name)
    {
        $this->where('username = :username')->setParameter('username', $id, $this->getGateway()->getMetaData()->getColumn('username')->getType());
        
        return $this;
    }
    
    
    public function filterByDateCreated(DateTime $created)
    {
        $this->where('dte_created = :dte_created')->setParameter('dte_created', $id, $this->getGateway()->getMetaData()->getColumn('dte_created')->getType());
        
        return $this;
    }
    
    public function filterByDateUpdated(DateTime $updated)
    {
        $this->where('dte_updated = :dte_updated')->setParameter('dte_updated', $id, $this->getGateway()->getMetaData()->getColumn('dte_updated')->getType());
        
        return $this;
    }
    
}

每个自定义过滤器应执行以下操作。

  1. 设置一个唯一的命名参数。
  2. 设置参数值并从表网关的元数据中获取 doctrine 类型。
  3. 返回 $this。

4. 集合类。

当在网关上使用 find() 时,结果将存储在 doctrine 提供的集合类 Doctrine\Common\Collections\ArrayCollection 中。如果网关的构造函数传递了一个实现 Doctrine\Common\Collections\Collection 接口的实例,它将克隆一个副本并使用该副本。

use Doctrine\Common\Collections\Collection;


class CustomCollection implements Collection
{
 .....

}

$collection = new CustomCollection();

$gateway = new MockUserGateway('user',$conn,$event,$meta,$collection,null);

此网关将在每次调用 find 时克隆 $collection注意: findOne() 不返回集合,只返回实体/数组。

5. 实体构建器

当使用 find()findOne() 时,找到的每个结果都将传递给构建器进行转换成实体。构建器必须实现 DBALGateway\Builder\BuilderInterface 接口。

use DBALGateway\Builder\BuilderInterface;

class EntityBuilder implements BuilderInterface
{
     /**
      *  Convert data array into entity
      *
      *  @return mixed
      *  @param array $data
      *  @access public
      */
    public function build($data)
    {
        $user = new UserEntity();
        
        $user->id = $data['id'];
        $user->username = $data['username'];
        
        ... etc
        
        return $user;
        
    }
    
    /**
      *  Convert and entity into a data array
      *
      *  @return array
      *  @access public
      */
    public function demolish($entity)
    {
    
    }

}

$builder = new EntityBuilder();
$gateway = new MockUserGateway('users',$conn,$event,$meta,null,$builder);

注意:如果使用了集合类,新实体将被添加到集合中。

运行查询

运行一个INSERT查询。

$gateway = new MockUserGateway('users',$conn,$event,$meta);

$success = $gateway->insertQuery()
             ->start()
                ->addColumn('username','ausername')
                ->addColumn('first_name','myfname')
                ->addColumn('last_name','mylname')
                ->addColumn('dte_created',new DateTime())
                ->addColumn('dte_updated',DateTime())
             ->end()
           ->insert(); 

if($success) {
 
 $id = $gateway->lastInsertId();
 
}

运行一个UPDATE查询。

$gateway = new MockUserGateway('users',$conn,$event,$meta);

$success = $gateway->updateQuery()
             ->start()
                ->addColumn('username','ausername')
                ->addColumn('first_name','myfname')
                ->addColumn('last_name','mylname')
                ->addColumn('dte_created',new DateTime())
                ->addColumn('dte_updated',DateTime())
             ->where()
                ->filterByUser(101)
             ->end()
           ->update(); 

if($success) {
 
 echo 'table row was updated';
 
}

运行一个DELETE查询。

$gateway = new MockUserGateway('users',$conn,$event,$meta);

$success = $gateway->deleteQuery()
             ->start()
                ->filterByUser(1)
             ->end()
           ->delete(); 

if($success) {
 
 echo 'table row was removed';
 
}

运行一个SELECT查询。

有两种方法 findOne()find()

$gateway = new MockUserGateway('users',$conn,$event,$meta);

$result = $gateway->selectQuery()
             ->start()
                ->filterByUser(1)
             ->end()
           ->findOne(); 

if($result !== null) {
    echo $result['id'];
    echo $result['username'];
    echo $result['dte_created']->format('U');
}
$gateway = new MockUserGateway('users',$conn,$event,$meta);

$result = $gateway->selectQuery()
             ->start()
                ->filterByUser(1)
             ->end()
           ->find(); 

if($result !== null) {
    echo $result[0]['id'];
    echo $result[0]['username'];
    echo $result[0]['dte_created']->format('U');
}

实例化一个网关吗?

网关有以下依赖。

  1. 模式中的表名。
  2. Doctrine\DBAL\Connection $connection。
  3. 一个实现 Symfony\Component\EventDispatcher\EventDispatcherInterface 的实例。
  4. 表的实例元数据,实现 DBALGateway\Metadata\Table
  5. (可选) 一个结果集,用于克隆实现 Doctrine\Common\Collections\Collection 的实例类。
  6. (可选) 一个实体构建器,实现 DBALGateway\Builder\BuilderInterface 的实例。
 new MockUserGateway('users',$conn,$event,$meta,$result_set,$builder);

特性和事件。

网关会触发多个事件。

例如,请参阅 BufferedQueryLogger