整理ThinkPHP5笔记

# 整理TP5笔记 #
## 安装
下载:
https://github.com/top-think/think

Windows安装Composer:
https://getcomposer.org/download/

Composer中国全量镜像安装方法,及检测方法

手册:
https://www.kancloud.cn/manual/thinkphp5_1/353946

## 目录结构

Select Code
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
www  WEB部署目录(或者子目录)
├─application           应用目录
│  ├─common             公共模块目录(可以更改)
│  ├─module_name        模块目录
│  │  ├─common.php      模块函数文件
│  │  ├─controller      控制器目录
│  │  ├─model           模型目录
│  │  ├─view            视图目录
│  │  ├─config          配置目录
│  │  └─ ...            更多类库目录
│  │
│  ├─command.php        命令行定义文件
│  ├─common.php         公共函数文件
│  └─tags.php           应用行为扩展定义文件
│
├─config                应用配置目录
│  ├─module_name        模块配置目录
│  │  ├─database.php    数据库配置
│  │  ├─cache           缓存配置
│  │  └─ ...            
│  │
│  ├─app.php            应用配置
│  ├─cache.php          缓存配置
│  ├─cookie.php         Cookie配置
│  ├─database.php       数据库配置
│  ├─log.php            日志配置
│  ├─session.php        Session配置
│  ├─template.php       模板引擎配置
│  └─trace.php          Trace配置
│
├─route                 路由定义目录
│  ├─route.php          路由定义
│  └─...                更多
│
├─public                WEB目录(对外访问目录)
│  ├─index.php          入口文件
│  ├─router.php         快速测试文件
│  └─.htaccess          用于apache的重写
│
├─thinkphp              框架系统目录
│  ├─lang               语言文件目录
│  ├─library            框架类库目录
│  │  ├─think           Think类库包目录
│  │  └─traits          系统Trait目录
│  │
│  ├─tpl                系统模板目录
│  ├─base.php           基础定义文件
│  ├─convention.php     框架惯例配置文件
│  ├─helper.php         助手函数文件
│  └─logo.png           框架LOGO文件
│
├─extend                扩展类库目录
├─runtime               应用的运行时目录(可写,可定制)
├─vendor                第三方类库目录(Composer依赖库)
├─build.php             自动生成定义文件(参考)
├─composer.json         composer 定义文件
├─LICENSE.txt           授权说明文件
├─README.md             README 文件
├─think                 命令行入口文件

## 配置
.env
Env::get(”)

应用配置目录 config
模块配置目录 application/module/config

打开调试模式:
‘app_debug’ => true,

读取配置:
use think\facade\Config;
Config::get()

助手函数:config()

数据库配置
D:\wamp\www\tp5.info\config\database.php

## 入口文件
默认的应用入口文件位于public/index.php

Select Code
1
2
3
4
5
6
7
8
9
namespace think;

// 加载基础文件
require __DIR__ . '/../thinkphp/base.php';
// 执行应用并响应
Container::get('app')->run()->send();

// 绑定指定的模块
Container::get('app')->bind('index')->run()->send();    // 绑定当前访问模块

## URL
没有定义路由的情况下典型的URL访问规则是:

Select Code
1
http://serverName/index.php(或者其它应用入口文件)/模块/控制器/操作/[参数名/参数值...]

默认采用PATH_INFO访问地址

根据路由信息生成URL:

Select Code
1
2
3
4
5
6
7
8
9
// 使用\think\facade\Url::build()
echo Url::build('index/blog/read', ['id' => 5]);    //http://tp5.info/blog/5.html
echo '<br>';
echo Url::build('index/blog/read', ['name' => 'admin']);    ///blog/read/name/admin.html
echo '<br>';
echo Url::build('index/blog/read', ['name' => 'admin'], 'shtml', true); //http://tp5.info/blog/read/name/admin.shtml
echo '<br>';
// 助手函数url()
echo url('index/blog/read');    ///blog/read.html

## 路由
D:\wamp\www\tp5.info\route\route.php

1、回调函数
2、字符串:控制器/方法

最基础的路由定义方法是:

Select Code
1
2
3
4
Route::rule('路由表达式','路由地址','请求类型');
Route::rule('new/:id','index/News/read');
Route::rule('new/:id','News/update','POST');
Route::rule('new/:id','News/read','GET|POST');

快捷注册方法的用法为:
Route::快捷方法名(‘路由表达式’,’路由地址’);

Select Code
1
2
3
4
5
Route::get('new/:id','News/read'); // 定义GET请求路由规则
Route::post('new/:id','News/update'); // 定义POST请求路由规则
Route::put('new/:id','News/update'); // 定义PUT请求路由规则
Route::delete('new/:id','News/delete'); // 定义DELETE请求路由规则
Route::any('new/:id','News/read'); // 所有请求都支持的路由规则

规则表达式:
每个参数中以:开头的参数都表示动态变量,并且会自动绑定到操作方法的对应参数
变量用[ ]包含起来后就表示该变量是路由匹配的可选变量
如果希望URL进行完全匹配,可以在路由表达式最后使用$符号

路由标识:
如果你需要快速的根据路由生成URL地址,可以在定义路由的时候指定生成标识。

Select Code
1
2
3
4
// 注册路由到index模块的News控制器的read操作
Route::rule('new/:id','index/News/read')->name('new_read');
// 生成路由地址的时候就可以使用
url('new_read',['id'=>10]);

定义路由:
1、路由到模块/控制器/操作
Route::get(‘blog/:id’,’index/group.blog/read’);
2、路由到操作方法:@[模块/控制器/]操作
Route::get(‘blog/:id’,’@index/blog/read’);
3、路由到重定向地址
Route::get(‘blog/:id’,’/blog/read/id/:id’);
4、闭包定义

Select Code
1
2
3
Route::get('hello', function () {
    return 'hello,world!';
});

5、路由分组
6、RESFUL资源路由
Route::resource(‘blog’,’index/blog’);

## MVC模式
https://www.kancloud.cn/manual/thinkphp5_1/353953
ThinkPHP支持传统的MVC(Model-View-Controller)模式以及流行的MVVM(Model-View-ViewModel)模式的应用开发,但无论采用何种模式,URL的规范仍然是统一的。

– 路由:路由是用于规划(一般同时也会进行简化)请求的访问地址,在访问地址和实际操作方法之间建立一个路由规则 => 路由地址的映射关系。
– 模块:一个典型的应用是由多个模块组成的,这些模块通常都是应用目录下面的一个子目录,每个模块都有自己独立的配置文件、公共文件和类库文件。
– 控制器:一个模块下面有多个控制器负责响应请求,而每个控制器其实就是一个独立的控制器类。控制器主要负责请求的接收,并调用相关的模型处理,并最终通过视图输出。严格来说,控制器不应该过多的介入业务逻辑处理。事实上,控制器是可以被跳过的,通过路由我们可以直接把请求调度到某个模型或者其他的类进行处理。
– 模型:模型类通常完成实际的业务逻辑和数据封装,并返回和格式无关的数据。ThinkPHP的模型层支持多层设计,你可以对模型层进行更细化的设计和分工,例如把模型层分为逻辑层/服务层/事件层等等。
– 视图:控制器调用模型类后,返回的数据通过视图组装成不同格式的输出。视图根据不同的需求,来决定调用模板引擎进行内容解析后输出还是直接输出。视图通常会有一系列的模板文件对应不同的控制器和操作方法,并且支持动态设置模板目录。
– 中间件:中间件主要用于HTTP请求的拦截处理

## 自定义函数
D:\wamp\www\tp5.info\application\common.php

## 控制器
控制器文件通常放在application/module/controller下面,类名和文件名保持大小写一致,并采用驼峰命名(首字母大写)。

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
<?php
namespace app\index\controller;

use think\Controller;

class Index extends Controller
{
    public function index()
    {
        return 'index';
    }
}

渲染输出:

Select Code
1
2
3
4
5
6
7
8
// 默认输出类型设置为JSON 'default_return_type'    => 'json',
return ['name' => 'thinkphp', 'time' => date('Y-m-d H:i:s')]; //输出JSON
// 输出字符串
return 'hello world';
// 输出json
return json(['name' => 'thinkphp', 'time' => date('Y-m-d H:i:s')]);
// 渲染模板输出
return view();  // 指向模板\index\view\index\hello.html

内置了两个跳转方法success和error,用于页面跳转提示。

控制器中间件

## 请求
当前的请求对象由think\Request类负责,在很多场合下并不需要实例化调用,通常使用依赖注入即可。在其它场合(例如模板输出等)则可以使用think\facade\Request静态类操作。

– 构造器注入
– 操作方法依赖注入
– 通过Facade机制来静态调用请求对象的方法
– request助手函数,可以在任何需要的时候直接调用当前请求对象

请求信息:

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
dump($request->host());
dump($request->port());
dump($request->url());
dump($request->pathinfo());
dump($request->param());
dump($request->type());
dump($request->method());
echo '<hr>';
dump(\think\facade\Request::url());
dump(\think\facade\Request::pathinfo());
dump(\think\facade\Request::method());
dump(\think\facade\Request::param());
echo '<hr>';
halt($request);

变量信息:

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dump(\think\facade\Request::has('id'));
dump(\think\facade\Request::param('id'));
// 依赖注入
dump($request->id);
dump($request->param('id'));
dump($request->param('name', 'admin'));
dump($request->param('name', ''));
dump($request->param('name', '', 'strip_tags'));
dump($request->param());
// 助手函数
dump(input());
dump(input('?get.id'));
dump(input('param.id'));
dump(input('param.'));
dump(input('id'));

## 响应
返回字符串:
最简单的响应输出是直接在路由闭包或者控制器操作方法中返回一个字符串

返回JSON:
修改默认输出文件类型;
快捷方法:json()

返回XML:
xml()

渲染模板:
view()

页面重定向
redirect()

附件下载
download()

重定向传参,传入session:
return redirect(‘hello’)->with(‘name’,’thinkphp’);

## 视图
模板渲染:
//fetch方法
fetch(‘[模板文件]'[,’模板变量(数组)’])

//助手函数view
view(‘[模板文件]'[,’模板变量(数组)’])

//直接解析内容而不通过模板文件display
return $this->display($content, [‘name’ => ‘thinkphp’, ’email’ => ‘thinkphp@qq.com’]);

模板赋值:
//assign方法
//模板渲染时,传入数组
//公共模板变量赋值share

## 模板引擎
变量输出:
//字符串
{$name}
//数组
{$data.name} {$data[‘name’]}
//默认值
{$user.nickname|default=’这个家伙很懒’}
//系统变量输出
{$Think.server.script_name}
//请求变量
{$Request.param.id}

使用函数:
{$Request.param.id|md5}
{:Date(‘Y-m-d H:i:s’)}

运算符:
+ – * /
{$status? ‘正常’ : ‘错误’} //三元运算

原样输出:
{literal}{/literal}

模板注释:
{/* 注释内容 */ } 或 {// 注释内容 }

模板布局:
开启layout_on
//布局文件模板

Select Code
1
2
3
{include file="public/header" /}
{__CONTENT__}
{include file="public/footer" /}

//不开启设置文件模板
{layout name=”layout” /}

模板继承:
模板可以定义一个基础模板(或者是布局),并且其中定义相关的区块(block),然后继承(extend)该基础模板的子模板中就可以对基础模板中定义的区块进行重载。

//定义一个基础模板

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>{block name="title"}标题{/block}</title>
</head>
<body>
{block name="menu"}菜单{/block}
{block name="left"}左边分栏{/block}
{block name="main"}主内容{/block}
{block name="right"}右边分栏{/block}
{block name="footer"}底部{/block}
</body>
</html>

//继承模板
{extend name=”Public:base” /}

//包含文件
{include file=’模版文件1,模版文件2,…’ /}

//传入参数
{include file=”Public/header” title=”$title” keywords=”开源WEB开发框架” /}

内置标签:
– 循环 foreach, for, volist
– 比较
– 条件判断 switch, if, in/notin/between/notbetween, empty…
– 資源文件加載 load
– 原生PHP {php}…{/php}

如:

Select Code
1
2
3
{for start="0" end="10" step="2"}
    {$i},
{/for}

## 调试
– 开启调试模式 ‘app_debug’ => true,
– APP_DEBUG = true
– 开启Trace调试 ‘app_trace’ => true,
– SQL:getLastSql()
– 查看变量 halt(), dump()

## 数据库连接
数据库配置文件 config\database.php

一、查找数据
//think\Db类查找

Select Code
1
2
3
4
5
6
7
8
9
10
// table不能省略表前缀
/*$user = Db::table('tp_user')->find();   //SELECT * FROM `tp_user` LIMIT 1
$user = Db::table('tp_user')->select(); //SELECT * FROM `tp_user`
$user = Db::table('tp_user')->where('id', 1)->find();   //查找不到则返回null
try {
$user = Db::table('tp_user')->where('id', 1)->findOrFail(); //查找不到则抛出异常
//SELECT * FROM `tp_user` WHERE `id` = 1 LIMIT 1
} catch (DataNotFoundException $exception) {
echo $exception->getMessage();
}

// name可以省略表前缀

Select Code
1
2
3
4
5
6
7
8
9
10
11
$user = Db::name('user')->where(['status' => 1])->find();   //SELECT * FROM `tp_user` WHERE `status` = 1 LIMIT 1
//查找某个字段的值
$user = Db::name('user')->where(['id' => 2])->value('name');    //SELECT `name` FROM `tp_user` WHERE `id` = 2
$user = Db::name('user')->where('status', 1)->column('name');   //SELECT `name` FROM `tp_user` WHERE `status` = 1*/
$user = Db::name('user')->where('status', 1)->column('*', 'name');   //指定name字段的值作为索引 返回所有数据
// 分批量处理,chunk方法一般用于命令行操作批处理数据库的数据
Db::name('user')->where('status', 1)->chunk(5, function ($users) {
	foreach ($users as $user) {
		...
	}
});

//db()辅助方法查找

Select Code
1
//$user = db('user')->find(); //SELECT * FROM `tp_user` LIMIT 1

二、新增数据、批量添加数据:

Select Code
1
2
3
4
5
6
7
8
9
$data = ['name' => 'La', 'age' => mt_rand(10, 50)];
$res = Db::name('user')->insert($data); //INSERT INTO `tp_user` (`name` , `age`) VALUES ('La' , 19)
$res = Db::name('user')->insertGetId($data);    //添加数据后如果需要返回新增数据的自增主键
$data = [
    ['name' => 'La', 'age' => mt_rand(10, 50)],
    ['name' => 'Kobe', 'age' => mt_rand(10, 50)],
    ['name' => 'ONil', 'age' => mt_rand(10, 50)],
];
$res = Db::name('user')->insertAll($data);  //INSERT INTO `tp_user` (`name` , `age`) VALUES ( 'La',14 ) , ( 'Kobe',35 ) , ( 'ONil',16 )

三、更新数据

Select Code
1
2
3
4
5
6
7
8
9
10
dump(Db::name('user')->where('id', 17)->update(['name' => 'think_2018']));
//UPDATE `tp_user` SET `name` = 'think_2018' WHERE `id` = 17
dump(Db::name('user')->update(['name' => 'think_2017', 'id' => 16]));   //和上面等同
//UPDATE `tp_user` SET `name` = 'think_2017' WHERE `id` = 16
dump(Db::name('user')->where('id', 17)->setField('name', 'think_php')); //修改某个字段
//UPDATE `tp_user` SET `name` = 'think_php' WHERE `id` = 17
dump(Db::name('user')->where('id', 17)->setInc('age'));
//UPDATE `tp_user` SET `age` = `age` + 1 WHERE `id` = 17
dump(Db::name('user')->where('id', 16)->setDec('age'));
//UPDATE `tp_user` SET `age` = `age` - 1 WHERE `id` = 16

四、删除数据

Select Code
1
2
3
4
5
6
7
8
9
10
//根据主键删除
dump(Db::name('user')->delete(17));
//DELETE FROM `tp_user` WHERE `id` = 17
dump(Db::name('user')->delete([15, 16]));
//DELETE FROM `tp_user` WHERE `id` IN (15,16)
//条件删除
dump(Db::name('user')->where('id', 14)->delete());
//DELETE FROM `tp_user` WHERE `id` = 14
dump(Db::name('user')->where('id', '>', 13)->delete());
//DELETE FROM `tp_user` WHERE `id` > 13

五、链式操作

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$res = Db::name('user')
    ->where('status', 1)
    ->order('create_time')
    ->limit(5)
    ->select();
//SELECT * FROM `tp_user` WHERE `status` = 1 ORDER BY `create_time` LIMIT 5

$res = Db::name('user')
    ->page(2, 5)
    ->select();
//SELECT * FROM `tp_user` LIMIT 5,5

$res = Db::table('tp_user')
    ->field('id, name, age')
    ->group('age')
    ->having('id>10')
    ->fetchSql(true)
    ->select();
//SELECT `id`,`name`,`age` FROM `tp_user` GROUP BY `age` HAVING id>10

// alias, field, join, union, distinct, lock, cache, comment, fetchSql, partition

六、链式查询

Select Code
1
2
3
4
5
6
7
8
9
10
$res = Db::name('user')->fetchSql(true)->count();
//SELECT COUNT(*) AS tp_count FROM `tp_user` LIMIT 1
$res = Db::name('user')->fetchSql(true)->count('id');
//SELECT COUNT(`id`) AS tp_count FROM `tp_user` LIMIT 1
$res = Db::name('user')->fetchSql(true)->max('age');
//SELECT MAX(`age`) AS tp_max FROM `tp_user` LIMIT 1
$res = Db::name('user')->fetchSql(true)->avg('age');
//SELECT AVG(`age`) AS tp_avg FROM `tp_user` LIMIT 1
$res = Db::name('user')->fetchSql(true)->sum('age');
//SELECT SUM(`age`) AS tp_sum FROM `tp_user` LIMIT 1

## 模型
定义一个模型

Select Code
1
2
3
4
5
6
7
8
9
10
<?php

namespace app\common\model;

use think\Model;

class User extends Model
{

}

模型设置:
默认主键为id;//protected $pk = ‘id’;

一、新增数据
新增数据的最佳实践原则:使用create方法新增数据,使用saveAll批量新增数据。

Select Code
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
$User = new User();
// 1. 实例化对象,赋值并保存
$User->name = 'LuLu';
$User->age = mt_rand(1, 99);
//INSERT INTO `tp_user` (`name` , `age`) VALUES ('LuLu' , 68)

// 2. 直接传入数据到save方法批量赋值
dump($User->save(['name' => 'Mai', 'age' => mt_rand(10, 30)])); 
//INSERT INTO `tp_user` (`name` , `age`) VALUES ('Mai' , 17)

// 3. 直接在实例化的时候传入数据
dump((new User(['name' => 'Yao', 'age' => mt_rand(20, 30)]))->save());  
// INSERT INTO `tp_user` (`name` , `age`) VALUES ('Yao' , 23)

// 新增多条数据,saveAll方法新增数据返回的是包含新增模型(带自增ID)的数据集对象。
dump($User->saveAll([
    ['name' => 'Tom', 'age' => mt_rand(1, 5)],
    ['name' => 'Jack', 'age' => mt_rand(1, 8)],
]));
//[ SQL ] INSERT INTO `tp_user` (`name` , `age`) VALUES ('Tom' , 3)
//[ SQL ] INSERT INTO `tp_user` (`name` , `age`) VALUES ('Jack' , 3)

// create方法:创建并写入,返回当前模型的对象实例
$res = User::create(['name' => 'Bob', 'age' => mt_rand(1, 5)]);
// INSERT INTO `tp_user` (`name` , `age`) VALUES ('Bob' , 5)

二、更新数据
更新的最佳实践原则是:如果需要使用模型事件,那么就先查询后更新,如果不需要使用事件,直接使用静态的Update方法进行条件更新,如非必要,尽量不要使用批量更新。

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//查找并更新
$user = User::get(2);
$user->name = '3bangz';
dump($user->save());
//UPDATE `tp_user` SET `name` = '3bangz' WHERE `id` = 2

//直接更新数据
$user = new User;
dump($user->save(['name' => 'update'], ['id' => 2]));
//UPDATE `tp_user` SET `name` = 'update' WHERE `id` = 2

//静态方法更新数据
User::where('id', 2)->update(['name' => 'after update']);
//UPDATE `tp_user` SET `name` = 'after update' WHERE `id` = 2

三、查找
模型查询的最佳实践原则是:在模型外部使用静态方法进行查询,内部使用动态方法查询,包括使用数据库的查询构造器。模型的查询始终返回对象实例,但可以和数组一样使用。

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//查询构造器查找并更新
$user = User::where('age', '>', 50)->find();    //SELECT * FROM `tp_user` WHERE `age` > 50 LIMIT 1
if ($user) {
    $user->age = $user->age - 1;
    dump($user->save());
    // UPDATE `tp_user` SET `age` = 57 WHERE `id` = 8
}
dump(User::where('id', 1)->find()); //返回值为null
try{
    dump(User::where('id', 1)->findOrFail());
} catch (ModelNotFoundException $exception) {
    echo $exception->getMessage();
}
//查找不到时抛出异常信息 model data Not Found:app\common\model\User
// SELECT * FROM `tp_user` WHERE `id` = 1 LIMIT 1

//根据主键获取多条信息
$users = User::all('1,2,3');    //SELECT * FROM `tp_user` WHERE `id` IN (1,2,3)
$users = User::all([1,2,3]);    //SELECT * FROM `tp_user` WHERE `id` IN (1,2,3)
dump($users);
//查询构造器,查询
$users = User::where('age', '>', 60)->select(); //SELECT * FROM `tp_user` WHERE `age` > 60
dump($users);

四、删除
删除的最佳实践原则是:如果删除当前模型数据,用delete方法,如果需要直接删除数据,使用destroy静态方法。

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//删除当前模型
/*$user = User::get(3);
dump($user->delete());*/
//SELECT * FROM `tp_user` WHERE `id` = 3 LIMIT 1
//DELETE FROM `tp_user` WHERE `id` = 3

//静态方法,根据主键删除
dump(User::destroy('5,6,7'));
//SELECT * FROM `tp_user` WHERE `id` IN (2,3,4)
//DELETE FROM `tp_user` WHERE `id` = 4

//闭包函数删除
User::destroy(function ($query) {
	$query->where('id', '<', 10);
});
//SELECT * FROM `tp_user` WHERE `id` < 10

dump(User::where('id', '<', 10)->delete()); //DELETE FROM `tp_user` WHERE `id` < 10