symlex / doctrine-active-record
面向对象的Doctrine DBAL CRUD
Requires
- php: >=7.3
- ext-json: *
- doctrine/dbal: ^2.8
Requires (Dev)
- lastzero/test-tools: ^4.2
This package is auto-updated.
Last update: 2024-09-07 12:40:51 UTC
README
作为Doctrine ORM的轻量级替代品,这个经过实战检验的库提供了封装了Doctrine DBAL的商务模型和数据库访问对象(DAO)类,以提供高性能的面向对象的CRUD(创建、读取、更新、删除)功能。它比Datamapper ORM实现更快、更简单。请参阅TRADEOFFS.md。
文档:https://docs.symlex.org/en/latest/doctrine-active-record/
基本示例
<?php use Doctrine\ActiveRecord\Dao\Factory as DaoFactory; use Doctrine\ActiveRecord\Model\Factory; $daoFactory = new DaoFactory($db); $modelFactory = new Factory($daoFactory); $modelFactory->setFactoryNamespace('App\Model'); $modelFactory->setFactoryPostfix('Model'); // Returns instance of App\Model\UserModel $user = $modelFactory->create('User'); // Throws exception, if not found $user->find(123); if ($user->email == '') { // Update email $user->update(array('email' => 'user@example.com')); } // Returns instance of App\Model\GroupModel $group = $user->createModel('Group');
在REST控制器上下文中使用
Doctrine ActiveRecord非常适合构建高性能REST服务。
此示例演示了如何在REST控制器上下文中使用EntityModel。注意,如何轻松避免深层嵌套结构。用户模型和表单工厂(由InputValidation包提供)作为依赖项注入。
<?php namespace App\Controller\Rest; use Symfony\Component\HttpFoundation\Request; use App\Exception\FormInvalidException; use App\Form\FormFactory; use App\Model\User; class UsersController { protected $user; protected $formFactory; public function __construct(User $user, FormFactory $formFactory) { $this->user = $user; $this->formFactory = $formFactory; } public function cgetAction(Request $request) { $options = array( 'count' => $request->query->get('count', 50), 'offset' => $request->query->get('offset', 0) ); return $this->user->search(array(), $options); } public function getAction($id) { return $this->user->find($id)->getValues(); } public function deleteAction($id) { return $this->user->find($id)->delete(); } public function putAction($id, Request $request) { $this->user->find($id); $form = $this->formFactory->create('User\Edit'); $form ->setDefinedWritableValues($request->request->all()) ->validate(); if($form->hasErrors()) { throw new FormInvalidException($form->getFirstError()); } $this->user->update($form->getValues()); return $this->user->getValues(); } public function postAction(Request $request) { $form = $this->formFactory->create('User\Create'); $form ->setDefinedWritableValues($request->request->all()) ->validate(); if($form->hasErrors()) { throw new FormInvalidException($form->getFirstError()); } $this->user->save($form->getValues()); return $this->user->getValues(); } }
另请参阅:InputValidation for PHP – 用于任何来源输入数据的简单且安全的白名单验证
数据访问对象(DAO)
DAO直接与数据库表打交道,如果需要,也可以处理原始SQL。 Doctrine\ActiveRecord\Dao\Dao
适用于使用原始SQL实现自定义方法。所有DAO默认公开以下公共方法
createDao(string $name)
: 返回一个新的DAO实例beginTransaction()
: 开始数据库事务commit()
: 提交数据库事务rollBack()
: 回滚数据库事务
此外,Doctrine\ActiveRecord\Dao\EntityDao
还提供了许多强大的方法,可以轻松处理数据库表行
setData(array $data)
: 设置原始数据(例如在调用更新()时无法检测更改)setValues(array $data)
: 设置多个值setDefinedValues(array $data)
: 设置只存在于表模式中的值(比setValues()慢)getValues()
: 以数组形式返回所有值find($id)
: 通过主键查找一行reload()
: 从数据库重新加载行值getValues()
: 以关联数组形式返回所有值exists($id)
: 如果存在具有给定主键的行,则返回truesave()
: 插入新行update()
: 更新数据库中的更改值delete()
: 从数据库删除实体getId()
: 返回当前加载记录的ID(如果为空则抛出异常)hasId()
: 如果DAO实例已分配ID(主键),则返回truesetId($id)
: 设置主键findAll(array $cond = array(), $wrapResult = true)
: 返回匹配$cond的所有实例(如果想要限制或排序结果集,请使用search()或searchAll())search(array $params)
: 返回一个SearchResult
对象(有关支持的参数请参见下文)wrapAll(array $rows)
: 为每个数组元素创建并返回一个新的DAOupdateRelationTable(string $relationTable, string $primaryKeyName, string $foreignKeyName, array $existing, array $updated)
: 用于更新多对多关系表的辅助函数hasTimestampEnabled()
: 如果此DAO在创建和更新行时自动添加时间戳,则返回truefindList(string $colName, string $order = '', string $where = '', string $indexName = '')
:返回所有匹配行的键/值数组(列表)getTableName()
:返回底层数据库表名getPrimaryKeyName()
:返回主键列名(如果主键是数组,则抛出异常)
搜索参数
search()接受以下可选参数以限制、过滤和排序搜索结果
table
:表名
table_alias
:"table"的别名(用于连接和left_join的表引用)
cond
:搜索条件作为数组(键/值或仅对原始SQL的值)
count
:最大结果数(整数)
offset
:结果偏移量(整数)
join
:要连接的表列表,包括连接条件,例如array(array('u', 'phonenumbers', 'p', 'u.id = p.user_id'))
,参见Doctrine DBAL手册
left_join
:见join
columns
:列列表(数组)
order
:排序顺序(如果不为false)
group
:按组(如果不为false)
wrap
:如果为false,则返回原始数组而不是DAO实例
ids_only
:仅返回主键值
sql_filter
:原始SQL过滤器(WHERE)
id_filter
:如果不为空,则将结果限制到此主键ID列表
搜索结果
在调用search()
的EntityDao
或EntityModel
时,您将获得返回值SearchResult
实例。它实现了ArrayAccess
、Serializable
、IteratorAggregate
和Countable
,可以作为数组或对象使用,具有以下方法
getAsArray()
:以数组形式返回搜索结果
getSortOrder()
:返回排序顺序字符串
getSearchCount()
:返回搜索计数(限制)整数
getSearchOffset()
:返回搜索偏移量整数
getResultCount()
:返回实际查询结果数(<=限制)
getTotalCount()
:返回总结果计数(在数据库中)
getAllResults()
:返回所有结果作为EntityDao
或EntityModel
实例的数组
getAllResultsAsArray()
:以嵌套数组形式返回所有结果(例如,将其序列化为JSON)
getFirstResult()
:返回第一个结果EntityDao
或EntityModel
实例,或抛出异常
实体配置
DAO实体使用受保护的类属性进行配置
<?php protected $_tableName = ''; // Database table name protected $_primaryKey = 'id'; // Name or array of primary key(s) protected $_fieldMap = array(); // 'db_column' => 'object_property' protected $_hiddenFields = array(); // Fields that should be hidden for getValues(), e.g. 'password' protected $_formatMap = array(); // 'db_column' => Format::TYPE protected $_valueMap = array(); // 'object_property' => 'db_column' protected $_timestampEnabled = false; // Automatically update timestamps? protected $_timestampCreatedCol = 'created'; protected $_timestampUpdatedCol = 'updated';
<$_formatMap>的可能值在Doctrine\ActiveRecord\Dao\Format
中定义为常量
<?php const NONE = ''; const INT = 'int'; const FLOAT = 'float'; const STRING = 'string'; const ALPHANUMERIC = 'alphanumeric'; const SERIALIZED = 'serialized'; const JSON = 'json'; const CSV = 'csv'; const BOOL = 'bool'; const TIME = 'H:i:s'; const TIMEU = 'H:i:s.u'; // Support for microseconds (up to six digits) const TIMETZ = 'H:i:sO'; // Support for timezone (e.g. "+0230") const TIMEUTZ = 'H:i:s.uO'; // Support for microseconds & timezone const DATE = 'Y-m-d'; const DATETIME = 'Y-m-d H:i:s'; const DATETIMEU = 'Y-m-d H:i:s.u'; // Support for microseconds (up to six digits) const DATETIMETZ = 'Y-m-d H:i:sO'; // Support for timezone (e.g. "+0230") const DATETIMEUTZ = 'Y-m-d H:i:s.uO'; // Support for microseconds & timezone const TIMESTAMP = 'U';
示例
<?php namespace App\Dao; use Doctrine\ActiveRecord\Dao\EntityDao; class UserDao extends EntityDao { protected $_tableName = 'users'; protected $_primaryKey = 'user_id'; protected $_timestampEnabled = true; }
业务模型
业务模型在逻辑上位于控制器和数据访问对象(DAO)之间,控制器负责渲染视图和验证用户输入,而DAO是低级接口,用于与存储后端或Web服务进行交互。
模型的公共接口是高级的,应该反映其领域内的所有用例。在基类Doctrine\ActiveRecord\Model\EntityModel
中预实现了许多标准用例
createModel(string $name = '', Dao $dao = null)
:创建新的模型实例
find($id)
:根据主键查找记录
reload()
:从数据库重新加载值
findAll(array $cond = array(), $wrapResult = true)
:查找多个记录;如果$wrapResult
为false,则返回原始DAO而不是模型实例
search(array $cond, array $options = array())
:返回一个SearchResult
对象($options可以包含计数、偏移量、排序顺序等,参见上面DAO文档中的search())
searchAll(array $cond = array(), $order = false)
:search()的简单版本,类似于findAll()
searchOne(array $cond = array())
:搜索单个记录;如果找到0个或多个记录,则抛出异常
searchIds(array $cond, array $options = array())
:返回匹配给定搜索条件的匹配主键ID数组
getModelName()
:返回不带前缀和后缀的模型名称
getId()
: 返回当前加载记录的ID(如果为空则抛出异常)
hasId()
: 如果模型实例分配了ID(主键),则返回true
getValues()
: 返回所有模型属性作为关联数组
getEntityTitle()
: 返回此实体的通用名称
isDeletable()
: 如果模型实例可以通过delete()方法删除,则返回true
isUpdatable()
: 如果模型实例可以通过update($values)方法更新,则返回true
isCreatable()
: 如果可以在数据库中通过create($values)方法创建新实体,则返回true
batchEdit(array $ids, array $properties)
: 更新多个记录的数据
getTableName()
: 返回相关的主要数据库表名称
hasTimestampEnabled()
: 如果为相关DAO启用了时间戳,则返回true
delete()
: 从数据库中永久删除实体记录
save(array $values)
: 使用提供的值创建新记录
update(array $values)
: 更新模型实例的数据库记录;在将多个值分配给模型实例之前,应使用表单类验证数据
模型中应实现多少验证? 在无效数据可能导致安全问题或重大不一致的地方,必须在模型层实现一些核心验证规则。模型异常消息通常不需要翻译(在多语言应用程序中),因为无效值应该由表单类预先识别。如果您期望某些异常,应在控制器中捕获和处理它们。
模型通过受保护的类属性与各自的Dao关联
protected $_daoName = ''; // DAO class name without namespace or postfix
示例
<?php namespace App\Model; use Doctrine\ActiveRecord\Model\EntityModel; class User extends EntityModel { protected $_daoName = 'User'; public function delete() { $dao = $this->getEntityDao(); $dao->is_deleted = 1; $dao->update(); } public function undelete() { $dao = $this->getEntityDao(); $dao->is_deleted = 0; $dao->update(); } public function search(array $cond, array $options = array()) { $cond['is_deleted'] = 0; return parent::search($cond, $options); } public function getValues() { $result = parent::getValues(); unset($result['password']); return $result; } }
单元测试
此库附带一个docker-compose.yml
文件和MySQL数据库测试用例,用于运行单元测试(MySQL将绑定到127.0.0.1:3308)
localhost# docker-compose up -d
localhost# docker-compose exec mysql sh
docker# cd /share/src/Tests/_fixtures
docker# mysql -u root --password=doctrine doctrine-active-record < schema.sql
docker# exit
localhost# bin/phpunit
PHPUnit 7.3.2 by Sebastian Bergmann and contributors.
................................................................. 65 / 91 ( 71%)
.......................... 91 / 91 (100%)
Time: 251 ms, Memory: 8.00MB
OK (91 tests, 249 assertions)
localhost# docker-compose down
Composer
要在项目中使用此库,只需运行composer require symlex/doctrine-active-record
或将"symlex/doctrine-active-record"添加到您的composer.json文件,然后运行composer update
{ "require": { "php": ">=7.1", "symlex/doctrine-active-record": "^4.0" } }
关于
Doctrine ActiveRecord 由Michael Mayer维护。如果您有任何问题、需要商业支持或只是想打个招呼,请随时发送电子邮件至hello@symlex.org。我们欢迎各种形式的贡献。如果您有错误或想法,在提交问题之前,请先阅读我们的指南。
注意:此库是Symlex(一个基于Symfony的敏捷Web开发框架堆栈)的一部分,而不是官方的Doctrine项目。