新增

php demo 项目地址

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');
    }
}
在src\Model\Table目录下新建PermissionsTable.php,写入代码:
 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');
    }
}
在src\Model\Table目录下新建RolePermissionsTable.php,写入代码:
 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,关联代码应该像下面所示:

php db

 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,关联代码应该像下面所示:

php db

 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

php db

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。表外键对应如下图所示。

php db

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

php api

参数设置情况:

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 查看结果:

php api