iqomp / model
通用数据库模型,用于标准化使用
Requires (Dev)
Suggests
- hyperf/translation: Allow model validator to be translated to id-ID or en-US
- iqomp/formatter: Object formatter for format type related to database
- iqomp/validator: Form validation for databases rule
README
另一种以风格管理数据库的方法。此模块的主要目的是创建一种与数据库交互的方式,而无需处理连接和管理本身。这是与数据库通信的另一种方式,不同于hyperf使用的方式。
安装
composer require iqomp/model
发布配置
php bin/hyperf.php vendor:publish iqomp/model
配置
配置保存于 config/autoload/model.php
,其中包含用于某些模型的模型连接类型。数据库连接本身返回到hyperf风格。
<?php return [ 'drivers' => [ 'pdo' => 'PDO\\Driver\\Class', // ... ], 'models' => [ 'Model\\Class\\Name' => [ 'read' => 'slave', 'write' => 'master' ], 'Model\\Wildcard\\*' => [ 'read' => 'slave', 'write' => 'master' ] ], 'chains' => [ 'Model\\Class\\Name' => [ '/field/' => [ 'model' => 'Model\\Other\\Class', 'self' => 'id', 'children' => 'wallet_id', 'type' => 'left' ] ] ] ];
驱动程序
所有已知驱动程序。大多数情况下,此属性由模块注册。
模型
配置以决定为每个模型使用哪个数据库连接。如果模型在此处未注册,则如果存在,将使用 default
连接,否则抛出 ConnectionNotFoundException
异常。模型名称接受通配符( *
),可以匹配模型名称的任何部分。每个模型都应该有 read
和 write
属性,用于定义读和写连接。
模型结构
每个模型类应扩展自 Iqomp\Model\Model
并至少有一个名为 table
的公共静态属性。以下是模型的大致样子
<?php namespace Company\Project; class Product extends \Iqomp\Model\Model { public static $table = 'product'; public static $chains = [ '/field/' => [ 'model' => 'Model\\Other\\Class', 'self' => 'id', 'children' => 'wallet_id', 'type' => 'left' ] ]; }
驱动程序
驱动程序是管理数据库连接和与数据库通信的一个。每个驱动程序应将自己注册为模型驱动程序,内容配置为文件 config/autoload/model.php
,如下所示
<?php return [ 'drivers' => [ 'name' => 'Class' ] ];
驱动程序类应实现接口 Iqomp\Model\DriverInterface
。以下是驱动程序应实现的方法列表
/** * Construct new model object * @param array DB connection options * @param string $model Model name * @param string $table Table name * @param array $chains? * @param array $q_field? * @param array $connections * @param array $read List of connection for read * @param array $write List of connection for write */ __construct(array $options); /** * Count average value of field * @param string $field the field sum total to average * @param array $where Where condition * @return float Average value of the column */ avg(string $field, array $where = []): float; /** * Count total rows in table * @param array $where Where condition * @return int Total row */ count(array $where = []): int; /** * Insert single data to database * @param array $row Array column-value pair of data to insert * @param bool $ignore Ignore error data already there * @return int Last inserted id on success, null otherwise */ create(array $row, bool $ignore = false): ?int; /** * Insert many data at once * @param array $rows List of array list data to insert * @param bool $ignore Ignore exists data if possible * @return boolean true on success false otherwise. */ createMany(array $rows, bool $ignore = false): bool; /** * Decrease multiple columns with condition * @param array $fields List of field-value pair of column to decrease by value * @param $where Where condition */ dec(array $fields, array $where = []): bool; /** * Escape string to use in raw query * @param string $str String to escape * @return escaped string */ escape(string $str): string; /** * Get single row from table * @param array $where Where condition * @param array $order Array list of field-direction pair of sort * @return object if exists or null */ getOne(array $where = [], array $order = ['id' => false]): ?object; /** * Get multiple rows from database * @param array $where Where condition * @param int $rpp Result per page, default 0 which is all. * @param int $page Page number, default 1. * @param array $order Array list of field-direction pair of sort * @return array list of object or empty array */ get(array $where = [], int $rpp = 0, int $page = 1, array $order = ['id' => false]): array; /** * Get connection object * @param string $target Connection type target * @return resource connection */ getConnection(string $target = 'read'); /** * Get connection name in config that the model use for $target connection * @param string $target Connection type target * @return string connection config name */ getConnectionName(string $target = 'read'): ?string; /** * Get current connection database name * @param string $target Connection type target * @return string database name */ getDBName(string $target = 'read'): ?string; /** * Get the driver name used for this model * @return string driver name */ getDriver(): ?string; /** * Get the model name of current model * @return string model name */ getModel(): string; /** * Get the tabel name that this model handle * @return string */ getTable(): string; /** * Increase multiple columns with condition * @param array $fields List of field-value pair of column to increase by value * @param $where Where condition */ inc(array $fields, array $where = []): bool; /** * Return last error accured * @return string error message or null */ lastError(): ?string; /** * Return last id inserted to database * @return int last inserted id, or null otherwise */ lastId(): ?int; /** * Return the most last executed query * @return string if exists, null otherwise */ lastQuery(): ?string; /** * Get the maximum value of field from table * @param string $field The field to process * @param array $where Where condition * @return int The max value of field. */ max(string $field, array $where = []): int; /** * Get the minimum value of field from table * @param string $field THe field to process * @param array $where Where condition * @return int The smallest value of field. */ min(string $field, array $where = []): int; /** * Remove row from table * @param array $where Where condition * @return boolean true on success, false otherwise. */ remove(array $where = []): bool; /** * Update table * @param array $fields List of field-value pair of data to update * @param array $where Where condition. * @return boolean true on success false otherwise. */ set(array $fields, array $where = []): bool; /** * Sum table single field. * @param string $field The field to sum * @param array $where Where conditon. * @return int total sum of the field value. */ sum(string $field, array $where = []): int; /** * Truncate the table * @param string $target Connection target */ truncate(string $target = 'write'): bool;
所有上述方法都是 public
方法。
用法
创建模型后,现在很容易从应用程序中使用它
<?php use Company\Project\Model\Product; $id = Product::create($array); $product = Product::getOne(['id'=>$id]);
排序
方法 get
和 getOne
具有可选参数 $order
,可用于对结果进行排序。值是一个字段-顺序对的数组,其中 field
是表的列名,而 order
是布尔值 false
为降序,true
为升序。
Where 条件
某些方法接受参数 $where
,用于过滤操作执行。本部分解释了where的用法
标准
创建where条件的最简单方法是如下所示
$where = [ 'id' => 1, 'name' => 'User' ]; // `id` = 1 AND `name` = 'User'
IN 查询
将多个筛选值组合到数组中,以便在 in
操作符中使用
$where = [ 'id' => [1,2,3], 'status' => 1 ]; // `id` IN (1,2,3) AND `status` = 1
运算符
要使用除 =
之外的运算符进行比较,请使用以下样式
$where = [ 'id' => ['__op', '!=', 12], 'status' => 1 ]; // `id` != 12 AND `status` = 1 $where = [ 'status' => ['__op', '>', 0] ]; // `status` > 0 $where = [ 'meta' => ['__op', '!=', NULL] ]; // `meta` IS NOT NULL $where = [ 'status' => ['__op', 'NOT IN', [1,2]] ]; // `status` NOT IN (1,2)
迄今为止已知的运算符有 >
、<
、<=
、>=
、!=
和 NOT IN
。
BETWEEN
要使用between运算符,请按以下方式使用它
$where = [ 'status' => ['__between', 1, 5] ]; // `status` BETWEEN 1 AND 5
LIKE
使用数组前缀 __like
可以使用 LIKE
操作符
$where = [ 'title' => ['__like', 'name'] ]; // `title` LIKE '%name%' $where = [ 'title' => ['__like', 'name', 'left'], // 'title' => ['__like', 'name', 'both'] // 'title' => ['__like', 'name', 'right'] // 'title' => ['__like', 'name', 'none'] ]; // `title` LIKE '%name' $where = [ 'title' => ['__like', 'name', null, 'NOT'] ]; // `title` NOT LIKE 'name' $where = [ 'title' => ['__like', ['na1', 'na2', 'na3']] ]; // `title` LIKE '%na1%' OR `title` LIKE '%na2%' OR `title` LIKE '%na3%'
AND
默认情况下,每个where数组通过 AND
操作符组合。如果您的where条件不那样规范,您可以使用 $and
数组键将每个子数组组合在一起,与 AND
结合
$where = [ '$and' => [ [ 'created' => ['__op', '!=', NULL] ], [ 'created' => ['__op', '>', '2010-02-01'] ] ] ]; // ( ( `created` IS NOT NULL ) AND ( `created` > '2010-02-01' ) )
OR
就像 $and
一样,您也可以使用 OR
使用 $or
数组键将每个条件组合在一起
$where = [ '$or' => [ [ 'status' => 1, 'user' => 2 ], [ 'status' => 2 ] ] ]; // ( ( `status` = 1 AND `user` = 2 ) OR ( `status` = 2 ) )
转义
如果您需要在where条件下转义列名,请在列名前添加前缀 ?
$where = [ '?`user`' => 1 ]; // `user` = 1 $where = [ '?JSON_EXTRACT(`price`, \'$.anually\')' => ['__op', '!=', ''] ]; // JSON_EXTRACT(`price`, '$.anually') != ''
验证器
如果您的应用程序使用 iqomp/validator 进行对象验证,则此模块添加了可用于验证与数据模型相关对象的验证器。
唯一
确保数据尚未存在于数据库中
// ... 'rules' => [ 'unique' => [ 'model' => 'Company\\Project\\Model\\Product', 'field' => 'slug', 'where' => [ /* ... */ ] // additional where condition ] ] // ...
如果需要测试多个列,将field
属性的值设置为数组
// ... 'rules' => [ 'unique' => [ 'model' => 'Company\\Project\\Model\\Product', 'field' => [ // same name of table column and object property 'slug', // object property => table column 'wallet' => 'wallet_id' ], 'where' => [ /* ... */ ] // additional where condition ] ] // ...
存在
确保数据存在于数据库中
// ... 'rules' => [ 'exists' => [ 'model' => 'Company\\Project\\Model\\Product', 'field' => 'slug', 'where' => [ /* ... */ ] // additional where condition ] ] // ...
存在列表
确保对象属性(数组)的所有值都已在数据库中存在
// ... 'rules' => [ 'exists-list' => [ 'model' => 'Company\\Project\\Model\\Product', 'field' => 'slug', 'where' => [ /* ... */ ] // additional where condition ] ] // ...
格式化器
如果您在对象格式化器中使用iqomp/formatter,此模块添加了一些格式类型。
默认情况下,不会从数据库中获取数据,而是将所有具有以下格式类型的属性值使用null
或{"id": value}
作为对象值。
如果您需要使用表中的数据填充属性,请在调用格式化器时添加额外的选项
$result = Formatter::format('fmt-name', $object, ['user'=>true]);
通过调用上述函数,options.user = true
告诉处理程序从数据库中获取数据。
如果对象user
有您想从数据库中获取的子对象,请使用数组键布尔对作为选项user的值
$result = Formatter::format('fmt-name', $object, ['user' => ['profile'=>true]]);
上述操作将从数据库中获取当前对象的user
属性,然后从user
属性的数据库中获取profile
属性。
如果您需要在获取数据时添加额外的where条件,您也可以在选项中添加
$result = Formatter::format('fmt-name', $object, [ 'user' => [ '_where' => [ 'status' => 1 ] ] ]);
上述操作将带有附加where条件status = 1
的数据从数据库中获取。
格式类型
多对象
将用某些字符串分隔的对象属性或使用json_decode分解成从数据库获取的对象
// ... 'publishers' => [ 'type' => 'multiple-object', 'separator' => ',', // 'json' 'model' => [ 'name' => 'Company\\Project\\Model\\User', 'field' => 'id' ], // optional see below 'field' => [ 'name' => '/field/', 'type' => '/type/' ], // optional, see below 'fields' => [ ['name' => '/field1/', 'type' => '/type/'], ['name' => '/field2/', 'type' => '/type/'] ], // optional, see below 'format' => '/object-format/' ] // ...
链式
从通过另一个表连接的另一个表中获取对象。
// ... 'tags' => [ 'type' => 'chain', 'chain' => [ 'model' => [ 'name' => 'Company\\Project\\Model\\PostTagChain', 'field' => 'post' ], 'identity' => 'post_tag' ], 'model' => [ 'name' => 'Company\\Project\\Model\\PostTag', 'field' => 'id' ], // optional see below 'field' => [ 'name' => '/field/', 'type' => '/type/' ], // optional, see below 'fields' => [ ['name' => '/field1/', 'type' => '/type/'], ['name' => '/field2/', 'type' => '/type/'] ], // optional, see below 'format' => '/object-format/' ] // ...
对象
将对象属性的值转换为从表中获取的对象数据
// ... 'user' => [ 'type' => 'object', 'model' => [ 'name' => 'Company\\Project\\Model\\User', 'field' => 'id' ], // optional see below 'field' => [ 'name' => '/field/', 'type' => '/type/' ], // optional, see below 'fields' => [ ['name' => '/field1/', 'type' => '/type/'], ['name' => '/field2/', 'type' => '/type/'] ], // optional, see below 'format' => '/object-format/' ] // ...
对象切换
根据其他对象属性的值切换模型使用
// ... 'meta' => [ 'type' => 'object-switch', 'field' => 'type', 'cases' => [ 1 => [ 'model' => [ 'name' => 'Company\\Project\\Model\\User', 'field' => 'id' ], // optional see below 'field' => [ 'name' => '/field/', 'type' => '/type/' ], // optional, see below 'fields' => [ ['name' => '/field1/', 'type' => '/type/'], ['name' => '/field2/', 'type' => '/type/'] ], // optional, see below 'format' => '/object-format/' ], 2 => [ // ... ] ] ] // ...
部分
使用关系id与当前对象id相同的其他表中的对象。此操作将添加新的对象属性
// ... 'content' => [ 'type' => 'partial', 'model' => [ 'name' => 'Company\\Project\\Model\\User', 'field' => 'id' ], // optional see below 'field' => [ 'name' => '/field/', 'type' => '/type/' ], // optional, see below 'fields' => [ ['name' => '/field1/', 'type' => '/type/'], ['name' => '/field2/', 'type' => '/type/'] ], // optional, see below 'format' => '/object-format/' ] // ...
附加属性
每个格式都接受名为field
、fields
或format
的附加配置。在单个属性配置中只能有一个。
field
从数据库获取数据后,只取一个列(即field->name
)的结果,并将其值用作当前对象属性。如果存在属性type
,将该格式类型应用于取值。
fields
类似于field
,接受此方法从数据库结果中获取多个数据,并使用所有提到的属性作为当前对象属性的新值。如果存在属性type
,将该格式类型应用于取值。
format
使用此格式名称格式化从数据库获取的数据,并使用格式化的数据作为当前对象属性。