新增¶
1 | git clone git@gitlab.freedomcz.com.cn:php/demo.git |
1.Controller:¶
在src\Controller目录下新建RolesController.php,写入代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php namespace App\Controller; use App\Controller\AppController; use App\Model\Logic\System\RolesLogic; class RolesController extends AppController { // 查询所有的角色 public function index() { $roles = RolesLogic::instance()->findAll(); $this->responseOk($roles); } } |
2.Logic:¶
在src\Model\Logic目录下新建RolesLogic.php,写入代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <?php namespace App\Model\Logic\System; use Cake\ORM\TableRegistry; class RolesLogic { protected static $instance; public function __construct() { $this->rolesTable = TableRegistry::getTableLocator()->get("Roles"); } public static function instance() { if (self::$instance == null) { self::$instance = new RolesLogic(); } return self::$instance; } public function findAll() { $roles = $this->rolesTable->find()->contain(['Permissions' => ['fields' => ['id', 'code']]]) ->select([ "id", "name", "status", "remarks", ]) ->where(["status =" => 0]) ->toArray(); return $roles; } } |
2.1 find()方法¶
方法:Cake\ORM\Table::find($type = 'all', options = []) find()若不传递任何参数,将会执行默认的findAll()方法,type有以下默认值:
| 类型($type) | 方法 | 说明 |
|---|---|---|
| all | findAll() | 返回一个数组 |
| list | findList() | 返回一个索引数组 |
| threaded | findThreaded() | 返回嵌套数组,默认情况下使用parent_id |
$type和方法对应规则为 find('xxx')=>findXxx()
如果想自定义findXxxx()方法,请参考资料自定义find方法
示例:
1 2 3 4 5 6 7 8 9 | <?php // src\Model\Table\XxxTable.php ,在XxxTable.php中写入以下代码: public function findRoleCount(Query $query, array $options) { return $query->where(['status =' => $options['status']])->count(); } 使用方式: $query = $this->rolesTb->find('roleCount', ['status' => 1]); |
$options的参数有:
| 参数名 | 作用 | 参数示例 | 函数示例 |
|---|---|---|---|
| contain | 关联查询,需在Table内设置OneToOne等对应关系 | ['contain' => ['Permissions' => ['fields' => ['id', 'code']]]] | find()->contain(['Permissions' => ['fields' => ['id', 'code']]]) |
| select | 字段名 | ['select' => ['id','name','status','remarks']] | find()->select(['id','name','status','remarks']) |
| conditions | where条件 | ['conditions' => ['roles.status =' => 0, 'or' => [['roles.id =' => 1], ['roles.id =' => 2]]]] | find()->where(["roles.status =" => 0, 'or' => [['roles.id =' => 1], ['roles.id =' => 2]]]) |
| join | join其他表 | ['join' => ['table' => 'departments','alias' => 'departments','type' => 'LEFT','conditions' => 'departments.id = roles.id']] type可选LEFT,RIGHT,INNER | find()->leftJoin(['departments' => 'departments'],['departments.id = roles.id']); find()->rightJoin(···); find()->innerJoin(···); |
| group | 在查询中添加GROUP BY子句 | ['group' => ['id','name']] | find()->group(['id']) |
| having | 在查询中添加HAVING子句 | ['having' => ['id >' => 2]] | find()->having(['id >' => 2]) |
| order | 对查询进行排序 | ['order' => ['created' => 'DESC']] | find()->order(['created' => 'DESC']) |
| offset | 跳过n行,从n+1行开始查询 | ['offset' => n], // n为整型数据 | find()->offset(n) |
| limit | 想要获取的数据条数 | ['limit' => n], // n为整型数据 | find()->limit(n) |
2.2 集合方法¶
在进行find()查询操作或集合操作时,可以使用集合方法对查询出来的结果集进行操作,下面是一些常用的方法:
| 方法名 | 作用 | 示例 | 返回值 |
|---|---|---|---|
| map() | 基于已有集合创建新的集合 | map(function ($item) { item->name = trim(item->name); return $item; }) | collection |
| isEmpty() | 检查集合是否为空 | isEmpty() | true or false |
| filter() | 按条件过滤集合元素 | filter(function ($item) { return $item->id === 1; }) | collection |
| first() | 返回集合第一个元素 | first() | 集合第一个元素 |
| last() | 返回集合最后一个元素 | last() | 集合最后一个元素 |
2.3 findByXxx()方法¶
使用动态查找可以做一些简单的查询,例如:
1 2 3 | <?php $query = $this->Users->findByUsername('jack'); $query = $this->Users->findAllByUsername('jack'); |
这两个查询是等价的,都会返回username='jack'的user。还可以使用OR或者AND进行查询,例如:
1 2 3 4 5 6 | <?php // 查询username='jack'并且age=20的user $query = $users->findAllByUsernameAndAge('jack', 20); // 查询username='jack'或者email='jack@example.com'的user $query = $users->findAllByUsernameOrEmail('jack', 'jack@example.com'); |
2.4 SQL函数¶
使用方式(通用):
1 2 3 4 | <?php find()->select([ 'count' => $this->rolesTable->find()->func()->count('*') ]) |
常见SQL函数:
| 函数名 | 作用 | 使用方法 |
|---|---|---|
| rand() | 生成0-1之间的随机数 | $xxxTable->rand() |
| sum() | 和 | $xxxTable->sum('字段名') |
| avg() | 平均值 | $xxxTable->avg('字段名') |
| min() | 最小值 | $xxxTable->min('字段名') |
| max() | 最大值 | $xxxTable->max('字段名') |
| count() | 计数 | $xxxTable->count('字段名') |
| concat() | 拼接(使用'identifier'表示数据库列) | $xxxTable->concat(['roles.id' => 'identifier', 'roles.name' => 'identifier']) |
| dateDiff() | 日期差异 | $xxxTable->dateDiff(['now()' => 'literal', 'roles.created_time' => 'literal']) |
| now() | 当前时间 | $xxxTable->now() |
| extract() | 返回指定列数据的数组 | $xxxTable->extract('字段名') |
3.创建Table:¶
在src\Model\Table目录下新建RolesTable.php,写入代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php namespace App\Model\Table; class RolesTable extends BaseTable { public function initialize(array $config) { $this->belongsToMany('Permissions', [ 'through' => 'RolePermissions', ]); $this->setTable('roles'); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php namespace App\Model\Table; class PermissionsTable extends BaseTable { public function initialize(array $config) { $this->belongsToMany('Roles', [ 'through' => 'RolePermissions', ]); $this->setTable('permissions'); } } |
1 2 3 4 5 6 7 8 9 10 11 12 | <?php namespace App\Model\Table; class RolePermissionsTable extends BaseTable { public function initialize(array $config) { $this->belongsTo('Roles'); $this->belongsTo('Permissions'); $this->setTable('role_permissions'); } } |
3.1 表与表之间的关联¶
| 关系 | 关联类型 |
|---|---|
| 一对一 | hasOne |
| 一对多 | hasMany |
| 多对一 | belongsTo |
| 多对多 | belongsToMany |
关联定义在表对象的 initialize() 方法中。
3.1.1 一对一¶
一个 A 只有一个 B ,我们可以创建 ATable 和 BTable,关联代码应该像下面所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?php class ATable extends Table { public function initialize(array $config) { $this->hasOne('B', [ 'className' => 'B', // 关联表的名称,默认为B(BTable) 'foreignKey' => 'a_id', // 关联表的外键名称,例如本例中'id',未设置时,会按照字段后缀名匹配:'id'和'a_id' 'bindingKey' => 'id', // 本表中和关联表相关对应的键名 'conditions' => ['B.status' => 0], // 关联条件,例如:['B.status' => 0] 'joinType' => 'LEFT', // 使用JOIN的类型,可选:LEFT和INNER,默认为LEFT 'dependent' => false, // 为true时,会删除关联信息 'cascadeCallbacks' => false, // 当本项和dependent设置成true的时候,会删除关联的信息,并回调函数,false则不回调 ,详情查看3.15章节 'propertyName' => 'propertyName', //关联表中数据填充到原表结果中的属性名称,默认为表名的单数形式 'strategy' => 'join', // 可选:select和join,默认为join 'finder' => 'all' // 查询关联记录使用的find方法,可选'all','list','threaded'。默认为'all' ]); } } |
使用方式:
1 2 | <?php $query = $aTable->find('all')->contain(['B']); |
上面的查询执行的sql会像下面所示:
1 2 3 | SELECT A.*,B.* FROM a A LEFT JOIN b B ON (B.status = 0 AND A.id = B.a_id) |
3.1.2 一对多¶
一个 A 有多个 B ,我们可以创建 ATable 和 BTable,关联代码应该像下面所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?php class ATable extends Table { public function initialize(array $config) { $this->hasMany('B', [ 'className' => 'B', // 关联表的名称,默认为B(BTable) 'foreignKey' => 'a_id', // 关联表的外键名称 'bindingKey' => 'id', // 本表中和关联表相对应的键名 'conditions' => ['B.id >=' => 1], // 关联条件,例如:['B.id >=' => 1] 'sort' => ['B.created_time' => 'DESC'], // 排序条件,例如:['B.created_time' => 'DESC'] 'dependent' => false, // 为true时,会级联删除关联信息 'cascadeCallbacks' => false, // 当本项和dependent设置成true的时候,会级联删除关联的信息,并回调函数,false则不回调,详情请查看3.15章节 'propertyName' => 'propertyName', // 关联表中数据填充到原表结果中的属性名称,默认为实体名称的复数 'strategy' => 'select', // 可选:select和subquery,默认为select。subquery会用子查询替代in的列表 'saveStrategy' => 'append', // 可选:append和replace,默认为append, 'finder' => 'all' // 查询关联记录使用的find方法,可选'all','list','threaded'。默认为'all' ]); } } |
'cascadeCallbacks'参数详情请查看3.1.5
使用方式:在Logic中使用
1 2 | <?php $query = $aTable->find('all')->contain(['B']); |
上面的查询执行的sql会像下面所示:
1 2 | SELECT * FROM a; SELECT * FROM b WHERE a_id IN (1, 2, 3, 4, 5); |
当 'strategy' => 'subquery' 时,sql会如下所示:
1 2 | SELECT * FROM a; SELECT * FROM b WHERE a_id IN (SELECT id FROM a); |
3.1.3 多对一¶
多个 A 属于一个 B
belongsTo可以设置的参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php class ATable extends Table { public function initialize(array $config) { $this->belongsTo('B',[ 'className' => 'B', // 关联表的名称,默认为B(BTable) 'foreignKey' => 'b_id', // 关联表的外键名称 'bindingKey' => 'id', // 本表中和关联表的外键对应的键名 'conditions' => ['B.status =' => 0], // 关联条件,例如:['B.status =' => 0] 'joinType' => 'LEFT', // 使用JOIN的类型,可选:LEFT和INNER,默认为LEFT 'propertyName' => 'propertyName', // 关联表中数据填充到原表结果中的属性名称 'strategy' => 'join', // 可选:select和join,默认为join 'finder' => 'all' // 查询关联记录使用的find方法,可选'all','list','threaded'。默认为'all' ]); } } |
使用方式:在Logic中使用
1 2 | <?php $query = $aTable->find('all')->contain(['B']); |
上面的查询执行的sql会像下面所示:
1 | SELECT * FROM a A LEFT JOIN b B ON A.b_id = B.id; |
3.1.4 多对多¶
一个 A 有多个 B ,一个 B 有多个 A。表外键对应如下图所示。
belongsToMany可以设置的参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?php class ATable extends BaseTable { public function initialize(array $config) { $this->belongsToMany('B', [ 'className' => 'B', // 关联表的名称,默认为B(BTable) 'joinTable' => 'a_b', // 中间表的表名,用于加载中间表实例,规则为a_b,当表名不符合规则的时候,需要给出表名 'foreignKey' => 'a_id', // 中间表和本表对应的外键名称 'bindingKey' => 'id', // 本表和中间表外键相对应的键名 'targetForeignKey' => 'b_id', // 中间表和关联表对应的外键名称 'conditions' => ['id >' => 2], // 执行关联的额外条件,例如:['B.id >' => 1] 'sort' => ['B.created_time' => 'DESC'], // 排序条件,例如:['B.created_time' => 'DESC'] 'dependent' => false, // 为true时,会级联删除关联信息 'cascadeCallbacks' => false, // 当本项和dependent设置成true的时候,会级联删除关联的信息,并回调函数,false则不回调,详情查看3.15章节 'through' => 'AB', // 自定义连接表实例名(joinTable和through不需要重复定义) 'propertyName' => 'propertyName', // 关联表中数据填充到原表结果中的属性名称 'strategy' => 'select', // 可选:select和subquery,默认为select。subquery会用子查询替代in的列表 'saveStrategy' => 'append', // 可选:append和replace,默认为append, 'finder' => 'all' // 查询关联记录使用的find方法,可选'all','list','threaded'。默认为'all' ]); } } |
使用方式:在Logic中使用
1 2 | <?php $query = $aTable->find('all')->contain(['B']); |
上面的查询执行的sql会像下面所示:
1 2 3 4 5 6 | SELECT * FROM a; SELECT * FROM b INNER JOIN a_b ON ( a.id = a_b.a_id AND b_id IN (1, 2, 3, 4, 5) ); |
当 'strategy' => 'subquery' 时, IN 字句会使用子查询:
1 2 3 4 5 6 | SELECT * FROM a; SELECT * FROM b INNER JOIN a_b ON ( a.id = a_b.a_id AND b_id IN (SELECT id FROM a) ); |
3.1.5 cascadeCallbacks¶
使用'cascadeCallbacks'参数时请将 'dependent' 设置为true
关联关系: A hasMany B ; B hasMany C ; C hasMany D
参数设置情况:
| A | B | C | D | 结果 |
|---|---|---|---|---|
| 'cascadeCallbacks'=>false | 'cascadeCallbacks'=>false | ---- | ---- | C和D不会被级联删除 |
| 'cascadeCallbacks'=>true | 'cascadeCallbacks'=>false | ---- | ---- | C会被级联删除,D不会被级联删除 |
| 'cascadeCallbacks'=>true | 'cascadeCallbacks'=>true | ---- | ---- | C和D会被级联删除 |
关联关系:A关联B,B关联C
删除A时,若想删除C,需将A中的cascadeCallbacks设置为true
关联关系:A关联B,B关联C,C关联D
删除A时,若想删除C和D,需将A和B中的cascadeCallbacks同时设置为true
3.2 Table初始化可设置参数¶
-
设置Table实例化的时候使用的表
- $this->setTable('roles');
-
设置表格别名
- $this->setAlias('roles');
-
设置表格主键
- $this->setPrimaryKey('id');
-
设置表格使用的实体对象
- $this->setEntityClass('App\Model\Entity\User');
4.创建Entity:¶
在src\Model\Entity目录下新建Role.php,写入代码:
1 2 3 4 5 6 7 8 9 | <?php namespace App\Model\Entity; use Cake\ORM\Entity; class Role extends Entity { protected $_hidden = ['created_by','created_time','modified_by','modified_time']; } |
在src\Model\Entity目录下新建Permission.php,写入代码:
1 2 3 4 5 6 7 8 9 | <?php namespace App\Model\Entity; use Cake\ORM\Entity; class Permission extends Entity { protected $_hidden = ['created_by','created_time','modified_by','modified_time']; } |
在src\Model\Entity目录下新建RolePermission.php,写入代码:
1 2 3 4 5 6 7 8 | <?php namespace App\Model\Entity; use Cake\ORM\Entity; class RolePermission extends Entity { } |
4.1 $_hidden¶
在实体转json时需要隐藏的字段,例如:
1 2 | <?php protected $_hidden = ['created_by','created_time','modified_by','modified_time']; |
4.2 $_accessible¶
可修改字段的黑白名单,当属性设置为 true 时,为白名单属性,在使用patchEntity()或者newEntity()时可以修改的属性,相反的,黑名单的属性设置为 false ,不可修改。
1 2 3 4 5 6 | <?php protected $_accessible = [ 'title' => true, 'body' => true, '*' => false // '*'指所有字段 ]; |
4.3 $_virtual¶
在使用json_encode()或者toArray()将entity转换为json或者array的时候,虚拟属性默认是隐藏的,当需要在json或array中查看虚拟属性,要导出虚拟字段。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php // 导出虚拟属性 protected $_virtual = ['full_name', 'is_admin']; protected function _getFullName($full_name) { return $full_name; } protected function _getIsAdmin($is_admin) { return $is_admin; } |
4.4 get和set¶
当需要改变属性get和set行为的时候重写对应属性的get和set方法,命名规则为_getXxx(xxx)和_setXxx(xxx):
1 2 3 4 5 6 7 8 9 10 | <?php protected function _getTitle($title) { return $title; } protected function _setTitle($title) { return $title; } |
4.5 检查属性¶
检查属性是否存在,可以使用has(),例:
1 2 3 4 5 6 7 8 | <?php $user = new User([ 'name' => 'jack', 'login_id' => null ]); $user->has('name'); // 存在属性name且有值,返回true $user->has('login_id'); // 存在属性login_id但为空,返回false $user->has('undefined'); // 不存在的属性undefined,返回false. |
检查属性是否为空,可以使用isEmpty()或者hasValue(),例:
1 2 3 4 5 6 | <?php $user = new User([ 'login_id' => null ]); $user->isEmpty('login_id'); // 属性为空时,返回true $user->hasValue('login_id'); // 属性为空时,返回false |
检查属性是否被修改过,可以使用isDirty(),例:
1 2 3 | <?php $user->isDirty(); // 未指定属性时,返回实体内所有属性的状态 $user->isDirty('login_id'); // 指定属性时,返回指定属性的状态 |
5.添加路由:¶
修改config\routes.php文件,在 "/system" 路由下添加代码 $routes->resources("Roles");:
1 2 3 4 5 | <?php Router::scope("/system", function (RouteBuilder $routes) { $routes->fallbacks(DashedRoute::class); $routes->resources("Roles"); }); |
6.结果¶
使用postman访问 http://localhost:8000/system/roles 查看结果: