icomefromthenet / dbal-gateway
Doctrine DBAL的表网关
Requires
- php: ^7.1
- ext-pdo: *
- doctrine/collections: ^1.5
- doctrine/dbal: ^2.8
- monolog/monolog: ^1.2
- symfony/event-dispatcher: ^2.8
Requires (Dev)
- phpunit/dbunit: ^4.0
- phpunit/phpunit: ^7
This package is not auto-updated.
Last update: 2024-09-14 13:09:58 UTC
README
DBALGateway - Doctrine DBAL的表网关。
Doctrine DBAL 是 PDO 的一个优秀扩展,但编写 CRUD 代码仍然是一个耗时的过程。此组件在 Doctrine DBAL 上实现了 Table Gateway,并深受 zf2 Table Gateway 的启发。
有什么好处?
- 使用元数据,网关将转换值,例如将 DateTime 转换为戳记,并将戳记转换回 DateTime。
- 事件系统,例如 pre_select、post_delete、pre_insert 等,基于 symfony2 事件调度器。
- 使用 Monolog 记录查询日志,你通常会自己编写这个。
- 构建器可以将记录映射到实体,你可以在网关之上构建一个活动记录。
- 提供集合类,并将其加载到其中。
- 用于运行选择、插入、更新和删除的流畅接口。
- 比手动 CRUD 更快。
有什么缺点?
- 在 IDE 中自动完成较为宽松,对于子类,只有基类才有。
- 开销稍微多一点内存和额外的方法调用。
安装
此组件可以通过 composer 安装。
{ "require" : { "icomefromthenet/dbal-gateway" : "dev-master", } }
用法。
每个表有三个组件。
- 元数据 instanceof
DBALGateway\Metadata\Table
。 - 网关是
DBALGateway\Table\AbstractTable
的子类。 - 查询类是
DBALGateway\Query\AbstractQuery
的子类。
每个表有两个可选组件
- 自定义结果集实现
Doctrine\Common\Collections\Collection
。 - 实体构建器实现
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; } }
每个自定义过滤器应执行以下操作。
- 设置一个唯一的命名参数。
- 设置参数值并从表网关的元数据中获取 doctrine 类型。
- 返回 $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'); }
实例化一个网关吗?
网关有以下依赖。
- 模式中的表名。
Doctrine\DBAL\Connection
$connection。- 一个实现
Symfony\Component\EventDispatcher\EventDispatcherInterface
的实例。 - 表的实例元数据,实现
DBALGateway\Metadata\Table
。 - (可选) 一个结果集,用于克隆实现
Doctrine\Common\Collections\Collection
的实例类。 - (可选) 一个实体构建器,实现
DBALGateway\Builder\BuilderInterface
的实例。
new MockUserGateway('users',$conn,$event,$meta,$result_set,$builder);
特性和事件。
网关会触发多个事件。
例如,请参阅 BufferedQueryLogger。