vladshut/dbal-gateway

Doctrine DBAL的表网关

v1.2.0 2018-11-23 11:09 UTC

This package is auto-updated.

Last update: 2024-09-11 18:00:45 UTC


README

Build Status

Doctrine DBAL是PDO的一个优秀扩展,但编写简单的1到1映射的CRUD代码仍然是一个耗时的工作。该组件在Doctrine DBAL之上实现了表网关,并受到了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",
    }

}

用法。

每个表都有3个组件。

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

每个表都有2个可选组件

  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 types,映射可以在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