[TOC]
## 命名规范
[【官方文档】](https://www.kancloud.cn/manual/thinkphp5/118007)
ThinkPHP5遵循PSR-2命名规范和PSR-4自动加载规范,并且注意如下规范:
### 目录和文件
* 目录使用小写+下划线;
* 类库、函数文件统一以`.php`为后缀;
* 类的文件名均以命名空间定义,并且命名空间的路径和类库文件所在路径一致;
* 类文件采用驼峰法命名(首字母大写),其它文件采用小写+下划线命名;
* 类名和类文件名保持一致,统一采用驼峰法命名(首字母大写);
### 函数和类、属性命名
* 函数的命名使用小写字母和下划线(小写字母开头)的方式,例如 `get_client_ip`;
* 类的命名采用驼峰法(首字母大写),例如 `User`、`UserType`,默认不需要添加后缀,例如`UserController`应该直接命名为`User`;
* 方法的命名使用驼峰法(首字母小写),例如 `getUserName`;
* 属性的命名使用驼峰法(首字母小写),例如 `tableName`、`instance`;
* 以双下划线“__”打头的函数或方法作为魔术方法,例如 `__call` 和` __autoload`;
### 常量和配置
* 常量以大写字母和下划线命名,例如 `APP_PATH`和 `THINK_PATH`;
* 配置参数以小写字母和下划线命名,例如 `url_route_on` 和`url_convert`;
### 数据表和字段
* 数据表和字段采用小写加下划线方式命名,并注意字段名不要以下划线开头
例如 `think_user` 表和 `user_name`字段,不建议使用驼峰和中文作为数据表字段命名。
### 应用类库命名空间规范
应用类库的根命名空间统一为app(不建议更改,可以设置`app_namespace`配置参数更改,V5.0.8版本开始使用`APP_NAMESPACE`常量定义);
例如:`app\index\controller\Index`和`app\index\model\User`。
## 模型对象
### 模型的基本CURD操作
| 用法 | Db类 | 模型(动态) | 模型(静态)| 说明|
| --- | --- | --- | --- | --- |
| 创建 | insert | save | create | save(动态):返回影响的记录数<br />create(静态):返回模型对象实例|
| 更新 | update | save | update | save :更新数据,返回影响的记录数<br />update:更新数据(静态),返回模型对象实例 |
| 读取单个 | find | find | get | get:查询单个记录,返回模型对象实例<br />find:查询单个记录,返回模型对象实例 |
| 读取多个 | select | select | all | all:根据主键查询多个记录,返回包含模型对象实例的数组或者数据集<br />select:根据条件查询多个记录,返回包含模型对象实例的数组或者数据集 |
| 删除 | delete | delete | destroy | delete:删除当前数据,返回影响的记录数<br />destroy:删除指定数据(静态),返回影响的记录数 |
注意事项:
1. 模型类可以直接调用Db类的所有方法;
2. 模型类和Db类的查询返回类型是完全不同的,即便是调用同一个方法查询;
3. 模型类封装的静态方法本质上还是调用的动态方法,只是为了方便不同的需求场景;
4. 模型对象的查询操作尽量使用静态方法调用;
>[danger] 在模型的删除功能设计的时候,应该尽量用软删除替代实际的删除,一方面是为了避免数据丢失,一方面也是为了性能考虑(数据库的删除操作会导致重建索引,数据量越大影响越大)。
### 数据集(`collection`对象)
模型的单个数据查询返回的都是模型对象实例,但查询多个数据的时候默认返回的是一个包含模型对象实例的数组。
框架提供了一个Collection数据集对象来进行统一的模型的对象化操作,替代默认的数组数据集更好的封装自己的数据处理和业务逻辑。
有两种方式可以设置,
1. 全局设置数据库的配置参数(默认设置为array):
~~~php
// 设置数据集返回类型
'resultset_type' => 'collection',
~~~
2. 在模型类中添加属性设置
~~~php
// 设置模型的数据集返回类型
protected $resultSetType = 'collection';
~~~
该设置仅仅影响设置的模型中的查询结果,如果需要多个模型或者全部模型支持,可以使用继承或者使用第一种数据库配置方式。
设置数据集对象后,查询多个数据的方法(包括Db类的`select`和模型类的`all`方法)返回的结果类型就会变成`think\model\Collection`对象实例。
数据集对象和普通的二维数组在使用上的一个最大的区别就是数据是否为空的判断,
~~~php
// 二维数组的数据集判断数据为空
$resultSet = User::all();
if (empty($resultSet)) {
echo '数据集为空';
}
//数据集对象的判断数据为空
$resultSet = User::all();
if ($resultSet->isEmpty()) {
echo '数据集为空';
}
//通用判断数据为空
$resultSet = User::all();
if (0 == count($resultSet)) {
echo '数据集为空';
}
~~~
其它操作的区别就是一个是对象的方法操作,一个是数组函数的操作,下面是数据集对象的方法和数组函数的对应关系:
| 作用| 数据集方法 | 数组函数 |
| --- | --- | --- |
| 合并数据 | merge | array_merge|
| 比较数据差集 | diff | array_diff|
| 交换数组中的键和值 | flip | array_flip|
| 比较数组交集 | intersect | array_intersect|
| 返回键名 | keys | array_keys|
| 最后元素出栈 | pop| array_pop|
| 数组迭代简化 | reduce | array_reduce|
| 数据反序 | reverse | array_reverse|
| 首个元素出栈 | shift | array_shift|
| 开头插入元素 | unshift | array_unshift|
| 元素回调 | each | ---|
| 过滤元素 | filter | array_filter|
| 返回指定列 | column | array_column|
| 元素排序 | sort | array_sort|
| 打乱元素 | shuffle | shuffle|
| 截取部分元素 | slice | array_slice|
| 元素分割 | chunk | array_chunk|
| 转换数组 | toArray | ---|
可以自定义数据集的返回对象,然后在里面封装其它的方法。
一般自定义的数据集对象建议继承`think\model\Collection`,然后在模型中设置`resultSetType`属性值为自定义查询类的类名。
~~~php
// 设置模型的数据集返回类型
protected $resultSetType = 'app\common\Collection';
~~~
总结下数据集的优势:
* 数据更对象化;
* 关联操作更方便;
* 数据集本身可以单独定义独立的业务方法;
### 自定义查询(`get`方法`all`方法)
~~~php
// 查询单个记录
User::get(['name' => 'thinkphp']);
// 查询数据集(闭包查询条件)
User::all(function ($query) {
$query->where('id', '>', 0)
->limit(10)
->order('id desc');
});
// 删除数据
User::destroy(['status' => 0]);
User::destroy(function ($query) {
$query->where('id', 'in', [1, 2, 3]);
});
~~~
>[danger] `get`、`all`以及`destroy`方法的参数用法记住如下原则,
> 1. 如果是数字、字符串或者普通数组都表示一个或者多个主键,
> 2. 如果是索引数组则表示查询条件,
> 3. 闭包则支持查询条件以外的其它链式操作方法。
> 4. 对于get方法的参数最好做一次非null检查,否则查询的就会是第一个数据(V5.0.8+已经改进,不需要检查是否为null了)。
### 查询范围(`scope`前缀方法)
对于一些常用的查询条件,我们可以事先定义好,以便快速调用,这个事先定义的查询条件方法有一个统一的前缀`scope`,我们称之为查询范围。例如在`User`模型中定义如下查询范围
~~~php
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
// email查询
protected function scopeEmail($query)
{
$query->where('email', 'thinkphp@qq.com');
}
// status查询
protected function scopeStatus($query)
{
$query->where('status', 1);
}
}
~~~
现在可以直接使用
~~~php
$users = User::scope('email,status')->select();
//或者
$users = User::scope('email')->scope('status')->select();
~~~
生成的SQL语句为
~~~sql
SELECT * FROM `user` WHERE `email` = 'thinkphp@qq.com' AND `status` = 1
~~~
查询范围之外仍然可以使用额外的查询条件,例如:
~~~php
$users = User::scope('email,status')
->where('nickname', 'like', '%think%')
->order('id desc')
->select();
~~~
>[danger] 查询范围方法必须首先被调用
查询范围方法支持额外的参数,例如`scopeEmail`方法改为:
~~~php
// email查询
protected function scopeEmail($query, $email = '')
{
$query->where('email', $email);
}
//然后,使用下面的方式调用即可(带参数调用的时候每次只能调用一个查询范围):
$list = User::scope('email', 'thinkphp@qq.com')->select();
~~~
>[info] 查询范围的方法的第一个参数必须是查询对象,并且支持多个额外参数。
### 全局查询范围(`base`方法)
查询范围有一个特殊的方法`base`,一旦在模型中定义了`base`方法后,无需显式调用scope方法,系统会在每次查询的时候自动调用。
~~~php
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
// 全局查询范围
protected static function base($query)
{
// 查询状态为1的数据
$query->where('status', 1);
}
// email查询
protected function scopeEmail($query)
{
$query->where('email', 'thinkphp@qq.com');
}
}
~~~
当使用下面的查询操作
~~~php
User::get(1);
User::scope('email')->select();
~~~
最后生成的SQL语句分别是:
~~~sql
SELECT * FROM `user` WHERE `status` = 1 AND `id` = 1 LIMIT 1
SELECT * FROM `user` WHERE `status` = 1 AND `email` = 'thinkphp@qq.com'
~~~
无论是什么查询都会默认带上全局查询范围中的条件。
可以临时关闭全局查询范围进行查询
~~~php
// 关闭全局查询范围
User::useGlobalScope(false)->get(1);
~~~
>[info] 查询范围方法中不仅支持where方法,任何查询构造器的方法都可以被支持。
### 软删除
软删除的作用就是把数据加上删除标记,而不是真正的物理删除,同时也便于需要的时候进行数据的恢复。因为在实际项目中,对数据频繁使用删除操作会导致性能问题,因此不推荐直接物理删除数据,而是用软删除(逻辑删除)替代。
**1. 模型启用软删除功能**
要使用软删除功能,需要引入`SoftDelete trait`,例如`User`模型按照下面的定义就可以使用软删除功能:
~~~php
<?php
namespace app\index\model;
use think\Model;
use traits\model\SoftDelete;
class User extends Model
{
use SoftDelete;
}
~~~
>[danger] 为了配合软删除功能,需要在数据表中添加`delete_time`字段,ThinkPHP5的软删除功能使用时间戳类型(数据表默认值为`Null`,MySQL中默认值为`NULL`),用于记录数据的删除时间。
**2. 数据表中指定软删除功能的标记字段**
如果软删除标记字段名称不是`delete_time`的话,需要添加属性定义:
~~~
<?php
namespace app\index\model;
use think\Model;
use traits\model\SoftDelete;
class User extends Model
{
use SoftDelete;
protected $deleteTime = 'delete_field_name';
}
~~~
>[info] 可以用类型转换指定软删除字段的类型。
> 建议数据表的所有时间字段统一使用autoWriteTimestamp属性规范时间类型(支持datetime、date、timestamp以及integer)。
~~~php
<?php
namespace app\index\model;
use think\Model;
use traits\model\SoftDelete;
class User extends Model
{
use SoftDelete;
protected $autoWriteTimestamp = 'datetime';
protected $deleteTime = 'delete_field_name';
}
~~~
**3. 软删除与物理删除**
定义好模型,并在模型中按前2步启用软删除功能,就可以使用静态`destroy`或动态`delete`方法进行软删除:
~~~php
// 软删除
User::destroy(1);
// 真实删除
User::destroy(1,true);
$user = User::get(1);
// 软删除
$user->delete();
// 真实删除
$user->delete(true);
~~~
是将MySQL数据库中的字段`delete_time`值设为由`NULL`设为时间戳。
**4. 恢复软删除的记录**
定义好模型,并在模型中按前2步启用软删除功能,就可以使用`restore()`方法恢复软删除的记录:
~~~php
//软删除的恢复
$user->restore(['id'=>1]);
~~~
`restore($where=[])`方法需要的是与查询方法类似的where条件数组,执行成功返回的是恢复的记录数。
是将MySQL数据库中的字段`delete_time`值设为`NULL`。
**5. 软删除与查询**
默认情况下查询的数据不包含软删除数据,如果需要包含软删除的数据,可以使用下面的方式查询:
~~~php
User::withTrashed()->find();
User::withTrashed()->select();
~~~
如果仅仅需要查询软删除的数据,可以使用:
~~~php
User::onlyTrashed()->find();
User::onlyTrashed()->select();
~~~
如果查询条件比较复杂,尤其是某些特殊情况下使用`OR`查询条件会把软删除数据也查询出来,可以使用闭包查询的方式解决,如下:
~~~php
User::where(function($query) {
$query->where('id', '>', 10)
->whereOr('name', 'like', 'think');
})->select();
~~~
>[danger] 使用闭包查询条件会在查询条件两边添加括号,从而不会和软删除条件产生混淆或者冲突。
如果模型定义了base基础查询(全局查询范围,框架在查询时自动调用),请确保添加软删除的基础查询条件,例如:
~~~php
protected static function base($query)
{
// 添加软删除条件
$query->whereNull('delete_time')
// 添加额外的基础查询条件
->where('id','>',0);
}
~~~
### 字段过滤 (`allowField`方法)
模型类提供了`allowField`方法用于在数据写入操作的时候设置字段过滤,从而避免数据库因为字段不存在而报错。
~~~php
// 获取当前用户对象
$user = User::get(request()->session('user_id'));
// 只允许更新用户的nickname和address数据
$user->allowField(['nickname', 'address'])
->data(requst()->param(), true)
->save();
~~~
如果仅仅是希望去除数据表之外的字段,可以使用
~~~php
// 只允许更新数据表字段数据
$user->allowField(true)
->data(requst()->param(), true)
->save();
~~~
如果使用的是模型的静态方法(如create和update方法)进行数据写入的话,可以使用下面的方式进行字段过滤。
~~~php
User::create(request()->param(), ['nickname', 'address']);
User::update(request()->param(), ['id' => 1], ['nickname', 'address']);
~~~
同样可以传入`true`表示过滤非数据表字段
~~~php
User::create(request()->param(), true);
User::update(request()->param(), ['id' => 1], true);
~~~
## 问题
### 1. 应用公共文件(common.php)如何自动加载,从而实现在应用中直接使用文件中定义的常量和函数?
### 2. 模块公共文件(module/common.php)中定义的常量和函数如何使用?
- WebAPP
- Linux Command
- 入门
- 处理文件
- 查找文件单词
- 环境
- 联网
- Linux
- Linux目录配置标准:FHS
- Linux文件与目录管理
- Linux账号管理与ACL权限设置
- Linux系统资源查看
- 软件包管理
- Bash
- Daemon/Systemd
- ftp
- Apache
- MySQL
- Command
- Replication
- mysqld
- remote access
- remark
- 限制
- PHP
- String
- Array
- Function
- Class
- File
- JAVA
- Protocals
- http
- mqtt
- IDE
- phpDesigner
- eclipse
- vscode
- Notepad++
- WebAPI
- Javasript
- DOM
- BOM
- Event
- Class
- Module
- Ajax
- Fetch
- Promise
- async/await
- Statements and declarations
- Function
- Framwork
- jQurey
- Types
- Promise
- BootStrap
- v4
- ThinkPHP5
- install
- 定时任务
- CodeIgniter
- React.js
- node.js
- npm
- npm-commands
- npm-folder
- package.json
- Docker and private modules
- module
- webpack.js
- install
- configuration
- package.json
- entry
- modules
- plugins
- Code Splitting
- loaders
- libs
- API
- webpack-cli
- Vue.js
- install
- Compile
- VueAPI
- vuex
- vue-router
- vue-devtools
- vue-cli
- vue-loader
- VDOM
- vue-instance
- components
- template
- Single-File Components
- props
- data
- methods
- computed
- watch
- Event-handling
- Render Func
- remark
- 案例学习
- bootstrap-vue
- modal
- fontAwesome
- Hosting Font Awesome Yourself
- using with jquery
- using with Vue.js
- HTML
- CSS
- plugins
- Chart.js
- D3.js
- phpSpreadSheet
- Guzzle
- Cmder
- Git
- git命令
- git流程
- Postman
- Markdown
- Regular Expressions
- PowerDesigner
- 附录1-学习资源