ThinkPHP小结

前言

框架只是工具,无需在意版本,纠结用法,只要工具喜欢,精通一个,仍能做成一个精美的软件。

规则

目录名:小写+下划线

类名:首字母大写,驼峰,都.php后缀

函数/方法/属性:首字母小写,驼峰

控制器

可继承BaseController, 已经注入了think\app , think\Request.

空控制器

单应用下:创建Error控制器,index方法。默认报错到这里,后面自行输出报错。

可以在config/app.php文件定义 HTTP异常页面:

'http_exception_template' => [
    // 定义404错误的重定向页面地址
    404 => \think\facade\App::getRootPath() . 'public\404.html',
    // 还可以定义其它的HTTP status
    401 => \think\facade\App::getRootPath() . 'public\401.html'
],
index控制器

输出状态码,自动跳到配的错误页。

namespace app\controller;
use app\BaseController;

Class Error extends BaseController{
    public function index()
    {
        throw new HTTPException(404);
    }
}

数据库

配置

​ 测试:.env文件

​ 部署:在 config/databases.php里写,还可以配置多个数据库

连接

​ table: 写完整表名 , name: 需配置前缀 prefix ,然后忽略前缀写

$res = Db::connect('数据库名')->table('完成表名')->select();
$res = Db::connect('数据库名')->table('表名')->select();
return json($res);
查询

单条:find findOrFail, findOrEmpty ( 有查询到就显示一条没有就显示空,相较于find用这个吧 ), 需加上where

Db::table('tp_user')->where('id', '>', '1')->findOrEmpty()

多条: select, selectOrFail(), toArray(), column()

$res = Db::table('tp_user')->selectOrFail()->column('usernanme', 'id');//查指定字段
if (!$res->isEmpty()) {
    $res = $res->toArray();
}

分批查:chunk 避免大数据一次性读取有可能会导致内存开销过大

Db::table('tp_user')->chunk(3, function(){
    foreach($users as $user) {
        dump($user);
    }
})
保留对象查

长连接,节约资源,注意要用Db::name不能用Db::table, 下次查询加上 removeOption()

$Db = Db::name('user');

$res1 = $Db->selectOrFail()->toArray();
$res2 = $Db->removeOption()
    	   ->selectOrFail()->toArray();

insert,save, insertGetId, insertAll

public function addDataToSql()
{
    $dbObj = Db::table('tp_user');
    //单数据
    $data = [
        'username'  => '辉夜',
        'password'  => '123',
        'gender'    => '女',
        'email'     => 'huiye@163.com',
        'price'     => 90,
        'details'   => '123'
    ];
    //成功返回: 1
    echo $dbObj->insert($data);
    echo $dbObj->save($data);
    //成功返回: ID
    echo $dbObj->removeOption()->insertGetId($data);
    //若不存在的字段数据,仍强插
    echo $dbObj->removeOption()->strict('false')->insert($data);

    //多数据
    $dataArr = [
        [
            'username' => '辉夜',
            'password' => '123',
            'gender' => '女',
            'email' => 'huiye@163.com',
            'price' => 90,
            'details' => '123'
        ],
        [
            'username' => '辉夜',
            'password' => '123',
            'gender' => '女',
            'email' => 'huiye@163.com',
            'price' => 90,
            'details' => '123'
        ]
    ];
    echo Db::table('tp_user')->insertAll($dataArr);
}

delete

//删除数据: delete 删了(影响)几行回复几个
public function deleteData() {
    $dbQuery = Db::name('user');
    echo $dbQuery->delete('309');
    echo $dbQuery->removeOption()->delete([307,308,306]);
    echo $dbQuery->removeOption()->where('id', '=', '301')->delete();
}

update

public function updateDate()
{
    $dbQuery = Db::name('user');
    $upName = [
        'username' => '李白'
    ];
    //成功1,未修改0
    echo $dbQuery->where('id','=','306')->update($upName);

    //自增/自减步长, mysql的函数等等
    echo $dbQuery->removeOption()->where('id','=','29')
        ->update([
            'email' => Db::raw('UPPER(email)'),
            'price' => Db::raw('price + 1'),
            'uid'   => Db::raw('uid   - 2'),
            'status'=> Db::raw('11')
        ]);
}
基本查
//查询表达式 =, !=, > , < , >=, <= , Between , Like, In, Null
//自行拼装: exp
//whereLike , whereNotLike,  whereBetween ,whereNotBetween, whereIn, whereNotIn, whereNull, whereNotNull
public function selectTypeMonth() {
    $dbQuery = Db::name('user');

    dump($dbQuery->where('id','<>', '1')->select()->toArray());
    //组合
    dump($dbQuery->removeOption()->where('email','like',['wu%','LIBA'], 'or')->select()->toArray());
    //whereNotLike
    dump($dbQuery->removeOption()->whereLike('email', 'wu%')->select()->toArray());
    //whereNotBetween
    dump($dbQuery->removeOption()->whereBetween('id','19,20')->select()->toArray());
    //whereNotIn
    dump($dbQuery->removeOption()->whereIn('id','19,20,21')->select()->toArray());
    //whereNotNull
    dump($dbQuery->removeOption()->whereNull('email')->select()->toArray());

    //自行拼装
    dump($dbQuery->removeOption()->where('email', 'exp', 'like \'wu%\' or \'LI%\' ')->select()->toArray());
}
聚合查询

系统提供更方便的列计算工具 count(), max, min, avg, sum

public function poly() {
    //所以数据数量, 若指定字段: 用户表有填写邮箱的总数,nulL则忽略。
    $res = Db::name('user')->count();
    $res = Db::name('user')->count('email');

    //字段最大值(强制转换false)
    $maxVal = Db::name('user')->max('price', false);
    //字段最小值
    $minVal = Db::name('user')->min('uid');
    //字段平均值
    $midVal = Db::name('user')->avg('id');
    //字段总和
    $tolVal = Db::name('user')->sum('id');

    dump($tolVal);
}
子查询

buildSql() 创建子查询语句

public function sonSearch()
{
    //在two表中查出男性的UID,在one表中查询男性的信息
    $buildSQlVal = Db::name('two')->field('uid')->where('gender', '男')->buildSql();

    $resSQl = Db::name('one')->where('id','exp', 'IN '. $buildSQlVal)
        		->select()->toArray();

    dump($resSQl);
}
原生查询

query(仅查询), execute (表操作)

Db::query()

Db::execute()

链式查询(1)

where , field, alisa

public function uplink() {
    //建议都这样写,条件清晰,最终组合至where里去
    $map[] = ['gender', '=', '男'];
    $map[] = ['price' , '>', '100'];
    //where: 条件
    $res = Db::table('tp_user')->where($map)->select()->toArray();

    //field: 指定查询字段. 可取别名、设置函数等。
    $fieldSql = Db::table('tp_user')
                    ->field('id, SUM(price), gender as sex')
                    ->where($map)->select()->toArray();
    //也可直接without(不要的字段列表)
    $fieldSql1 = Db::table('tp_user')->withoutField('id, price, sex')->select()->toArray();

    //alisa: 取别名
    $alisa = Db::name('user')->alias('a')->select();

    dump($fieldSql1);
}
链式查询(2)

limit , page, order, group, having

public function uplink2() {
    //限制
    $res = Db::table('tp_user')->limit(5)->select()->toArray();
    //分页用page(哪一页,一页几条)
    $res = Db::table('tp_user')->page(0,5)->order('id','ASC')->select()->toArray();
    //排序 支持数组写入
    $res = Db::table('tp_user')->order([ 'price'=>'desc', 'id' => 'asc'])->select()->toArray();
    //Mysql函数排序 orderRow: 按username排序的同时把 username为黑崎一护的行 始终排到第一行
    $res = Db::table('tp_user')->orderRaw('FIELD(username, "黑崎一护") DESC')->select()->toArray();
    //分组: 分别计算男,女的价格/成绩/积分等等总和, 是分别哦 group
    $res = Db::table('tp_user')->field('gender, SUM(price)')->group('gender')->select()->toArray();
    //having: 在分组之后的再筛选: 分别计算男,女的价格/成绩/积分等等总和,并且只统计存在大于100分的价格/成绩/积分才统计。
    $res = Db::table('tp_user')->field('gender, SUM(price) as a')->group('gender')->having('a > 100')->select()->toArray();

    dump($res);
}
WhereRaw

组合查询:特简单

$map作为where条件的用法
1$map1 = [
        ['username', 'like', '%X%'],
        ['email', 'like', '%xx'],
    ]
    $map2 = [
        ['username', 'like', '%X%'],
        ['email', 'like', '%xx'],
    ]
    Db::table(xx)->whereOr($map1, $map2);
        SELECT * FROM `tp_user` WHERE 
            ( `username` LIKE '%小%' AND `email` LIKE '%163%' ) 
        OR 
            ( `username` LIKE '%孙%%' AND `email` LIKE '%com%' )
2:
	$map[] = ['id', '1'];
	$map[] = ['price', 'exp', Db::raw('>100')];
	$map[] = ['create_time', 'between', ['2016-06-27 16:45:26', '2023-01-01 01:01:01']];
	Db::table(xx)->where($map)->xxx;

3. WhereRaw直接写条件
    Db::table(xx)->whereRow("( `username` LIKE '%小%' AND `email` LIKE '%163%' ) OR ( `username` LIKE '%孙%%' AND `email` LIKE '%com%' )");
WhereColumn

比较字段的值

xxx -> whereColumn('update_time' , '>', 'create_time')->xxx
事务
Db::StartTrans();
try {
    //支付他三块钱
    Db::table(xx)->where('id','1')->update(['price' => Db::raw('price + 3')]);
    //调用API,确认已经收到三块钱。
    Db::table(xx)->where('id','2')->update(['price' => Db::raw('price - 3')]);
    //确认其他逻辑
    Db::commit();
} catch (\Exception $e) {
    Db::rollback();
    Throw new HTTPException(404);
}

模型

遵循mvc,尽量在model层操作数据。

说明

模型会自动对应数据表

​ 配置前缀prefix, 创建对应数据库表tp_user,即可在app目录下新建 model 文件夹,User.php类。

​ 操作与前面数据库没太大差别

属性
//软删除与字段
use SoftDelete;
protected $deleteTime = 'delete_time';

//表名
protected $name = 'user';

//主键
protected $pk   = 'id';

//模式
protected $strict = 'true';

//json字段:特殊的要写出来
protected $json = ['list'];

//只读的字段: 无法被修改的字段
protected $readonly = ['id', 'uid'];

//忽略字段: 当某个字段在开发项目版本升级中不再使用
protected $disuse = ['notUseColumn'];

//强制定义类型: 后面控制器不用麻烦数据格式,查出来就自动转格式
protected $type = [
    'price' => 'integer',
    'status'=> 'boolean'
];

//初始化
protected static function init()
{
    parent::init(); // TODO: Change the autogenerated stub
    echo "init中,初始化User模型" . "<hr/>";
}

create,saveAll

public function saveAllData(array $data)
{
    return self::create($data);
    return $this->saveAll($data);
}

一条数据就找到对应的数据集后操作

$changeRow = self::where($option, $optVal)->find();
return $changeRow->save($data);

更新全部数据, 必须含有主键ID

$changeDataArr = [
    [
        'id'        => '320',
        'username'  => '白 + 黑',
        'email'     => 'test@qq.com',
        'price'     => '999'
    ],
    [
        'id'        => '322',
        'username'  => '白 + 黑',
        'email'     => 'test@qq.com',
        'price'     => '999'
    ],
];
return $this->saveAll($changeDataArr);

条件删一条或直接传主键数组删多个

return self::where('id', '=', $id)->delete();
return self::destroy($idArr);
软删除

很有用,例如关联表,A表硬删除了B表就关联不到了.

就不是真的删,就是数据库多个字段记录它被删了。

现要在model属性设置 软删除与字段

use SoftDelete;
protected $deleteTime = ‘delete_time’;

return self::destroy($id);

带上被删除的数据显示
return self::withTrashed()->select();
public function searchByOpt($opt)
{
    $res = self::where($opt)->select();
    if (!$res->isEmpty()) {
        return $res->toArray();
    } else {
        return false;
    }
}
Json

​ json字段处理: 都需要特别在链式指出 在模型层的属性指定

控制器端:

public function jsonFin()
{
    $User = new TestUserModel();
    $data = [
        'username' => 'vechis',
        'password' => 'wwa',
        'gender'   => '男',
        'email'    => 'vechis@163.com',
        'price'    => '1000',
        'details'  => '312',
        'list'     => ['username'=>'辉夜', 'gender'=>'女', 'email'=>'huiye@163.com']
    ];
    //增json数据
    dump($User->json(['list'])->save($data));
    //更新json数据
    dump($User->where('id', '326')->json(['list'])->update($data));
    //查询json数据
    dump($User->where('id','326')->json(['list'])->select()->toArray());

}
获取器

就是把取出来的数据在返回之前自动处理。比如数据库存字段tinyint, 就存0,1,2,3,取得时候自带原有的中文

规则: get + 字段名(驼峰) + Attr

/**
   * 获取器
   * @param $value
   * @return string
*/
public function getStatusAttr($value)
{
    $status = [-1=>'删除', 0=>'禁用', 1=>'正常', 2=>'待审核'];
    return $status[$value];
}
修改器

从控制层到模型层的数据,经过模型层写的修改器方法,将要修改的数据自动处理

规则: set+ 字段名(驼峰) + Attr

控制器:

public function setToModel()
{
    $changeData = [
        'id' => '323',
        'gender' => '女',
        'username' => 'shuiy',
        'email' => 'xxx@dds.com'
    ];
    $User = new TestUserModel();
    $User->updateOne('id','323',$changeData);
}

模型层:

/**
 * 修改器
 * @param $value
 * @return string
 */
public function setEmailAttr($value)
{
    return strtoupper($value);
}
事件

改查的时候,模型层额外记录这个事儿的一些操作。

protected的,静态的方法, on+ 固定的事件名 , $query

删除后: after_delete

protected static function onAfterDelete($query) {
	//获取请求的ip地址
    $ip = request()->ip();
    //谁删的
    $no = Session::get('Userno');
    //执行
    echo "工号" . $no . "IP为" . $ip . ”执行了语句“ . $query;
}

其他:

​ 查询后:after_read

​ 新增后: after_insert

​ 更新后:after_update

关联

父子表必须含有外键连在一起。

父表在model写个函数声明, 即可在控制器中直接使用

主要根据数据库表设计的三大范式,分为父表和多个子表,引申现在的关联。

目的:能根据父表统一操作子表数据,以便完成莫须有的方便。

一对一
父表 1条数据 对应 子表 1条数据

hasOne

public function profile()
{
    // 					          子表模型类, 外键,子表主键
    return $this->hasOne(Profile::class, 'user_id', 'id');
}

控制器端

public function oneByoneFromUser()
{
    $User = new UserFather();
    //查
    dump($User->where('id','20')->findOrEmpty()->profile->hobby);
    //增
    dump($User->where('id','20')->findOrEmpty()->profile()->save([
        'hobby' => '特爱吃肉2',
        'status' => 3
    ]));
    //改
    dump($User->where('id','20')->findOrEmpty()->profile()->update([
        'hobby' => '不爱吃肉',
        'status' => '3'
    ]));
    //删
    dump($User->where('id', '326')->findOrEmpty()->profile()->delete());
}
一对多

hasMany

​ 意思是父表对应子表中,有多条数据。

模型端:

public function intellInfo()
{
    return $this->hasMany(IntellInfo::class, 'Info_id', 'id');
}

控制器端

public function oneByManyFromIntell()
{
    $Intell = new Intell();
    //增
    dump($Intell->where('id', '4')->findOrEmpty()->intellInfo()->saveAll([
        ['user' => '卿', 'dept' => 'IT', 'type' => '6', 'apply_time' => '2023-01-01', 'acc_time' => '2023-10-05'],
        ['user' => '宋', 'dept' => 'IT', 'type' => '7', 'apply_time' => '2023-02-01', 'acc_time' => '2023-10-05']
    ]));
    //删
    dump($Intell->where('id','4')->findOrEmpty()->intellInfo()->delete(30));
    //改
    dump($Intell->where('id','4')->findOrEmpty()->intellInfo()->where('id','31')->update(['user'=>'Huang']));
    //查
    dump($Intell->with(['IntellInfo', 'IntellReward'])->where('id','4')->select()->toArray());
}
多对多

​ 直接用join,别费事。

关联预加载

​ 不懂为啥叫的这么高级,其实就是为了节约在关联的时候查询多的资源,一开始想好要掉子表的时候,加个with关键字即可。

	$list = Intell::whereRaw('id in (1,2,3)')->select();
=>
    $list = Intell::with(['IntellInfo', 'IntellReward'])->where.....

中间件

就是一种:用户请求和控制器之间的处理机制。

可拦截和过滤HTTP 请求,URL 重定向、权限验证等。

创建
php think make:middleware Check
基本

​ 文件:app - middleware - Check.php

必须返回:Response对象

​ 根据传入的$request对象进行各种操作

前置中间件:在拿到 n e x t ( next( next(request)之的操作

后置中间件:在拿到 n e x t ( next( next(request)之的操作

结束: 中间件结束后会执行的操作。 加个end函数。

public function handle($request, \Closure $next)
{
    $token = 5;
    if ($request->param('token') == $token) {
        echo "鉴权ok" . "<br/>";
    }
    echo "前置中间件" . "<br/>";

    $response =  $next($request);

    echo "后置中间件" . "<br/>";

    return $response;
}

public function end(Response $response)
{
    //中间件结束执行
    dump($response->getHeader());
}
使用

控制器端

​ 直接定义属性。其中属性还可以细化哪个方法使用,不给哪个方法使用。

protected $middleware = ['app\middleware\Check'];
或者
protected $middleware = [
   'app\middleware\Check' => ['only'   => ['控制器的方法1''控制器的方法2']]'app\middleware\Auth' => ['except' => ['控制器的方法3']]
];

路由

​ 尾部加个中间件类即可

Route::rule('test', 'XXXX@YYY')->middleware(app\middleware\Check::class, "可以传值")
    
 =》中间件的handle方法中加个参数就可以拿到 
    public function handle($request, \Closure $next, $params)
    

事件

说个:事件监听类, 可以写个日志啥的比较方便

创建
php think make:listener TestListen
基本

​ 文件:app - listener- TestListen.php

​ TestListen类: 可以做这些事:

public function handle($event)
{
    dump("监听了XXX事件,根据session获取用户名,写入日志" . $event);
}
配置

app/even文件

​ 如下:添加一个自定义监听的value,绑定刚创建的TestListen类

'listen'    => [
    'AppInit'  => [],
    'HttpRun'  => [],
    'HttpEnd'  => [],
    'LogLevel' => [],
    'LogWrite' => [],
    'TestListener' => [\app\listener\TestListener::class]
],
使用

控制器直接调用:

Event::trigger('TestListener');

或者直接在配置文件中 'HttpRun’绑定TestListen类,这样每个http请求在执行时候都会执行TestListen类

'HttpRun'  => [\app\listener\TestListener::class],

验证器

就是验证各个数据是否匹配框架自带的各种验证工具

定义传入的参数,错误的提示信息

验证速查
格式验证类
    'field' => 'require', 	//不得为空::isRequire 或::must
    'field' => 'number', 	//是否是纯数字,非负数非小数点
    'field' => 'integer', 	//是否是整数
    'field' => 'float', 	//是否是浮点数
    'field' => 'boolean', //是否是布尔值,或者bool
    'field' => 'email', //是否是email
    'field' => 'array', 		//是否是数组
    'field' => 'accepted', //是否是“yes”“no”“1”这三个值
    'field' => 'date', 		//是否是有效日期
    'field' => 'alpha', 	//是否是纯字母
    'field' => 'alphaNum', //是否是字母和数字
    'field' => 'alphaDash', //是否是字母和数字以及_-(下划线和破折号)
    'field' => 'chs', //是否是纯汉字
    'field' => 'chsAlpha', //是否是汉字字母
    'field' => 'chsAlphaNum', //是否是汉字字母数字
    'field' => 'chsDash', //是否是汉字字母数字以及_-(下划线和破折号)
    'field' => 'cntrl', //是否是控制字符(换行、缩进、空格)
    'field' => 'graph', //是否是可打印字符(空格除外)
    'field' => 'print', //是否是可打印字符(包含空格)
    'field' => 'lower', //是否是小写字符
    'field' => 'upper', //是否是大写字符
    'field' => 'space', //是否是空白字符
    'field' => 'xdigit', //是否是十六进制
    'field' => 'activeUrl', //是否是有效域名或IP 地址
    'field' => 'url', //是否是有效URL 地址
    'field' => 'ip', //是否是有效IP(支持ipv4,ipv6)
    'field' => 'dateFormat:Y-m-d', //是否是指定日期格式
    'field' => 'mobile', //是否是有效手机
    'field' => 'idCard', //是否是有效身份证
    'field' => 'macAddr', //是否是有效MAC 地址
长度和区间验证类
	'field' => 'in:1,2,3', //是否是有某个值
    'field' => 'notIn:1,2,3', //是否是没有某个值
    'field' => 'between:1,100', //是否是在区间中
    'field' => 'notBetween:1,100', //是否是不在区间中
    'field' => 'length:2,20', //是否字符长度在范围中
    'field' => 'length:4', //是否字符长度匹配
    'field' => 'max:20', //是否字符最大长度
    'field' => 'min:5', //是否字符最小长度
    'field' => 'after:2020-1-1', //是否在指定日期之后
    'field' => 'before:2020-1-1', //是否在指定日期之前
    //是否在当前操作是否在某个有效期内
    'field' => 'expire:2019-1-1,2020-1-1',
    //验证当前请求的IP 是否在某个范围之间,
    'field' => 'allowIp:221.221.78.1, 192.168.0.1',
    //验证当前请求的IP 是否被禁用
    'field' => 'denyIp:221.221.78.1, 127.0.0.1',
其它验证类:
    'field' => '\d{6}', //正则表达式验证
    'field' => 'regex:\d{6}', //正则表达式验证
    'field' => 'file', //判断是否是上传文件
    'field' => 'image:150,150,gif', //判断图片(参数可选)
    'field' => 'fileExt:jpg,txt', //判断文件允许的后缀
    'field' => 'fileMime:text/html',//判断文件允许的文件类型
    'field' => 'fileSize:2048', //判断文件允许的字节大小
单个

直接调用,返回bool

//字符串
$mailData = 'xx@xx.com';
dump(Validate::checkRule($mailData,'email|require|max:20|between:1,20'));

//日期
$date1 = '2023-01-01';
dump(Validate::checkRule($date1, 'after:2021-01-01|before:2025-01-01|dateFormat:Y-m-d|expire:2019-1-1,2020-1-1'));

//文件
$filePath = Request::file('file');
dump(Validate::checkRule($filePath, 'file|fileExt:txt|fileSize:1024|fileMime:text/plain'));
多个

控制器自定义小型验证器

public function verityNotWithValidate()
{
    $validate = Validate::rule([
        'name|用户名'     => 'require|max:20',
        'price|价格'      => 'number|between:1,100',
        'email|邮箱'      => 'email'
    ]);

    $validate->message([
        'name.require'  => '姓名不得为空',
        'name.max'      => '姓名不得大于20位',
        'price.number'  => '价格必须为数字',
        'price.between' => '价格必须在1-100的区间',
        'email.email'   => '必须符合email格式'
    ]);
	//batch: 显示全部验证错误
    $result = $validate->batch(true)->check([
        'name' => 'qqq',
        'price'=> '122',
        'email'=> 'vechis@154.com'
    ]);

    if (!$result) {
        dump($validate->getError());
    }
}
验证类

可以把所有字段和限制定义到一个数组

统一定义所有字段限制的错误信息

调用的时候简单,还可以指定scene(场景)去使用

同时可以自定义规则

创建
php think make:validate User
编写

文件 app - validate - User.php

填写三个属性

定义验证规则
protected $rule = [
        'name|用户名'     => 'require|max:20|checkName:xx',
        'price|价格'      => 'number|between:1,100',
        'email|邮箱'      => 'email',
        'id|序号'         => 'number|between:1,33'
    ];
定义错误信息
protected $message = [
    'name.require'  => '姓名不得为空',
    'name.max'      => '姓名不得大于20位',
    'price.number'  => '价格必须为数字',
    'price.between' => '价格必须在1-100的区间',
    'email.email'   => '必须符合email格式',
    'id.number'     => '必须为数字!'
];

自定义验证场景
//就是定义key=>要去验证哪些。这样可以在一个类写全部验证的东西,但可以分类哪个去验证哪些
protected $scene = [
    'ToVerify' => ['name', 'number'],
    'toRoute'  => ['id']
];
调用
//调用validate下User的类
//scene: 指定验证哪些
//batch: 开启全部都验证一遍,不开则有一条没验证通过就返回
public function verifyFromData()
{
    try {
        Validate(User::class)->batch(true)->scene('ToVerify')->check([
            'name' => 'ss',
            'price'=>'222',
            'email'=>'asd@vvv',
            'id'   =>'dd'
        ]);
    } catch (ValidateException $exception) {
        dump($exception->getError());
    }
}

路由

自定义url,可以传点参数,可以自己定义中间件/验证器,可以分组管理一个控制器的全部方法。

感觉有点用,但不多的样子。浅谈下。

参数

option: 参数

append: 追加参数

pattern: 正则匹配

cache: 缓存(仅对GET有效,返回304状态码)

ajax: 必须为ajax请求等等。。

domain: 域名

ext: 后缀

filter : 参数过滤

validate : 验证器

middleware: 中间件

Route::rule('aaaaaaaa/:id/:uid', function ($id, $uid) {
    echo $id . $uid;
}, 'POST')  ->cache(3600)
    		->ajax()
            ->option([
                'domain' => '10.10.200.27',                         //域名
                'ext'    => 'html',                                 //后缀必须为html
                'filter' => ['id' => '3', 'uid' => '9']             //传入参数限制
            ])
            ->validate([
                'id' => 'number|between:1,10',
                null,
                ['id.between' => 'id必须在1到10之间'],
                true
            ])
            ->middleware(\app\middleware\Auth::class, 'This is Params');
规则

设置全局涉及路由的参数都得这样匹配。 就是在文件前面设个固定匹配。

Route::pattern([
    'id' => '\d+',
    'uid'=> '\d+'
]);
或单个路由加单个验证 xxx... ->pattern(['id' => '\d+'....]);
基本

在config/route文件配置, 在route/app文件写实际路由。

=》 www.xxx.com/address/details

Route::rule('ad', 'Address/details', 'GET|POST');

=》 www.xxx.com/ad 访问,仅限get/post请求

MISS

匹配不到的时候自动到 Error控制器的Miss方法里

Route::miss('\app\controller\Error@miss');
不需要控制器和方法

闭包支持我们可以通过URL 直接执行,而不需要通过控制器和方法;

Route::rule('xx/:name/:id', function($param) {
   rerurn "This Param Use Route To this result" . $patam;
});
跨域

allowCrossDomain

Route::rule('test1' , '\app\controller\Test@Index')->allowCrossDomain([
    'Access-Control-Allow-Origin' => 'http://10.10.200.27:666' //闲没事干也可以指定域名访问
]);
分组(*)
Route::group('ad', function (){

    Route::rule('gi-:id', 'Address/getId');
    Route::rule('gn-:name', 'Address/getName');
    Route::rule('gu-:uid', 'Address/reToUid');

})->append(['status' => '1'])
  ->pattern(['id' => '\d+', 'name' => '\w+'])
  -> xxxxx.....;
真实地址

没做路由和做了路由,url傻傻分不清,直接在控制器用url(控制器/方法)->domain(true) 获取真实地址返回前端

public function index()
{
    //意思:根据当前控制器返回实际url,tp自动判断经过路由或没经过的最终实际路径。
    return url('Url/index')->domain(true);
}
验证器

​ 不建议在路由自己搞验证器

​ 老老实实

​ ->validate(\app\validate\XXX:class)

中间件

​ 不建议在路由自己搞中间件

​ 老老实实

​ ->middleware(\app\middleware\XXX:class)

注解路由

​ 不建议在注释中搞这个

要安装:composer require topthink/think-annotation

再引用:use think\annotation\Route;

/**

  • @param $id

  • @return string

  • @route(“details/:id”);

    */

请求 & 响应

常规简单。

讲一般开发中,前后端交互或者需要后台响应发送过来的请求。

以下什么依赖注入,门面facade, 容器绑定和实例化其实都在做一件事:

​ 为了节约资源,把要调用的类以各种形式实例化。管理类的实例化。

​ new是新建一个会有开销,依赖注入是从容器取对象,像连接池也是类似

public function __construct(\think\Request $request)
{
    $this->request = $request;
}

public function test()
{
    //facade
    $file1 = Request::param();

    //依赖注入
    $file2 = $this->request->param();

    //容器绑定
    $file3 = app('request')->param();

    $file1,$file2,$file3完全一样
}
依赖注入

官方: 依赖注入其实本质上是指对类的依赖通过构造器完成自动注入

有人理解: 框架帮我实例化了模型的对象并且作为参数传递进了构造方法这里

我的理解:控制器的构造方法中引入需要的类,指定一个变量接收,就完成了这个类的实例化。

public function __construct(\think\Request $request)
{
    $this->request = $request;
}
门面Facade

一种设计模式,为容器的类提供了一种静态的调用方式。

比如请求Request::?,路由Route::?,数据库Db::?等等,均来自Facade;

说下怎么做自己的门面

app -> 新建common目录,-> 新建 TestFacade.php文件 -> 写个简单的方法

namespace app\common;

class TestFacade
{
    public function hello($name)
    {
        return 'Hello !' . $name;
    }
}

app -> 新建facade目录 -> 新建同名的TestFacade.php文件 -> 继承门面,引入自写的门面

namespace app\facade;

use think\Facade;

class TestFacade extends Facade
{
    protected static function getFacadeClass()
    {
        //引入facade类,创建静态方法,指向自定义文件夹common里的XXX类
        //可在控制器端使用facade方式调用本方法 等同 => common里的XXX类
        return 'app\common\TestFacade';
    }
}

控制器调用

use app\facade\TestFacade;

dump(TestFacade::hello(1));
容器

bind() , app()

自己建个类,绑个类,app实例化

bind('test', 'app\model\One');
$test = app('test');
HTTPT头
//HTTP头: 是在HTTP请求和响应中传输的元数据,它们提供了关于请求或响应的额外信息,用于控制和管理HTTP通信。
/*
允许客户端和服务器之间交换关键信息,以确保请求和响应的正确处理和解释
1. **Host**:用于指定目标服务器的主机名和端口号。这是必需的,因为同一台服务器上可能托管多个网站。

2. **User-Agent**:包含了发送请求的用户代理(通常是浏览器)的信息。服务器可以使用这个头信息来适应不同的客户端类型。

3. **Accept**:指定客户端接受的内容类型,通常是MIME类型。服务器可以使用这个头信息来确定如何响应请求。

4. **Content-Type**:指定HTTP响应中实际内容的类型。这有助于客户端正确解释服务器返回的数据。

5. **Content-Length**:指定响应正文的长度,以字节为单位。这有助于客户端正确接收和处理响应。

6. **Authorization**:包含身份验证凭据,通常用于向服务器发送用户名和密码,以访问受保护的资源。

7. **Cookie**:包含客户端发送给服务器的cookie数据,用于在多个请求之间保持会话状态。

8. **Set-Cookie**:服务器在响应中设置的cookie信息,用于在客户端上维护会话状态。

9. **Location**:用于重定向客户端到另一个URL。通常在响应码为3xx的响应中使用。

10. **Cache-Control**:用于控制缓存的行为,指定是否可以缓存响应以及缓存的有效期等信息。

11. **Referer**:包含了引导用户到当前页面的URL,通常在跟踪和分析中使用。

12. **User-Agent**:用于识别发送请求的用户代理,有助于服务器适应不同的浏览器和设备。

13. **Date**:指定HTTP消息的创建日期和时间。

14. **Server**:包含服务器的信息,通常是服务器软件的名称和版本。

15. **ETag**:用于支持条件请求,客户端可以使用此标签来检查资源是否已更改。

16. **Accept-Encoding**:指定客户端接受的内容编码方式,服务器可以使用这个头信息来压缩响应内容以提高性能。

17. **Connection**:控制连接的行为,如是否保持持久连接。
*/
Request

请求,就是发送过来的请求, 进行接收

原生就是 G E T , _GET, GET,_POST[‘xxx’]

现在都用Request, 就是看下Request有啥方法,可以判断请求类型,请求的参数等等

//接收请求的控制器和方法
return Request::controller() . ' | ' . Request::action();

//          匹配指定参数, 没有参数的默认值, 参数匹配的规则
dump(Request::param('name', 'abc', 'htmlspecialchars'));

//匹配全部, 包含route里的字段
Request::param();

//头信息
Request::header();

//请求方法
Request::method();

请求缓存

仅对GET有效,在 app\middleware 文件解除注释

// 全局请求缓存
 \think\middleware\CheckRequestCache::class,

在 config/route中, 声明周期,默认为null

// 请求缓存有效期
'request_cache_expire'  => 3600,

当第二次访问时,会自动获取请求缓存的数据响应输出,并发送304 状态码;

Response

就是对请求做出回复

return , json(), view()

没啥,就是返回数据而已。

可以在返回的时候加状态码,响应头

return json($data)
    		->code('201')
    		->header(['Cache-control' => 'no-cache, must-recalidate'])
重定向

可以访问当前控制器的某个方法A后挑到另外的控制器的某个方法B去。

A跳过去的时候可以记住A的url, 在B的逻辑判断后可以跳回A。

redirect,还可以在跳的时候设置Session。

public function redirectA()
{
    if (Session::get('isRedirect') == 'yes') {
        return "已经跳转过了!";
    } else {
        return redirect(url('Redirect/redirectB'))->with('isRedirect', 'no')->remember();
    }
}

public function redirectB()
{
    if (Session::has('isRedirect')) {
        Session::set('isRedirect', 'yes');
        return redirect(Session::get('redirect_url'));
    } else {
        return "没有东西";
    }
}
上传文件

接收前端传入的文件请求,验证文件,保存文件,响应返回文件路径

<form action="Upload/uploadImage" enctype="multipart/form-data" method="post">
        <input type="file" name="image" multiple="multiple"><br>
        <input type="submit" value="确认">
</form>

响应

public function upload()
{
    $validate = Validate::rule([
        'image' => 'fileSize:1024000|fileExt:png,gif'
    ]);

    $res      = $validate->check([
        'image' => Request::file('image')	//接收input框中的name值=》image
    ]);

    if (!$res) {
        dump($validate->getError());
    }

    $info = [];
    $files = Request::file('image');
    foreach ($files as $file) {
        $info[] = Filesystem::putFile('topic', $file);
    }
    dump($info);
}

模板

得安装

composer require topthink/think-view
基础

新建View目录在 app下。 新建同名控制器的目录 Show , 新建同名控制器的方法Index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Test SHow</title>
</head>
<body>

hi, {$name} !

<?php
    echo $name;
?>

</body>
</html>

新建控制器Show

public function index()
{
    View::assign([
        'name'  => 'xx'
    ]);
    return View::fetch();
}

assign:赋值, fetch赋值

最后输出: Hi, xx xx

配置渲染

渲染其他的

public function useOther()
{
    //渲染其他控制器
    return View::fetch('Verify/showThis');
    //渲染多应用下其他控制器
    return View::fetch('admin@User/index');
    //渲染公共目录下的文件,要改配置
    View::config(['view_dir_name' => 'public']);
    return View::fetch('../public/test/index');
}
原生PHP

指定php引擎渲染,这样原本View的html转为php去渲染,都是用php原生语法输出

public function native()
{
    return View::engine('php')->fetch('index', [
        'name'  => 'Mr.xing'
    ]);
}
原生标签
{php}
    if($num1 != $num2){
        echo '不相等';
    } else {
        echo '相等';
    }
{/php}
变量输出/运算
public function output()
{
    $user = ['name' => 'xing', 'age' => '23', 'gender' => 'man'];
    View::assign([
        'user' => $user,
        'num'  => 4,
        'num1' => 9,
        'flag' => true,
        'str'  => 'iadoifmgiodbf',
        'date' => strtotime('-1 month'),
        'obj'  => $this
    ]);
    return View::fetch();
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模版的变量输出</title>
</head>
<body>


//数组                            <br>
{$user.name}

<hr>

//对象                            <br>
{$obj->method}

<hr>

//默认/系统输出                   <br>
{$user.salaer|default='未填写工资'}

//自带函数 md5, substr
默认值default, 不使用转义raw, 输出数组的第一个元素first, 输出数组的最后一个元素last, 转换为大写upper <br>
{$date|date='Y-m-d'}            <br>
{$str|substr=0,3}               <br>
{$user|first} - {$user|last}    <br>
{$num|md5}                      <br>
{$obj->method|raw}              <br>

//运算符 +,-,*,/,%,++,--         <br>
{$num1 % $num}

//三元运算符                      <br>
{$flag ? '正确' : '错误'}
{$num == $num1 ? '确实相等' : '假的'}
    
{eq name='$num1' value='$num2'}
    <span>相等</span>
   {else/}
    <p>不相等 = =</p>
{/eq}

</body>
</html>
循环/比较

foreach, for

<table class="table table-bordered table-hover">
    {foreach $list as $key => $val}
        {eq name='20' value="$val['id']"}
        <tr>
            <td>{$val['id']}</td>
            <td>{$val['username']}</td>
            <td>{$val['gender']}</td>
            <td>{$val['email']}</td>
            <td>{$val['price']}</td>
        </tr>
        {/eq}
    {/foreach}
</table>

{for start = '1' end = '100' step = '10' name = 'todo' }
 {$todo}
{/for}
加载

可以设置公共头部,尾部,直接在后续模板中以 include方式引入

还可以指定特别的变量

public function includeFile()
{
    View::assign([
        'title' => 'includeFile',
        'keywords' => 'Test Words!!'
    ]);
    return View::fetch();
}

公共头部放app/view/public/ header.html 与 foot.html, 特殊字换为[xxx]

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>[title]</title>
</head>
<body>
<p>[keywords]</p>

头部
<hr>

引入CSS,JS文件时,在 config/view文件,指定替换的关键字

// 模版替换输出
'tpl_replace_string' => [
    '__JS__' => '/static/js',
    '__CSS__' => '/static/css',
]

最后引入的文件就可以简化为:

{include file='public/header' title="$title" keywords="$keywords" }

	 <link rel="stylesheet" href="__CSS__/base.css">

		body.....

	<script src="__JS__/base.js"></script>

{include file='public/foot'}
令牌

就是自带的表单验证token,判断表单的内容有没有被篡改

在表单中新增一个input框, 特定name = token value={:token()}

{include file="public/header" title="$title" keywords = "$kw" }

<form action="/verify/token" method="post">
    <input type="text" name="username">
    <input type="text" name="__token__" value="{:token()}" style="width: 800px">
    <input type="submit" value="提交">
</form>

<script src="__JS__/base.js"></script>

{include file="public/foot"}

在控制器端用请求匹配接收和验证

$validate = Validate::rule([
    'name'  => 'require|token'
]);

$res = $validate->batch(true)->check([
    'name'          => Request::param('username'),
    '__token__'     => Request::param('__token__')
]);

if (!$res) {
    dump($validate->getError());
}

Cookie/Session/Cache

都大差不差 拿,存,取,删

Cookie

set, has ,get ,delete

public function setCookie()
{
    dump(Cookie::set('username', 'name'));
    dump(Cookie::has('username'));
    dump(Cookie::get('username') . "==" . Request::cookie('username'));
    dump(Cookie::delete('username'));
}
Session

set, has, get, delete

flash: 射一次出去了就清空 pull: 拿一次就删

all: 取全部 clear:清全部

Session::set('user', 'x');
dump(Session::has('user'));
dump(Session::get('user'));
dump(Session::delete('user'));
//拿一次就删, 删
Session::pull('user');

//设一次
Session::flash('psw', 'asdvedrg12312313sdv');

//全部取, 全部清
dump(Session::all());
dump(Session::clear());

Cache

tp使用文件缓存

set,has,get, clear

tag: 设置分组标签

Cache::tag('UserCache')->set('age', '30');
Cache::tag('UserCache')->set('sex', '0');
Cache::clear('UserCache');

其他

异常

throw new Exception(…)

HTTP状态码

根据实际错误输出不同HTTP状态码跳转到不同得错误页面

throw new HttpException(404

在config/app配置

// HTTP 异常页面的模板文件
'http_exception_template' => [
    // 定义404错误的重定向页面地址
    404 => \think\facade\App::getRootPath() . 'public\404.html',
    // 还可以定义其它的HTTP status
    401 => \think\facade\App::getRootPath() . 'public\401.html'
],
图像

缩略 文字图片水印 裁剪

得安装: composer require topthink/think-image

public function uploadImage()
{
    $upload = Request::file('image');
    $file   = Filesystem::putFile('image', $upload);

    $filepath = Filesystem::getDiskConfig('local', 'root') . '/' . $file;
    $image  = Image::open($filepath);
    // 缩略图
    $image->thumb(150, 150, 6)->save($filepath);
    // 水印
    $image->water('./static/image/logo.png', 9, 50)->save($filepath);
    // 文字
    $image->text('hello', './static/font/1.ttf', 20, '#ffffff')->save($filepath);
    // 裁剪
    $image->crop(100, 100, 50, 50)->save($filepath);
}
日志

runtime下得文件形式,真记还得用数据库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值