ThinkPHP8.xx
<h1>01. phpEnv配置安装</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. 学习基础</h3>
<ul>
<li>ThinkPHP8.x:
<ul>
<li>前端基础:HTML5/CSS(必须)、JavaScript(可选、但推荐有);</li>
<li>后端基础:PHP基础,版本不限,但不能太老,至少PHP5.4以上语法,TP8是兼容PHP8.x的;</li>
<li>数据库基础:MySQL数据库,掌握了常规的SQL语句。</li>
<li>编码思维基础:MVC、MVVM、设计模式(单例、观察者等)有简单认知即可;</li>
</ul></li>
</ul>
<h3>2. 软件获取</h3>
<ul>
<li>phpEnv:</li>
<li>
<p>官方网站:<a href="https://www.phpenv.cn/">https://www.phpenv.cn/</a> (专业优雅强大的PHP集成环境);</p>
<ul>
<li>下载最新版本,自动安装后(默认C盘,可选),界面如下:</li>
</ul>
<p><img src="ThinkPHP8.x.assets/01-1.jpg" style="zoom:80%;margin:0" /></p>
</li>
<li>
<p>简单配置:</p>
<ul>
<li>
<p>应用软件->设置->开启CDN加速(方便我们后续安装各种软件);</p>
</li>
<li>
<p>安装 PHP8.2 和 MySQL8 ,选择 Apache (个人向,教学方便);</p>
</li>
<li>
<p>然后点击 启动服务 ,允许各种允许访问即可;</p>
</li>
<li>在浏览器输入:localhost,即可访问PHP探针。</li>
</ul>
<p><img src="ThinkPHP8.x.assets/01-2.jpg" style="zoom:80%;margin:0" /></p>
</li>
<li>
<p>创建站点:</p>
<ul>
<li>
<p>点击 主页 下 网站,添加一个虚拟网站,以便后续学习测试;</p>
</li>
<li>
<p>可以选择自定义的目录,我教学方便,就默认了;</p>
</li>
<li>
<p>添加后,会写入hosts,这样我们输入 www.tp.com 域名后就可以访问了;</p>
</li>
<li>默认会给你一个index.php,显示结果为:php8.2 info</li>
</ul>
<p><img src="ThinkPHP8.x.assets/01-3.jpg" style="zoom:80%;margin:0" /></p>
</li>
</ul>
<h1>02. ThinkPHP8.x配置安装</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. 8.x安装</h3>
<ul>
<li>
<p>主要特性:(来自手册)</p>
<ul>
<li>支持PHP8强类型、支持6.x无缝升级、语义化版本策略、升级PSR规范依赖版本;</li>
</ul>
</li>
<li>安装方式:
<ul>
<li>仅在windows教学,Linux或其它平台对照手册执行即可;</li>
<li>键盘:win + R,输入cmd,输入composer,判断是否已经安装;</li>
<li>如果没有安装composer,则:<a href="https://getcomposer.org/Composer-Setup.exe">https://getcomposer.org/Composer-Setup.exe</a> (来自手册);</li>
<li>切换至你项目的根目录,比如第一节课创建的 www.tp.com 文件夹的上一层 www;</li>
<li>输入安装命令:composer create-project topthink/think www.tp.com;</li>
<li>注意:被安装的文件夹必须是空的,先要删除里面的内容;</li>
<li>各种超时,可以先修改成国内源,比如阿里:</li>
<li>composer config -g repo.packagist composer <a href="https://mirrors.aliyun.com/composer/">https://mirrors.aliyun.com/composer/</a></li>
<li>查看是否成功:composer config -g -l </li>
</ul></li>
</ul>
<h3>2. 启动程序</h3>
<ul>
<li>
<p>IDE载入</p>
<ul>
<li>开发工具任意,因为我们在浏览器测试即可,我这里选择了phpStorm,备选如下:</li>
<li>VS Code</li>
<li>sublime Text4</li>
<li>Zend Stduio</li>
<li>载入项目后,在IDE的终端输入内置服务器命令:php think run,启动Web服务;</li>
<li>访问方式为:www.tp.com:8000 ,默认端口8000,当然也有80端口,不过被占用了;</li>
<li>此时,我们发现,phpEnv还没启动呢,怎么就可以访问了呢?</li>
<li>官方手册 -> 命令行 -> 启动内置服务器,有讲,这是内置的Web服务器,其它服务没有;</li>
<li>所以学习使用时,推荐开启着,比如数据库连接等服务还需要。</li>
</ul>
</li>
<li>
<p>删除广告</p>
<ul>
<li>ThinkPHP8.x目前非常的商业化,打开后,广告糊一脸,我们先把这个去掉。</li>
<li>在app/controller/index.php中的index方法,自定义return结果即可。</li>
<li>或者执行phpinfo()函数,来检测php版本。</li>
</ul>
</li>
<li>配色问题
<ul>
<li>我个人开发学习,都是用的深色背景,明亮代码,一般开发工具都是默认这个样式;</li>
<li>也推荐学习者,也用这个默认或差不过的样式,用于保护眼睛和缓解视觉疲劳;</li>
<li>但如果讲课,我一般采用的都是亮色背景,原因如下:</li>
<li>场景切换:如果是深色,切换到亮色界面,对学习者那么一闪,暴击效果显著;</li>
<li>专注力:深色的背景,更加容易让听课者昏睡,注意力不集中;</li>
<li>上课时长:每节课10分钟上下,并不会太疲劳,自己长时间开发用深色;</li>
</ul></li>
</ul>
<h1>03. 编码规范.目录.配置</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. 编码规范</h3>
<ul>
<li>完全版参考手册,这里说重点:
<ul>
<li>TP8遵循PSR-2命名规范、PSR-4自动加载规范;</li>
<li>类名和文件名保持一致,驼峰式命名(首字母大写)如:Index,BaseController</li>
<li>方法和函数名用驼峰式命名(首字母小写)如:index(),hello()</li>
<li>数据表和字段用蛇式命名(两个单词中间下划线)如:think_name,user_name</li>
<li>更多对照手册</li>
</ul></li>
</ul>
<h3>2. 目录结构</h3>
<ul>
<li>
<p>对于初学者,一个个说是干啥的,没有意义,这里说重点:</p>
<ul>
<li>app目录是应用目录,我们开发程序就在这个目录</li>
<li>config是配置目录,顾名思义,配置程序状态</li>
<li>public是公共目录,对外访问目录,运行程序默认执行此目录的index.php</li>
<li>剩下的,后续学到一个,讲一个。</li>
</ul>
</li>
</ul>
<h3>3. 配置定义</h3>
<ul>
<li>默认情况下,程序出错会显示:页面出错!请稍候再试~</li>
<li>这种情况,一般是应用部署好后,万一出错给用户看的;</li>
<li>
<p>如果我们自己在开发阶段,需要开启调试模式,来提示具体的错误信息:</p>
<ul>
<li>在根目录有一个文件:.example.env,改成 .env ,也就是去掉点前面;</li>
<li>然后在配置信息的第一行:APP_DEBUG = true 即可,false则不开启。</li>
</ul>
</li>
<li>调试模式开启后,可以发现右下角会出现trace调试工具小图标:
<ul>
<li>包含了丰富的调试内容:具体自点查看。</li>
</ul></li>
</ul>
<h1>04. URL访问模式</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. 单应用URL</h3>
<pre><code class="language-php">http://serverName/index.php/控制器/操作/参数/值…</code></pre>
<ul>
<li>
<p>注意:这里服务器启动是 php think run 的内置服务器,下节课会探讨外置服务器;</p>
</li>
<li>
<p>结构分析:</p>
<ul>
<li>
<p>serverName就是我们的:www.tp.com:8000;</p>
</li>
<li>
<p>index.php 是入口文件,带上 / ;</p>
</li>
<li>
<p>控制器是app\controller\Index.php中的 Index 这个名称,也就是类名;</p>
</li>
<li>
<p>操作是类里面的方法名,比如:index(默认方法),hello(普通方法);</p>
</li>
<li>
<p>默认方法可以省略,会直接方法,其他普通方法需要键入方法名:</p>
</li>
<li><a href="http://www.tp.com:8000/index.php/Index">http://www.tp.com:8000/index.php/Index</a> (默认执行index操作)</li>
<li><a href="http://www.tp.com:8000/index.php/Index/index">http://www.tp.com:8000/index.php/Index/index</a> (完整路径)</li>
<li>
<p><a href="http://www.tp.com:8000/index.php/Index/test">http://www.tp.com:8000/index.php/Index/test</a> (普通方法,必须完整路径)</p>
</li>
<li>
<p>系统默认自带的hello方法,是针对后续路由课程的,在路由文件设置过导致无效;</p>
</li>
<li>
<p>我们在config/app.php中将路由关闭:"with_route" => false,</p>
</li>
<li><a href="http://www.tp.com:8000/index.php/Index/hello">http://www.tp.com:8000/index.php/Index/hello</a> (执行默认参数值)</li>
<li>
<p><a href="http://www.tp.com:8000/index.php/Index/hello/name/World(修改参数值">http://www.tp.com:8000/index.php/Index/hello/name/World(修改参数值</a>)</p>
</li>
<li>
<p>参数不够直观,尤其多参数的时候,也是支持传统方案的:</p>
</li>
<li><a href="http://www.tp.com:8000/index.php/Index/hello?name=World">http://www.tp.com:8000/index.php/Index/hello?name=World</a> (问号键值对)</li>
</ul>
</li>
</ul>
<h3>2. 多应用URL</h3>
<pre><code class="language-php">http://serverName/index.php/应用/控制器/操作/参数/值…</code></pre>
<ul>
<li>如果开启多应用的话,URL比单应用多一个 应用 路径,其它一致;</li>
<li>由于我们教学,都在单应用上,暂时不刻意演示,如果后续有机会,放到最后;</li>
</ul>
<h1>05. 内.外置服务器</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. 启动内置</h3>
<ul>
<li>在手册命令行 -> 启动内置服务器,有 php think run 启动的服务器介绍;</li>
<li>启动后直接输入:127.0.0.1:8000 或 localhost:8000 或 www.tp.com:8000 访问;</li>
<li>停止内置服务器,在命令行使用 ctrl + c</li>
</ul>
<h3>2. 启动phpEnv</h3>
<ul>
<li>不管是本地的外置服务器集成环境phpEnv,还是以后要部署到远程服务器,都需要配置;</li>
<li>这里需要配置的重点就是伪静态,我们不启动内置服务器,只用外置访问试试;</li>
<li>注意:这里外置服务器,在第一课我们已经创建了 端口为 80 的 www.tp.com;</li>
<li>
<p>现在,直接访问试试?但出现了如下图的错误:</p>
</li>
<li>我们在 www.tp.com 目录下创建一个index.html或index.php索引文件,访问后:</li>
</ul>
<h3>3. 配置目录</h3>
<ul>
<li>在第一节课,我们创建的启动目录是 www.tp.com ,但目前知道这个目录下没有 index.php;</li>
<li>在手册的 基础 -> 目录结构 中 public 目录 解释为:WEB目录(对外访问目录);</li>
<li>并规范到:在实际项目部署中,确保只将 public 对外访问;</li>
<li>所以,我们将phpEnv站点配置目录设置为:www.tp.com/pubic 即可;</li>
<li>至于http.conf、伪静态和.htaccess等配置信息,默认情况下支持都用,后续需要配置再说;</li>
</ul>
<h1>06. 控制器的定义</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. 定义方式</h3>
<ul>
<li>
<p>控制器:顾名思义MVC中的C,即逻辑控制定义;</p>
</li>
<li>
<p>默认在app\controller下编写对应的控制器类文件,如果想改默认目录:</p>
<pre><code class="language-php">// 访问控制器层名称
&quot;controller_layer&quot; =&gt; &quot;controller&quot;,</code></pre>
</li>
<li>
<p>类名和文件名大小写保持一致,并采用驼峰式(首字母大写);</p>
<pre><code class="language-php">// app\controller\User.php
namespace app\controller;
class User
{
public function index()
{
return &quot;用户&quot;;
}
public function login()
{
return &quot;登录成功!&quot;;
}
}</code></pre>
<ul>
<li>User类创建两个方法 index(默认)和 login,访问 URL 如下:</li>
<li><a href="http://www.tp.com:8000/Index.php/user/">http://www.tp.com:8000/Index.php/user/</a></li>
<li><a href="http://www.tp.com:8000/Index.php/user/login">http://www.tp.com:8000/Index.php/user/login</a></li>
</ul>
</li>
<li>
<p>那么如果创建的是双字母组合,比如 class HelloWorld,访问 URL 如下:</p>
</li>
<li>
<p><a href="http://www.tp.com:8000/Index.php/helloworld">http://www.tp.com:8000/Index.php/helloworld</a></p>
</li>
<li>
<p><a href="http://www.tp.com:8000/Index.php/hello_world">http://www.tp.com:8000/Index.php/hello_world</a></p>
<pre><code class="language-php">namespace app\controller;
class HelloWorld
{
public function index()
{
return &quot;index&quot;;
}
}</code></pre>
</li>
</ul>
<h3>2. URL重写</h3>
<ul>
<li>
<p>URL重写可以省去 index.php 入口文件的键入,默认情况下内置服务器支持重写;</p>
<p><a href="http://www.tp.com:8000/User">http://www.tp.com:8000/User</a></p>
</li>
<li>
<p>而外置服务器,比如phpEnv,省略了入口文件,则出现如下问题:</p>
<p><img src="ThinkPHP8.x.assets/image-20231008153530630.png" alt="image-20231008153530630" style="zoom:80%;margin:0" /></p>
</li>
<li>
<p>查看手册,根据它URL重写的修改方案(Apache),需要修改.htaccess最后一行:</p>
<pre><code class="language-php">#RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] Apache替换成下面一行
RewriteRule ^(.*)$ index.php [L,E=PATH_INFO:$1]</code></pre>
<p><img src="ThinkPHP8.x.assets/image-20231008161340527.png" alt="image-20231008161340527" style="zoom:80%;margin:0" /></p>
</li>
<li>备注:不同的环境、服务器等等都会有千奇百怪的问题,努力搜索解决即可;</li>
<li>如果实在解决不了,请就用 php think run 内置服务器就了,省事。</li>
<li>到时候部署到真实服务器环境,再折腾。。。</li>
</ul>
<h3>3. 配置渲染</h3>
<ul>
<li>
<p>如果你想避免引入同类名时的冲突,可以 config\route.php 设置控制器后缀:</p>
<pre><code class="language-php">// 是否使用控制器后缀
// 此时,User.php 就必须改成 UserController.php,并类名也需要增加后缀
&quot;controller_suffix&quot; =&gt; true,</code></pre>
</li>
<li>
<p>ThinkPHP 直接采用方法内 return 返回的方式直接就输出了;</p>
</li>
<li>
<p>如果需要使用 json 输出,直接采用 json 函数;</p>
<pre><code class="language-php">// json输出,不同浏览器美化样式不同,我这里用的FireFox
public function json()
{
return json([&quot;name&quot; =&gt; &quot;王二狗&quot;, &quot;age&quot; =&gt; 25, &quot;gender&quot; =&gt; &quot;男&quot;]);
}</code></pre>
</li>
<li>
<p>不推荐使用 die、exit 等 PHP 方法中断代码执行,推荐助手函数 halt();</p>
<pre><code class="language-php">halt(&quot;中断测试&quot;);</code></pre>
</li>
</ul>
<h1>07. 基础和空控制器</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. 基础控制器</h3>
<ul>
<li>
<p>一般来说,创建控制器后,推荐继承基础控制器来获得更多的功能方法;</p>
</li>
<li>
<p>基础控制器仅仅提供了控制器验证功能,并注入了think\App和think\Request;</p>
</li>
<li>
<p>这两个对象后面会有章节详细讲解,下面我们继承并简单使用一下;</p>
<pre><code class="language-php">namespace app\controller;
use app\BaseController;
class User extends BaseController
{
public function index()
{
// 返回实际路径
return $this-&gt;app-&gt;getBasePath();
// 返回当前方法名
return $this-&gt;request-&gt;action();
}
}</code></pre>
</li>
</ul>
<h3>2. 空控制器</h3>
<ul>
<li>
<p>空控制器一般用于载入不存在的控制器时,进行错误提示;</p>
<pre><code class="language-php">class Error
{
public function __call(string $name, array $arguments)
{
return &quot;当前控制器不存在!&quot;;
}
}</code></pre>
</li>
</ul>
<h1>08. 创建数据表及填充</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. 创建数据表</h3>
<ul>
<li>
<p>我们直接使用phpEnv自带的MySQL操作软件:HeidiSQL,简称HS;</p>
</li>
<li>
<p>登录的时候,注意修改root和密码后,保存,下次方可直接打开;</p>
</li>
<li>
<p>首先,我们创建一个数据库:demo,用于测试:</p>
<p><img src="ThinkPHP8.x.assets/image-20231009153158426.png" alt="image-20231009153158426" style="zoom:80%;margin:0;" /></p>
</li>
</ul>
<h3>2. 创建用户表</h3>
<ul>
<li>
<p>设置好数据库后,我们创建一个user表,包含id、name、age、gender、details等字段;</p>
<p><img src="ThinkPHP8.x.assets/image-20231009154005270.png" alt="image-20231009154005270" style="zoom:80%;margin:0" /></p>
</li>
<li>
<p>然后,我们填充一些数据,方便查询测试:</p>
<p><img src="ThinkPHP8.x.assets/image-20231009154404157.png" alt="image-20231009154404157" style="zoom:80%;margin:0" /></p>
</li>
<li>
<p>最后,我们通过SQL语句来测试一下:</p>
<p><img src="ThinkPHP8.x.assets/image-20231009154542782.png" alt="image-20231009154542782" style="zoom:80%;margin:0" /></p>
</li>
</ul>
<h1>09. 连接数据库和查询</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. 连接数据库</h3>
<ul>
<li>
<p>我们可以在 <strong>config\database.php</strong> 配置文件中设置与数据库的连接信息:</p>
<ul>
<li>
<p>如果是一般性数据库连接,在 <strong>‘’connections‘’</strong> 配置区设置即可;</p>
</li>
<li>如果是本地测试,它会优先读取 <strong>.env</strong> 配置,然后再读取 <strong>database.php</strong> 的配置;</li>
</ul>
<pre><code># .env文件,部署服务器,请禁用我
APP_DEBUG = true
DB_TYPE = mysql
DB_HOST = 127.0.0.1
DB_NAME = demo
DB_USER = root
DB_PASS = 123456
DB_PORT = 3306
DB_CHARSET = utf8
DEFAULT_LANG = zh-cn</code></pre>
<ul>
<li>如果禁用了 .env 配置,则会读取数据库连接的默认配置:</li>
</ul>
<pre><code class="language-php">// 数据库连接配置信息
&quot;connections&quot; =&gt; [
&quot;mysql&quot; =&gt; [
// 数据库类型
&quot;type&quot; =&gt; env(&quot;DB_TYPE&quot;, &quot;mysql&quot;),
// 服务器地址
&quot;hostname&quot; =&gt; env(&quot;DB_HOST&quot;, &quot;127.0.0.1&quot;),
// 数据库名
&quot;database&quot; =&gt; env(&quot;DB_NAME&quot;, &quot;demo&quot;),
// 用户名
&quot;username&quot; =&gt; env(&quot;DB_USER&quot;, &quot;root&quot;),
// 密码
&quot;password&quot; =&gt; env(&quot;DB_PASS&quot;, &quot;123456&quot;),
// 端口
&quot;hostport&quot; =&gt; env(&quot;DB_PORT&quot;, &quot;3306&quot;),</code></pre>
</li>
</ul>
<h3>2. PHP获取数据</h3>
<ul>
<li>
<p>我们暂时没有详细学习此类语法,可以简单用一些了解一下即可:</p>
<pre><code class="language-php">// 引入Db数据库类
use think\facade\Db;
class User extends BaseController
{
public function get()
{
// 连接user表,查询
$user = Db::table(&quot;user&quot;)-&gt;select();
// 输出数据
return json($user);
}
}</code></pre>
</li>
</ul>
<h1>10. 构造器之数据查询</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. table方法</h3>
<ul>
<li>
<p>Db类旗下有一个 <strong>table</strong> 静态调用的方法,参数为完整的表名(前缀都不能省略);</p>
</li>
<li>
<p>如果希望只查询一条数据,可以使用 <strong>find()</strong> 方法,需指定 <strong>where</strong> 条件:</p>
<pre><code class="language-php">// 通过ID查询指定的数据
// find 方法查询结果不存在,返回 null,否则返回结果数组
$user = Db::table(&quot;user&quot;)-&gt;where(&quot;id&quot;, 1)-&gt;find();</code></pre>
</li>
<li>
<p>想要了解执行的原生SQL是什么,可以注释掉 return 直接通过 trace 查看;</p>
</li>
<li>
<p>使用 <strong>findOrEmpty()</strong> 方法也可以查询一条数据,但在没有数据时返回一个空数组:</p>
<pre><code class="language-php">// 没有数据返回空数组
$user = Db::table(&quot;user&quot;)-&gt;where(&quot;id&quot;, 11)-&gt;findOrEmpty();</code></pre>
</li>
<li>
<p>使用 <strong>findOrFail()</strong> 方法同样可以查询一条数据,在没有数据时抛出一个异常:</p>
<pre><code class="language-php">// 没有数据抛出异常
$user = Db::table(&quot;user&quot;)-&gt;where(&quot;id&quot;, 11)-&gt;findOrFail();</code></pre>
</li>
<li>
<p>想要获取多列数据,可以使用 <strong>select()</strong> 方法:</p>
<pre><code class="language-php">// 查询所有数据
$user = Db::table(&quot;user&quot;)-&gt;select();</code></pre>
</li>
<li>
<p><strong>select()</strong> 方法默认返回 <strong>Collection</strong> 对象的数据集,可以通过 <strong>toArray()</strong> 方法转换成数组:</p>
<pre><code class="language-php">// 用中断函数,来检测返回值
$user = Db::table(&quot;user&quot;)-&gt;select();
halt($user);
// 转换成数组
$user = Db::table(&quot;user&quot;)-&gt;select()-&gt;toArray();
halt($user);</code></pre>
</li>
<li>
<p>多列数据也可以参与 where() 方法的筛选:</p>
<pre><code class="language-php">// 多列筛选
$user = Db::table(&quot;user&quot;)-&gt;where(&quot;age&quot;, 14)-&gt;select();</code></pre>
</li>
</ul>
<h3>2. 链式查询</h3>
<ul>
<li>我们发现通过指向符号 “->” 多次连续调用方法称为:链式查询;</li>
<li>当 Db::table("user") 时,返回查询对象(Query),即可连缀数据库对应的方法;</li>
<li>当返回查询对象(Query)时,就可以继续调用链式方法,where() 也是链式方法;</li>
<li>而 where() 被调用后,依旧返回(Query),可以再次链式调用;</li>
<li>在手册 <strong>数据库 -> 查询构造器 -> 链式操作</strong> 可以了解所有可链式的方法:table、where等;</li>
<li>直到遇到 find() 或 select() 返回数组或数据集时,结束查询;</li>
</ul>
<h1>11. 表前缀之扩展查询</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. 表前缀</h3>
<ul>
<li>
<p>一般来说,为了保持表名统一性和防止冲突,都会给表加上一个前缀,以下划线结束;</p>
<ul>
<li>
<p>比如:<strong>tp_user</strong>,这里的 <strong>tp_</strong> 就是表前缀,所有;</p>
</li>
<li>
<p>我们修改MySQL中表名,然后刷新程序,报错;</p>
</li>
<li>当然,你可以传递 <strong>Db::table("tp_user")</strong>,但没必要;</li>
</ul>
</li>
<li>
<p>首先,我们可以来配置统一前缀:</p>
<ul>
<li>
<p>在 .env 文件中 添加:DB<em>PREFIX = tp</em></p>
</li>
<li>如果部署环境 database.php 中 设置</li>
</ul>
<pre><code class="language-php">&quot;prefix&quot; =&gt; env(&quot;DB_PREFIX&quot;, &quot;tp_&quot;),</code></pre>
</li>
<li>
<p>然后,使用 <strong>Db::name("user")</strong> 方法即可:</p>
<pre><code class="language-php">// 此时,tp_ 表名的前缀可以省略
$user = Db::name(&quot;user&quot;)-&gt;select();</code></pre>
</li>
</ul>
<h3>2. 扩展查询</h3>
<ul>
<li>
<p>通过 <strong>value()</strong> 方法,可以查询指定字段的值(单个),没有数据返回 <strong>null</strong> ;</p>
<pre><code class="language-php">// value() 方法查询单个列值
$user = Db::name(&quot;user&quot;)-&gt;where(&quot;id&quot;, 3)-&gt;value(&quot;name&quot;);</code></pre>
</li>
<li>
<p>通过 <strong>colunm()</strong> 方法,可以查询指定列的值(多个),没有数据返回空数组;</p>
<pre><code class="language-php">// colunm() 方法查询多个列值
$user = Db::name(&quot;user&quot;)-&gt;column(&quot;name&quot;);
// 通过id 作为索引
$user = Db::name(&quot;user&quot;)-&gt;column(&quot;name,age&quot;, &quot;id&quot;);</code></pre>
</li>
<li>
<p>当大量数据需要 <strong>批处理</strong> 时,比如给所有用户更新数据,就不能一次性全取出来,一批一批的来;</p>
<pre><code class="language-php">// 批量处理
Db::name(&quot;user&quot;)-&gt;chunk(2, function ($users) {
foreach ($users as $user) {
dump($user);
}
echo 1;
});
// 通过获取最后的SQL语句,发现用的是LIMIT 2
return Db::getLastSql();</code></pre>
</li>
<li>
<p>另一种处理大量数据:<strong>游标查询</strong>,为了解决内存开销,每次读取一行,并返回到下一行再读取;</p>
<pre><code class="language-php">// 批量处理2
$users = Db::name(&quot;user&quot;)-&gt;cursor();
// PHP生成器
// halt($user);
foreach ($users as $user) {
dump($user);
}</code></pre>
</li>
</ul>
<h1>12. 数据的新增方式</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. 单条新增</h3>
<ul>
<li>
<p>使用 <strong>insert()</strong> 方法可以向数据表添加一条数据,更多的字段采用默认;</p>
<pre><code class="language-php">// 数据
$data = [
&quot;name&quot; =&gt; &quot;张麻子&quot;,
&quot;age&quot; =&gt; 28,
&quot;gender&quot; =&gt; &quot;男&quot;,
&quot;details&quot; =&gt; &quot;我脸上没有麻子!&quot;
];
// 单条新增,成功返回1
return Db::name(&quot;user&quot;)-&gt;insert($data);</code></pre>
</li>
<li>
<p>如果想强行新增抛弃不存在的字段数据,则使用 <strong>strick(false)</strong> 方法,忽略异常;</p>
<pre><code class="language-php">// 数据
$data = [
&quot;name&quot; =&gt; &quot;马邦德&quot;,
&quot;age&quot; =&gt; 30,
&quot;gender&quot; =&gt; &quot;男&quot;,
&quot;deta&quot; =&gt; &quot;我脸上没有麻子!&quot;
];
// 单条新增,成功返回1
return Db::name(&quot;user&quot;)-&gt;strict(false)-&gt;insert($data);</code></pre>
</li>
<li>
<p>如果我们采用的数据库是 mysql,可以支持 <strong>replace</strong> 写入;</p>
</li>
<li>
<p>insert 和 replace insert 写入的区别,前者表示表中存在主键相同则报错,后者则修改;</p>
<pre><code class="language-php">// 新增数据时,主键冲突时,直接修改这条记录
Db::name(&quot;user&quot;)-&gt;replace()-&gt;insert($data);</code></pre>
</li>
<li>
<p>使用 <strong>insertGetId()</strong> 方法,可以在新增成功后返回当前数据 ID;</p>
<pre><code class="language-php">// 返回自增ID
return Db::name(&quot;user&quot;)-&gt;replace()-&gt;insertGetId($data);</code></pre>
</li>
</ul>
<h3>2. 多条新增</h3>
<ul>
<li>
<p>使用 <strong>insertAll()</strong> 方法,可以批量新增数据,但要保持数组结构一致;</p>
<pre><code class="language-php">// 数据
$data = [[
&quot;name&quot; =&gt; &quot;林克&quot;,
&quot;age&quot; =&gt; 19,
&quot;gender&quot; =&gt; &quot;男&quot;,
&quot;details&quot; =&gt; &quot;先收集999个呀哈哈!&quot;
],[
&quot;name&quot; =&gt; &quot;普尔亚&quot;,
&quot;age&quot; =&gt; 100,
&quot;gender&quot; =&gt; &quot;女&quot;,
&quot;details&quot; =&gt; &quot;我先来个返老还童,再快速长大!&quot;
]];
return Db::name(&quot;user&quot;)-&gt;insertAll($data);</code></pre>
</li>
<li>
<p><strong>insertAll()</strong> 方法 也支持 replace 写入,如果添加数据量大,可以通过 <strong>-> limit()</strong> 方法限制添加数量;</p>
<pre><code class="language-php">Db::name(&quot;user&quot;)-&gt;replace()-&gt;limit(100)-&gt;insertAll($data);</code></pre>
</li>
</ul>
<h1>13. 更新删除和save方法</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. 数据修改</h3>
<ul>
<li>
<p>使用 <strong>update()</strong> 方法来修改数据,修改成功返回影响行数,没有修改返回 <strong>0</strong>;</p>
<pre><code class="language-php">// 修改的数据
$data = [
&quot;name&quot; =&gt; &quot;王三狗&quot;,
&quot;age&quot; =&gt; &quot;13&quot;,
];
// 执行修改并返回
return Db::name(&quot;user&quot;)-&gt;where(&quot;id&quot;, 4)-&gt;update($data);</code></pre>
</li>
<li>
<p>如果修改数据包含了主键信息,比如 id,那么可以省略掉 where 条件;</p>
<pre><code class="language-php">// 修改的数据
$data = [
&quot;id&quot; =&gt; 4,
&quot;name&quot; =&gt; &quot;王三狗&quot;,
&quot;age&quot; =&gt; &quot;13&quot;,
];
// 执行修改并返回
return Db::name(&quot;user&quot;)-&gt;update($data);</code></pre>
</li>
<li>
<p>如果想让一些字段修改时执行 SQL 函数操作,可以使用 <strong>exp()</strong> 方法实现;</p>
<pre><code class="language-php">// 让details字段内的英文大写
return Db::name(&quot;user&quot;)-&gt;exp(&quot;details&quot;, &quot;UPPER(details)&quot;)-&gt;update($data);</code></pre>
</li>
<li>
<p>如果要自增/自减某个字段,可以使用 inc/dec 方法,并支持自定义步长;</p>
<pre><code class="language-php">// 修改时,让age自增和自减,默认1,要去掉$data里面age字段的修改,不然冲突
return Db::name(&quot;user&quot;)-&gt;inc(&quot;age&quot;)-&gt;dec(&quot;age&quot;, 2)-&gt;update($data);
// 派生自字段,延迟执行,毫秒
setInc(&quot;age&quot;, 1, 600)
setDec(&quot;age&quot;, 2, 600)</code></pre>
</li>
<li>
<p>使用 <strong>Db::raw()</strong> 来设置每个字段的特殊需求,灵活且清晰:</p>
<pre><code class="language-php">// 使用Db::raw 更加清晰灵活
Db::name(&quot;user&quot;)-&gt;where(&quot;id&quot;, 4)-&gt;update([
&quot;details&quot; =&gt; Db::raw(&quot;UPPER(details)&quot;),
&quot;age&quot; =&gt; Db::raw(&quot;age-2&quot;)
]);
return Db::getLastSql();</code></pre>
</li>
<li>
<p><strong>save()</strong> 方法是一个通用方法,可以自行判断是新增还是修改(更新)数据;</p>
<pre><code class="language-php">// 包含主键,即修改;否则,新增
return Db::name(&quot;user&quot;)-&gt;save($data);</code></pre>
</li>
</ul>
<h3>2. 数据删除</h3>
<ul>
<li>
<p>极简删除可以根据主键直接删除,删除成功返回影响行数,否则 0;</p>
<pre><code class="language-php">// 根据主键删除
Db::name(&quot;user&quot;)-&gt;delete(8);</code></pre>
</li>
<li>
<p>根据主键,还可以删除多条记录;</p>
<pre><code class="language-php">// 根据主键删除多条
Db::name(&quot;user&quot;)-&gt;delete([48,49,50]);</code></pre>
</li>
<li>
<p>正常情况下,通过 where()方法来删除;</p>
<pre><code class="language-php">// 条件删除
Db::name(&quot;user&quot;)-&gt;where(&quot;id&quot;, 47)-&gt;delete();</code></pre>
</li>
<li>清空表以及逻辑软删除,参考手册即可;逻辑软删除,在模型篇单独讲解,这里略过。</li>
</ul>
<h1>14. 查询表达式规则</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>表达式查询</strong></h3>
<ul>
<li>
<p>查询表达式支持大部分常用的 SQL 语句,语法格式如下:</p>
<pre><code class="language-php">where(&quot;字段名&quot;,&quot;查询表达式&quot;,&quot;查询条件&quot;);</code></pre>
</li>
<li>
<p>所有的表达式,查阅手册 -> 查询表达式 中的表格即可;这里列出几个意思一下:</p>
<table>
<thead>
<tr>
<th style="text-align: center;">表达式</th>
<th style="text-align: center;">含义</th>
<th style="text-align: center;">快捷方式</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">=</td>
<td style="text-align: center;">等于</td>
<td style="text-align: center;"></td>
</tr>
<tr>
<td style="text-align: center;"><= time</td>
<td style="text-align: center;">小于等于某个时间</td>
<td style="text-align: center;">whereTime</td>
</tr>
<tr>
<td style="text-align: center;">EXP</td>
<td style="text-align: center;">SQL表达式查询</td>
<td style="text-align: center;">whereExp</td>
</tr>
<tr>
<td style="text-align: center;">[NOT] LIKE</td>
<td style="text-align: center;">模糊查询</td>
<td style="text-align: center;">whereLike</td>
</tr>
<tr>
<td style="text-align: center;">[NOT] IN</td>
<td style="text-align: center;">[非] IN 查询</td>
<td style="text-align: center;">whereIN</td>
</tr>
</tbody>
</table>
</li>
</ul>
<h3>2. <strong>查询示例</strong></h3>
<ul>
<li>
<p>条件判断类的,id大于4的;</p>
<pre><code class="language-php">// 查询id大于4的数据
$user = Db::name(&quot;user&quot;)-&gt;where(&quot;id&quot;, &quot;&gt;&quot;, 4)-&gt;select();
return json($user);</code></pre>
</li>
<li>
<p>Like模糊查询,姓王的;</p>
<pre><code class="language-php">// 查询姓王的用户
$user = Db::name(&quot;user&quot;)-&gt;where(&quot;name&quot;, &quot;like&quot;, &quot;王%&quot;)-&gt;select();
// like快捷方式
$user = Db::name(&quot;user&quot;)-&gt;whereLike(&quot;name&quot;, &quot;王%&quot;)-&gt;select();</code></pre>
</li>
<li>
<p>IN区间查询,根据id;</p>
<pre><code class="language-php">// 区间查询,支持not in
$user = Db::name(&quot;user&quot;)-&gt;where(&quot;id&quot;, &quot;in&quot;, &quot;1, 3, 5&quot;)-&gt;select();
// 语义更好一点
$user = Db::name(&quot;user&quot;)-&gt;where(&quot;id&quot;, &quot;in&quot;, [1,3,5])-&gt;select();
// IN快捷查询,两种均可,支持whereNotIn
$user = Db::name(&quot;user&quot;)-&gt;whereIn(&quot;id&quot;, [1, 2, 3])-&gt;select();
// Between,和IN一样 支持 not between
$user = Db::name(&quot;user&quot;)-&gt;where(&quot;id&quot;, &quot;between&quot;, [2,5])-&gt;select();
// 快捷方式和IN一样:whereBetween和whereNotBetween</code></pre>
</li>
<li>
<p>NULL查询;</p>
<pre><code class="language-php">// NULL, null 或 not null:
$user = Db::name(&quot;user&quot;)-&gt;where(&quot;details&quot;, &quot;not null&quot;)-&gt;select();
// 快捷方式:whereNull和whereNotNull</code></pre>
</li>
<li>
<p>EXP查询,自定义SQL片段;</p>
<pre><code class="language-php">// EXP查询,自定义SQL
$user = Db::name(&quot;user&quot;)-&gt;where(&quot;id&quot;, &quot;EXP&quot;, &quot;&lt;&gt; 8 and id &gt;5&quot;)-&gt;select();
// 快捷查询
$user = Db::name(&quot;user&quot;)-&gt;whereExp(&quot;id&quot;,&quot;&lt;&gt; 8 and id &gt;5&quot;)-&gt;select();</code></pre>
</li>
<li>这里推荐:有快捷方式的用快捷方式,通过Ctrl+方法名得知,快捷方式绕过了很多拼装流程,速度更快;</li>
</ul>
<h1>15. 超多查询都不讲</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<ul>
<li>警告:这里罗列了大量的快捷查询的示例、链式查询以及其它非常用的查询方法;</li>
<li>介于TP6课程大家的反馈,我这里不再详细讲解每个方法的用法;</li>
<li>但我会录入在讲义里,方便后续项目课程时候回头查阅;后续类似的知识点也这么砍;</li>
</ul>
<h3>1. 字符串条件(不讲)</h3>
<ul>
<li>
<p><strong>whereRaw</strong> 可以直接写入多条件:</p>
<pre><code class="language-php">// 多条件字符串
$user = Db::name(&quot;user&quot;)-&gt;whereRaw(&quot;age &gt; 15 AND gender=&quot;女&quot;&quot;)-&gt;select();</code></pre>
</li>
<li>
<p>包含变量的多条件查询:</p>
<pre><code class="language-php">// 变量
$age = 15;
$gender = &quot;女&quot;;
// 预处理机制
$user = Db::name(&quot;user&quot;)-&gt;whereRaw(&quot;age&gt;:age AND gender=:gender&quot;, [
&quot;age&quot; =&gt; $age,
&quot;gender&quot; =&gt; $gender
])-&gt;select();</code></pre>
</li>
</ul>
<h3>2. field()字段筛选(不讲)</h3>
<ul>
<li>
<p>使用 <strong>field()</strong> 方法,可以指定要查询的字段;</p>
<pre><code class="language-php">// 字段筛选
$user = Db::name(&quot;user&quot;)-&gt;field(&quot;id, age, gender&quot;)-&gt;select();
$user = Db::name(&quot;user&quot;)-&gt;field([&quot;id, age, gender&quot;])-&gt;select();</code></pre>
</li>
<li>
<p>使用 <strong>field()</strong> 方法,给指定的字段设置别名;</p>
<pre><code class="language-php">// 字段别名
$user = Db::name(&quot;user&quot;)-&gt;field(&quot;id, gender as sex&quot;)-&gt;select();
$user = Db::name(&quot;user&quot;)-&gt;field([&quot;id&quot;, &quot;gender&quot;=&gt;&quot;sex&quot;])-&gt;select();</code></pre>
</li>
<li>
<p>在 <strong>fieldRaw()</strong> 方法里,可以直接给字段设置 MySQL 函数;</p>
<pre><code class="language-php">// 直接SQL函数
$user = Db::name(&quot;user&quot;)-&gt;fieldRaw(&quot;id, UPPER(details)&quot;)-&gt;select();
$user = Db::name(&quot;user&quot;)-&gt;field(true)-&gt;select(); // 推荐
return Db::getLastSql();</code></pre>
</li>
<li>
<p>使用 <strong>withoutField()</strong> 方法中字段排除,可以屏蔽掉想要不显示的字段;</p>
<pre><code class="language-php">// 排除字段
$user = Db::name(&quot;user&quot;)-&gt;withoutField(&quot;details&quot;)-&gt;select();</code></pre>
</li>
<li>
<p>使用 <strong>field()</strong> 方法在新增时,验证字段的合法性;</p>
<pre><code class="language-php">// 排除新增字段
Db::name(&quot;user&quot;)-&gt;field(&quot;name,age,gender&quot;)-&gt;insert($data);</code></pre>
</li>
</ul>
<h3>3. <strong>常用链式方法</strong>(不讲)</h3>
<ul>
<li>
<p>使用 <strong>alias()</strong> 方法,给数据库起一个别名;</p>
<pre><code class="language-php">// 给数据库起个别名
Db::name(&quot;user&quot;)-&gt;alias(&quot;a&quot;)-&gt;select();
return Db::getLastSql();
// 起别名最主要是和另一张表进行关联,这里看手册好了,没表测试
alias(&quot;a&quot;)-&gt;join()</code></pre>
</li>
<li>
<p>使用 <strong>limit()</strong> 方法,限制获取输出数据的个数;</p>
<pre><code class="language-php">// 显示前5条
$user = Db::name(&quot;user&quot;)-&gt;limit(5)-&gt;select();</code></pre>
</li>
<li>
<p>分页模式,即传递两个参数,比如从第 3 条开始显示 5 条 limit(2,5);</p>
<pre><code class="language-php">// 从第2个位置,也就是第3条开始,显示5条
$user = Db::name(&quot;user&quot;)-&gt;limit(2,5)-&gt;select();
// 查询第一页数据,1至10条
$user = Db::name(&quot;user&quot;)-&gt;page(1,10)-&gt;select(); </code></pre>
</li>
<li>
<p>使用 <strong>order()</strong> 方法,可以指定排序方式,没有指定第二参数,默认 asc;</p>
<pre><code class="language-php">// 按id倒序排列
$user = Db::name(&quot;user&quot;)-&gt;order(&quot;id&quot;, &quot;desc&quot;)-&gt;select();</code></pre>
</li>
<li>
<p>支持数组的方式,对多个字段进行排序;</p>
<pre><code class="language-php">// 按多个字段规则排序
$user = Db::name(&quot;user&quot;)-&gt;order([&quot;age&quot;=&gt;&quot;asc&quot;, &quot;id&quot;=&gt;&quot;desc&quot;])-&gt;select();
//支持 orderRaw() 方法,可以传入SQL函数,和前面各类Raw一样,不再赘述</code></pre>
</li>
<li>
<p>使用 <strong>group()</strong> 方法,给性别不同的人进行 age 字段的总和统计;</p>
<pre><code class="language-php">// 统计性别的年龄总和
$user = Db::name(&quot;user&quot;)-&gt;fieldRaw(&quot;gender, SUM(age)&quot;)
-&gt;group(&quot;gender&quot;)-&gt;select();</code></pre>
</li>
<li>
<p>使用 <strong>group()</strong> 分组之后,再使用 <strong>having()</strong> 进行筛选;</p>
<pre><code class="language-php">// 统计性别的年龄总和,筛选大于100的
$user = Db::name(&quot;user&quot;)-&gt;fieldRaw(&quot;gender, SUM(age)&quot;)
-&gt;group(&quot;gender&quot;)
-&gt;having(&quot;SUM(age) &gt; 100&quot;)-&gt;select();</code></pre>
</li>
</ul>
<h3>4. <strong>时间查询</strong>(不讲)</h3>
<ul>
<li>
<p>可以使用 >、<、>=、<= 来筛选匹配时间的数据;</p>
<pre><code class="language-php">// 传统时间筛选
$user = Db::name(&quot;user&quot;)-&gt;where(&quot;create_time&quot;, &quot;&gt;&quot;, &quot;2022-1-1&quot;)-&gt;select();
$user = Db::name(&quot;user&quot;)-&gt;where(&quot;create_time&quot;, &quot;between&quot;, [&quot;2020-1-1&quot;, &quot;2023-1-1&quot;])-&gt;select(); </code></pre>
</li>
<li>
<p>快捷方式 <strong>whereTime</strong>:</p>
<pre><code class="language-php">// 使用快捷方式查询
$user = Db::name(&quot;user&quot;)-&gt;whereTime(&quot;create_time&quot;, &quot;&gt;=&quot;, &quot;2022-1-1&quot;)-&gt;select();
// 默认是 &gt; 可以省略
$user = Db::name(&quot;user&quot;)-&gt;whereTime(&quot;create_time&quot;, &quot;2022-1-1&quot;)-&gt;select();</code></pre>
</li>
<li>
<p>区间查询快捷方式 <strong>whereBetweenTime</strong>:</p>
<pre><code class="language-php">// 区间查询,包含 whereNotBetweenTime
$user = Db::name(&quot;user&quot;)-&gt;whereBetweenTime(&quot;create_time&quot;, &quot;2020-1-1&quot;, &quot;2023-1-1&quot;)-&gt;select();</code></pre>
</li>
<li>
<p>使用 <strong>whereYear</strong> 查询今年的数据、去年的数据和某一年的数据;</p>
<pre><code class="language-php">// 查询今年
$user = Db::name(&quot;user&quot;)-&gt;whereYear(&quot;create_time&quot;)-&gt;select();
// 查询去年
$user = Db::name(&quot;user&quot;)-&gt;whereYear(&quot;create_time&quot;, &quot;last year&quot;)-&gt;select();
// 查询某一年
$user = Db::name(&quot;user&quot;)-&gt;whereYear(&quot;create_time&quot;, &quot;2019&quot;)-&gt;select();</code></pre>
</li>
<li>
<p>使用 <strong>whereMonth</strong> 查询当月的数据、上月的数据和某一个月的数据;</p>
<pre><code class="language-php">Db::name(&quot;user&quot;)-&gt;whereMonth(&quot;create_time&quot;)-&gt;select();
Db::name(&quot;user&quot;)-&gt;whereMonth(&quot;create_time&quot;, &quot;last month&quot;)-&gt;select();
Db::name(&quot;user&quot;)-&gt;whereMonth(&quot;create_time&quot;, &quot;2020-6&quot;)-&gt;select();</code></pre>
</li>
<li>
<p>使用 <strong>whereDay</strong> 查询今天的数据、昨天的数据和某一个天的数据;</p>
<pre><code class="language-php">Db::name(&quot;user&quot;)-&gt;whereDay(&quot;create_time&quot;)-&gt;select();
Db::name(&quot;user&quot;)-&gt;whereDay(&quot;create_time&quot;, &quot;last day&quot;)-&gt;select();
Db::name(&quot;user&quot;)-&gt;whereDay(&quot;create_time&quot;, &quot;2020-6-27&quot;)-&gt;select();</code></pre>
</li>
<li>
<p>查询指定时间的数据,比如两小时内的;</p>
<pre><code class="language-php">// 两小时内的
$user = Db::name(&quot;user&quot;)-&gt;whereTime(&quot;create_time&quot;, &quot;-2 hours&quot;)-&gt;select();</code></pre>
</li>
<li>
<p>查询两个时间字段时间有效期的数据,比如活动开始到结束的期间;</p>
</li>
<li>
<p>比如创建两个字段:start_time,end_time,注册后,分别写入对应时间表明它的有效期;</p>
<pre><code class="language-php">// 直接这么写,不太好理解,看手册的另一种普通写法很容易理解
// 实战中,字段丰富的时候再演示
$user = Db::name(&quot;user&quot;)-&gt;whereBetweenTimeField(&quot;start_time&quot;, &quot;end_time&quot;)-&gt;select();</code></pre>
</li>
</ul>
<h3>5. <strong>聚合查询</strong>(不讲)</h3>
<ul>
<li>
<p>使用 <strong>count()</strong> 方法,可以求出所查询数据的数量;</p>
<pre><code class="language-php">// 获取记录数
$user = Db::name(&quot;user&quot;)-&gt;count();</code></pre>
</li>
<li>
<p><strong>count()</strong> 可设置指定 id,比如有空值(Null)的 details,不会计算数量;</p>
<pre><code class="language-php">// 值NULL不计数
$user = Db::name(&quot;user&quot;)-&gt;count(&quot;details&quot;);</code></pre>
</li>
<li>
<p>使用 <strong>max()</strong> 方法,求出所查询数据字段的最大值;</p>
<pre><code class="language-php">// 求最大年龄
$user = Db::name(&quot;user&quot;)-&gt;max(&quot;age&quot;);</code></pre>
</li>
<li>
<p>如果 <strong>max()</strong> 方法,求出的值不是数值,则通过第二参数强制转换;</p>
<pre><code class="language-php">// 如果最大值不是数值,false关闭强制转换
$user = Db::name(&quot;user&quot;)-&gt;max(&quot;name&quot;, false);</code></pre>
</li>
<li>
<p>使用 <strong>min()</strong> 方法,求出所查询数据字段的最小值,也可以强制转换;</p>
<pre><code class="language-php">// 求最小值
$user = Db::name(&quot;user&quot;)-&gt;min(&quot;age&quot;);</code></pre>
</li>
<li>
<p>使用 <strong>avg()</strong> 方法,求出所查询数据字段的平均值;</p>
<pre><code class="language-php">// 求平均值
$user = Db::name(&quot;user&quot;)-&gt;avg(&quot;age&quot;);</code></pre>
</li>
<li>
<p>使用 <strong>sum()</strong> 方法,求出所查询数据字段的总和;</p>
<pre><code class="language-php">// 求总和
$user = Db::name(&quot;user&quot;)-&gt;sum(&quot;age&quot;);</code></pre>
</li>
</ul>
<h3>6. <strong>子查询</strong>(不讲)</h3>
<ul>
<li>
<p>使用 <strong>fetchSql()</strong> 方法,传递参数true时,可以设置不执行 SQL,直接返回SQL语句;</p>
<pre><code class="language-php">// 子查询语句
$user = Db::name(&quot;user&quot;)-&gt;fetchSql(true)-&gt;select();</code></pre>
</li>
<li>
<p>使用 <strong>buildSql()</strong> 方法,也是返回 SQL 语句,不需要再执行 select(),且有括号;</p>
<pre><code class="language-php">// 第二种子查询
$subQuery = Db::name(&quot;user&quot;)-&gt;buildSql(true);</code></pre>
</li>
<li>
<p>结合以上方法,我们实现一个子查询;</p>
<pre><code class="language-php">// 子查询样式
$subQuery = Db::name(&quot;user&quot;)-&gt;field(&quot;id&quot;)-&gt;where(&quot;age&quot;,&quot;&gt;&quot;, 18)-&gt;buildSql();
$user = Db::name(&quot;user&quot;)-&gt;whereExp(&quot;id&quot;, &quot;IN &quot;.$subQuery)-&gt;select();</code></pre>
</li>
<li>
<p>使用闭包的方式执行子查询;</p>
<pre><code class="language-php">// 采用闭包构建子查询
$user = Db::name(&quot;user&quot;)-&gt;where(&quot;id&quot;, &quot;IN&quot;, function ($query) {
$query-&gt;name(&quot;user&quot;)-&gt;field(&quot;id&quot;)-&gt;where(&quot;age&quot;,&quot;&gt;&quot;, 18);
})-&gt;select();</code></pre>
</li>
</ul>
<h3>7. <strong>原生查询</strong></h3>
<ul>
<li>
<p>使用 <strong>query()</strong> 方法,进行原生 SQL 查询,适用于读取操作,SQL 错误返回 false;</p>
<pre><code class="language-php">// 原生SQL
$user = Db::query(&quot;SELECT * FROM tp_user&quot;);</code></pre>
</li>
<li>
<p>使用 <strong>execute</strong> 方法,进行原生 SQL 更新写入等,SQL 错误返回 false;</p>
<pre><code class="language-php">// 原生更新写入
$user = Db::execute(&quot;update tp_user set details=&quot;快快快来救我!&quot; where id=5&quot;);</code></pre>
</li>
</ul>
<h3>8. <strong>列字段快捷查询</strong>(不讲)</h3>
<ul>
<li>之前用过诸如:whereIn、whereExp、whereLike等等快捷查询;</li>
<li>
<p>所有快捷查询列表的手册位置:数据库 -> 查询构造器 -> 高级查询中,找到快捷查询表格;</p>
</li>
<li>
<p><strong>whereColumn()</strong> 方法,比较两个字段的值,符合的就筛选出来;</p>
<pre><code class="language-php">// 字段比较,id大于age
$user = Db::name(&quot;user&quot;)-&gt;whereColumn(&quot;id&quot;, &quot;&gt;&quot;, &quot;age&quot;)-&gt;select();
// 如果是 等于判断 可以简化
-&gt;whereColumn(&quot;id&quot;, &quot;age&quot;)</code></pre>
</li>
<li>
<p><strong>whereFieldName()</strong> 方法,查询某个字段的值,注意 FileName 是字段名;</p>
<pre><code class="language-php">// 获取所有性别为:男
$user = Db::name(&quot;user&quot;)-&gt;whereGender(&quot;男&quot;)-&gt;select();
// 获取名字叫王二狗的信息
$user = Db::name(&quot;user&quot;)-&gt;whereName(&quot;王二狗&quot;)-&gt;find();</code></pre>
</li>
<li>
<p><strong>getByFieldName()</strong> 方法,查询某个字段的值,注意只能查询一条,不需要 <strong>find()</strong>;</p>
<pre><code class="language-php">// 单条数据
$user = Db::name(&quot;user&quot;)-&gt;getByName(&quot;王二狗&quot;);</code></pre>
</li>
<li>
<p><strong>getFieldByFieldName()</strong> 方法,通过查询得到某个指定字段的单一值;</p>
<pre><code class="language-php">// 查询单条并返回单列,找出王二狗的年龄
$user = Db::name(&quot;user&quot;)-&gt;getFieldByName(&quot;王二狗&quot;, &quot;age&quot;);</code></pre>
</li>
</ul>
<h3>9. <strong>条件查询</strong>(不讲)</h3>
<ul>
<li>
<p><strong>when()</strong> 可以通过条件判断,执行闭包里的分支查询;</p>
<pre><code class="language-php">// 条件判断
$user = Db::name(&quot;user&quot;)-&gt;when(false, function ($query) {
// 满足条件执行这段SQL
$query-&gt;where(&quot;id&quot;, &quot;&gt;&quot;, 5);
}, function ($query) {
// 不满足条件执行这段SQL
$query-&gt;where(&quot;id&quot;, &quot;&lt;=&quot;, 5);
})-&gt;select();</code></pre>
</li>
</ul>
<h3>10. 事务(不讲)</h3>
<ul>
<li>
<p>数据库的表引擎需要是 InnoDB 才可以使用,如果不是调整即可;</p>
</li>
<li>
<p>事务处理,需要执行多个 SQL 查询,数据是关联恒定的;</p>
</li>
<li>
<p>如果成功一条查询,改变了数据,而后一条失败,则前面的数据回滚;</p>
</li>
<li>
<p>比如:银行取钱,银行ATM扣了1000,但入口被卡住,你没拿到,这时需要事务处理;</p>
</li>
<li>
<p>系统提供了两种事务处理的方式,第一种是自动处理,出错自动回滚;</p>
<pre><code class="language-php">// 出现异常回滚
Db::transaction(function () {
Db::name(&quot;user&quot;)-&gt;delete(12);
Db::name(&quot;user&quot;)-&gt;findOrFail(13);
});</code></pre>
</li>
<li>
<p>手动处理,基本和原生处理类似,可以自行输出错误信息;</p>
<pre><code class="language-php">// 启动事务
Db::startTrans();
try {
Db::name(&quot;user&quot;)-&gt;delete(12);
Db::name(&quot;user&quot;)-&gt;findOrFail(13);
//提交事务
Db::commit();
} catch (\Exception $e) {
echo &quot;执行SQL失败!&quot;;
// 回滚
Db::rollback();
}</code></pre>
</li>
</ul>
<h3>11. 获取器(不讲)</h3>
<ul>
<li>
<p>获取器的意思就是:将数据的字段进行转换处理再进行操作;</p>
</li>
<li>
<p>比如在获取数据列表的时候,将获取到的详情字段全部大写;</p>
<pre><code class="language-php">// 获取器改变字段值
$user = Db::name(&quot;user&quot;)-&gt;withAttr(&quot;details&quot;, function ($value, $data) {
// NULL 不处理
if ($value != null) {
return strtoupper($value);
}
})-&gt;select();</code></pre>
</li>
<li>withAttr也是支持JSON字段的,具体参考手册 查询构造器 -> 获取器;</li>
</ul>
<h1>16. 关联索引查询</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>索引关联</strong></h3>
<ul>
<li>
<p><strong>where</strong> 方法的数组查询:</p>
<pre><code class="language-php">// 性别男,年龄大于15岁,常规做法
$user = Db::name(&quot;user&quot;)-&gt;where(&quot;age&quot;, &quot;&gt;&quot;, 15)
-&gt;where(&quot;gender&quot;, &quot;男&quot;)-&gt;select();
// 索引数组方式,二维数组,返回的SQL 是一条 AND 并列关系
$user = Db::name(&quot;user&quot;)-&gt;where([
[&quot;age&quot;, &quot;&gt;&quot;, &quot;15&quot;],
[&quot;gender&quot;,&quot;=&quot;, &quot;男&quot;]
])-&gt;select();
// 如果是等于,可以直接用关联数组,一维
$user = Db::name(&quot;user&quot;)-&gt;where([
&quot;age&quot; =&gt; 15,
&quot;gender&quot; =&gt; &quot;男&quot;
])-&gt;select();
// 两种模式结合起来,
$user = Db::name(&quot;user&quot;)-&gt;where([
[&quot;age&quot;, &quot;&gt;&quot;, &quot;15&quot;],
&quot;gender&quot; =&gt; &quot;男&quot;
])-&gt;select();
// 搜索条件独立管理,这里=号写全
$map[] = [&quot;age&quot;, &quot;&gt;&quot;, &quot;15&quot;];
$map[] = [&quot;gender&quot;,&quot;=&quot;, &quot;男&quot;];
$user = Db::name(&quot;user&quot;)-&gt;where($map)-&gt;select();</code></pre>
</li>
</ul>
<h1>17. 拼装高级查询</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>拼装查询</strong></h3>
<ul>
<li>
<p>使用 <strong>|(OR)</strong> 或 <strong>&(AND)</strong> 来实现 where 条件的高级查询,where 支持多个连缀;</p>
<pre><code class="language-php">// or和and 拼装查询
$user = Db::name(&quot;user&quot;)-&gt;where(&quot;name|details&quot;, &quot;like&quot;, &quot;%王%&quot;)
-&gt;where(&quot;id&amp;create_time&quot;, &quot;&gt;&quot;, 0)
-&gt;select();
// 拼装返回的SQL
SELECT * FROM `tp_user` WHERE ( `name` LIKE &quot;%王%&quot; OR `details` LIKE &quot;%王%&quot; ) AND ( `id` &gt; 0 AND `create_time` &gt; &quot;0&quot; )</code></pre>
</li>
<li>
<p>索引数组方式,可以在 where 进行多个字段进行查询;</p>
<pre><code class="language-php">// 索引数组拼装
$user = Db::name(&quot;user&quot;)-&gt;where([
[&quot;id&quot;, &quot;&gt;&quot;, &quot;5&quot;],
[&quot;gender&quot;, &quot;=&quot;, &quot;女&quot;],
[&quot;age&quot;, &quot;&lt;=&quot;, 15],
[&quot;details&quot;, &quot;like&quot;, &quot;%我%&quot;]
])-&gt;select();
// 拼装返回的SQL
SELECT * FROM `tp_user` WHERE `id` &gt; 5 AND `gender` = &quot;女&quot; AND `age` &lt;= 15 AND `details` LIKE &quot;%我%&quot;</code></pre>
</li>
<li>
<p>根据之前的课程中,条件字符串复杂组装,比如使用 exp 了,就使用 <strong>raw()</strong> 方法;</p>
<pre><code class="language-php">// exp 拼装
$user = Db::name(&quot;user&quot;)-&gt;where([
[&quot;gender&quot;, &quot;=&quot;, &quot;男&quot;],
[&quot;age&quot;, &quot;exp&quot;, Db::raw(&quot;&gt;=10 AND id&lt;5&quot;)]
])-&gt;select();
// 拼装返回的SQL
SELECT * FROM `tp_user` WHERE `gender` = &quot;男&quot; AND ( `age` &gt;=10 AND id&lt;5 )</code></pre>
</li>
<li>
<p>如果有多个where,并需要控制优先级,那么在可以在需要的部分加上中括号;</p>
<pre><code class="language-php">// 下面的代码无法控制优先级
$user = Db::name(&quot;user&quot;)-&gt;where([
[&quot;gender&quot;, &quot;=&quot;, &quot;男&quot;],
[&quot;age&quot;, &quot;exp&quot;, Db::raw(&quot;&gt;=10 AND id&lt;5&quot;)]
])-&gt;where(&quot;details&quot;, &quot;like&quot;, &quot;%我%&quot;)-&gt;select();
// 外加一个中括号
-&gt;where([[
...
]])
// 拼装返回的SQL
SELECT * FROM `tp_user` WHERE ( `gender` = &quot;男&quot; AND ( `age` &gt;=10 AND id&lt;5 ) ) AND `details` LIKE &quot;%我%&quot;
// 推荐用变量代替
$map =[
[&quot;gender&quot;, &quot;=&quot;, &quot;男&quot;],
[&quot;age&quot;, &quot;exp&quot;, Db::raw(&quot;&gt;=10 AND id&lt;5&quot;)]
];
-&gt;where([$map])</code></pre>
</li>
<li>
<p>如果,条件中有多次出现一个字段,并且需要 OR 来左右筛选,可以用 <strong>whereOr</strong>;</p>
<pre><code class="language-php">// 多条件重复字段 OR 选项
$map1 = [
[&quot;name&quot;, &quot;like&quot;, &quot;%王%&quot;],
[&quot;details&quot;, &quot;=&quot;, null]
];
$map2 = [
[&quot;gender&quot;, &quot;=&quot;, &quot;女&quot;],
[&quot;details&quot;, &quot;exp&quot;, Db::raw(&quot;IS NOT NULL&quot;)]
];
$user = Db::name(&quot;user&quot;)-&gt;whereOr([$map1, $map2])-&gt;select();
// 拼装返回的SQL
SELECT * FROM `tp_user` WHERE ( `name` LIKE &quot;%王%&quot; AND `details` IS NULL ) OR ( `gender` = &quot;女&quot; AND ( `details` IS NOT NULL ) )</code></pre>
</li>
</ul>
<h1>18. <strong>模型的定义方式</strong></h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>定义模型</strong></h3>
<ul>
<li>
<p>为了避免被前面课程中控制器的类名干扰,删除或改名都行:</p>
<pre><code class="language-php">// UserBak.php,目前不存在任何地方的User.php了
class UserBak extends BaseController</code></pre>
</li>
<li>
<p>定义一个和数据库表相匹配的模型,可在app应用目录下创建model文件夹;</p>
<pre><code class="language-php">namespace app\model;
use think\Model;
class User extends Model
{
}</code></pre>
</li>
<li>
<p>模型会自动对应数据表,并且有一套自己的命名规则;</p>
</li>
<li>
<p>模型类需要去除表前缀(tp_),采用驼峰式命名,并且首字母大写;</p>
<pre><code class="language-php">tp_user(表名) =&gt; User
tp_user_type(表名) =&gt; UserType</code></pre>
</li>
<li>
<p>在控制器段创建一个任意名称的类,当然有语义更好,但为了教学理解起名为:TestUser.php;</p>
<pre><code class="language-php">namespace app\controller;
use app\model\User;
// 注意:类名不限制
class TestUser
{
public function index()
{
return json(User::select());
}
}</code></pre>
</li>
</ul>
<h3>2. <strong>模型设置</strong></h3>
<ul>
<li>
<p>系统会自动识别 <strong>模型类名</strong> 对应 <strong>表名</strong>,User.php 对应 user 表(不含前缀);</p>
</li>
<li>
<p>但如果你的模型类名不是按照规则对应表名,则需要通过成员字段去设置;</p>
<pre><code class="language-php">class Abc extends Model
{
// 设置表名
protected $name = &quot;user&quot;;
}
// 使用$table时,指定表时需要完整的表名:tp_user</code></pre>
</li>
<li>
<p>系统也会默认id为你的主键名,如果不是id,则需要设置;</p>
<pre><code class="language-php">// 设置主键
protected $pk = &quot;uid&quot;;</code></pre>
</li>
<li>
<p>模型支持初始化功能,需要设置静态方法,并只在第一次实例化的时候执行,且只执行一次;</p>
<pre><code class="language-php">protected static function init()
{
echo &quot;初始化&quot;;
}</code></pre>
</li>
</ul>
<h1>19. <strong>模型的新增和删除</strong></h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>新增操作</strong></h3>
<ul>
<li>
<p>用模型新增数据,首先要实例化模型,开发工具会补全use,非集成工具别忘了;</p>
<pre><code class="language-php">use app\model\User;
// 手册new User,这里括号是工具补全的,都可以
$user = new User();</code></pre>
</li>
<li>
<p>使用实例化的方式添加一条数据,并使用 <strong>save()</strong> 方法进行保存;</p>
</li>
<li>
<p>注意:使用模型时,会自动给时间字段 <strong>create_time</strong>,<strong>update_time</strong> 写入当前时间;</p>
<pre><code class="language-php">$user = new User();
$user-&gt;name = &quot;李白&quot;;
$user-&gt;age = 28;
$user-&gt;gender = &quot;男&quot;;
$user-&gt;details = &quot;床前明月光,好诗!&quot;;
// 成功返回true,失败抛异常,其它看手册
$user-&gt;save();</code></pre>
</li>
<li>
<p>也可以通过 <strong>save()</strong> 传递数据数组的方式,来新增数据;</p>
<pre><code class="language-php">$user-&gt;save([
&quot;name&quot; =&gt; &quot;杜甫&quot;,
&quot;age&quot; =&gt; 19,
&quot;gender&quot; =&gt; &quot;男&quot;,
&quot;details&quot; =&gt; &quot;一行白鹭上青天,好诗!&quot;
]);</code></pre>
</li>
<li>
<p>使用 <strong>allowField()</strong> 方法,允许要写入的字段,其它字段就无法写入了;</p>
<pre><code class="language-php">$user-&gt;allowField([&quot;name&quot;,&quot;age&quot;,&quot;gender&quot;])-&gt;save([
&quot;name&quot; =&gt; &quot;蒲松龄&quot;,
&quot;age&quot; =&gt; 25,
&quot;gender&quot; =&gt; &quot;男&quot;,
&quot;details&quot; =&gt; &quot;十里平湖霜满天,好诗!&quot;
]);</code></pre>
</li>
<li>
<p>模型新增也提供了 <strong>replace()</strong> 方法来实现 <strong>REPLACE into</strong> 新增;</p>
<pre><code class="language-php">$user-&gt;replace()-&gt;save([
&quot;id&quot; =&gt; 15,
&quot;name&quot; =&gt; &quot;蒲松龄&quot;,
&quot;age&quot; =&gt; 25,
&quot;gender&quot; =&gt; &quot;男&quot;,
&quot;details&quot; =&gt; &quot;十里平湖霜满天,好诗!&quot;
]);</code></pre>
</li>
<li>
<p>当新增成功后,使用 <strong>$user->id</strong> ,可以获得自增 ID(主键需是 id);</p>
<pre><code class="language-php">return $user-&gt;id;</code></pre>
</li>
<li>
<p>使用 saveAll()方法,可以批量新增数据,返回批量新增的数组;</p>
<pre><code class="language-php">return $user-&gt;saveAll([
[
&quot;name&quot; =&gt; &quot;赵六&quot;,
&quot;age&quot; =&gt; 19,
&quot;gender&quot;=&gt; &quot;男&quot;
],
[
&quot;name&quot; =&gt; &quot;钱七&quot;,
&quot;age&quot; =&gt; 22,
&quot;gender&quot;=&gt; &quot;男&quot;,
&quot;details&quot; =&gt; &quot;我很有钱,排行老七!&quot;
]
]);</code></pre>
</li>
<li>
<p>使用 <strong>::create()</strong> 静态方法,来创建要新增的数据;</p>
<pre><code class="language-php">$user = User::create([
&quot;name&quot; =&gt; &quot;李逍遥&quot;,
&quot;age&quot; =&gt; 18,
&quot;gender&quot;=&gt; &quot;男&quot;,
&quot;details&quot; =&gt; &quot;我是一代主角!&quot;
], [&quot;name&quot;, &quot;age&quot;, &quot;gender&quot;, &quot;details&quot;], false);
//参数 1 是新增数据数组,必选
//参数 2 是允许写入的字段,可选
//参数 3 为是否 replace 写入,可选,默认 false 为 Insert 写入
return $user-&gt;id;</code></pre>
</li>
</ul>
<h3>2. <strong>删除操作</strong></h3>
<ul>
<li>
<p>使用 find() 方法,通过主键 (id) 查询到想要删除的数据;</p>
</li>
<li>
<p>然后再通过 delete()方法,将数据删除,返回布尔值;</p>
<pre><code class="language-php">// 根据主键值,删除数据
$user = User::find(20);
return $user-&gt;delete();</code></pre>
</li>
<li>
<p>也可以使用静态方法调用 destroy()方法,通过主键(id)删除数据;</p>
<pre><code class="language-php">// 单条删除
return User::destroy(21);
// 批量删除
return User::destroy([22, 33, 44]);
// 条件删除
return User::where(&quot;id&quot;, &quot;&gt;&quot;, 15)-&gt;delete();</code></pre>
</li>
<li>
<p>destroy() 方法,使用闭包的方式进行删除;</p>
<pre><code class="language-php">// 闭包模式
User::destroy(function ($query) {
$query-&gt;where(&quot;id&quot;, &quot;&gt;&quot;, 15);
});</code></pre>
</li>
</ul>
<h1>20. <strong>模型的更新操作</strong></h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>数据更新</strong></h3>
<ul>
<li>
<p>使用 find()方法获取数据,然后通过 save()方法保存修改,返回布尔值;</p>
<pre><code class="language-php">$user = User::find(19);
$user-&gt;details = &quot;我是一代主角!&quot;;
return $user-&gt;save();</code></pre>
</li>
<li>
<p>通过 where()方法结合 find()方法的查询条件获取的数据,进行修改;</p>
<pre><code class="language-php">$user = User::where(&quot;name&quot;, &quot;李逍遥&quot;)-&gt;find();
$user-&gt;details = &quot;我想做二代主角!&quot;;
return $user-&gt;save();</code></pre>
</li>
<li>
<p>save()方法只会更新变化的数据,如果提交的修改数据没有变化,则不更新;</p>
</li>
<li>
<p>但如果你想强制更新数据,即使数据一样,那么可以使用 force()方法;</p>
<pre><code class="language-php">// 如何验证被强制了,查看update_time字段是否更新了
$user-&gt;force()-&gt;save();</code></pre>
</li>
<li>
<p>Db::raw()执行 SQL 函数的方式,同样在这里有效;</p>
<pre><code class="language-php">$user-&gt;age = Db::raw(&quot;age + 2&quot;);</code></pre>
</li>
<li>
<p>关于验证过滤,后续学习Request再说,手册中 模型 -> 更新 里也有说明:</p>
<pre><code class="language-php">$user-&gt;allowField([&quot;name&quot;,&quot;age&quot;])-&gt;save(...)</code></pre>
</li>
<li>
<p>通过 saveAll()方法,可以批量修改数据,返回被修改的数据集合;</p>
<pre><code class="language-php">$user = new User;
return $user-&gt;saveAll([
[&quot;id&quot;=&gt;17, &quot;gender&quot;=&gt;&quot;女&quot;],
[&quot;id&quot;=&gt;18, &quot;gender&quot;=&gt;&quot;女&quot;],
[&quot;id&quot;=&gt;19, &quot;gender&quot;=&gt;&quot;女&quot;],
]);</code></pre>
</li>
<li>
<p>使用静态方法::update()更新,返回的是对象实例;</p>
<pre><code class="language-php">return User::update([&quot;id&quot;=&gt;17, &quot;gender&quot;=&gt;&quot;男&quot;]);
// ID放在后面,返回数据不含ID
return User::update([&quot;gender&quot;=&gt;&quot;男&quot;], [&quot;id&quot;=&gt;18]);
// 限制更新的内容,只允许gender被修改
return User::update([&quot;gender&quot;=&gt;&quot;男&quot;, &quot;name&quot;=&gt;&quot;可笑的人&quot;], [&quot;id&quot;=&gt;19], [&quot;gender&quot;]);</code></pre>
</li>
</ul>
<h1>21. <strong>模型的查询都一样</strong></h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>模型的查询</strong></h3>
<ul>
<li>
<p>模型的绝大部分语法基本都来自于 <strong>Db::name()</strong> 的查询:</p>
</li>
<li>
<p>在手册 模型 -> 查询 中可以查阅,这里就演示几个常用的意思一下:</p>
</li>
<li>
<p><strong>find()</strong> 单个 和 <strong>select()</strong> 多个;</p>
<pre><code class="language-php">$user = User::find(1);
$user = User::select();
$user = User::select([1, 3, 5]);</code></pre>
</li>
<li>
<p>也可以使用 where()方法进行条件筛选查询数据;</p>
<pre><code class="language-php">$user = User::where(&quot;id&quot;, &quot;&lt;&quot;, 5)-&gt;select();</code></pre>
</li>
<li>
<p>模型方法也可以使用 where 等连缀查询,和数据库查询方式一样;</p>
<pre><code class="language-php">$user = User::limit(3)-&gt;order(&quot;id&quot;, &quot;desc&quot;)-&gt;select();</code></pre>
</li>
<li>
<p>模型支持聚合查询:max、min、sum、count、avg 等;</p>
<pre><code class="language-php">$user = User::count();</code></pre>
</li>
<li>
<p>模型也支持大量的快捷方式,这里演示一个:</p>
<pre><code class="language-php">$user = User::whereLike(&quot;name&quot;, &quot;%王%&quot;)-&gt;select();</code></pre>
</li>
</ul>
<h1>22. <strong>模型的字段设置</strong></h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>字段设置</strong></h3>
<ul>
<li>
<p>模型的数据字段和表字段是对应关系,默认会自动获取,包括字段的类型;</p>
</li>
<li>
<p>自动获取会导致增加一次查询,如果在模型中配置字段信息,会减少内存开销;</p>
</li>
<li>
<p>可以在模型设置$schema 字段,明确定义字段信息,字段需要对应表写完整;</p>
</li>
<li>
<p>字段类型的定义可以使用PHP类型或者数据库的字段类型都可以,以便自动绑定类型;</p>
<pre><code class="language-php">// 设置字段信息,需要写完整的数据表字段
protected $schema = [
&quot;id&quot; =&gt; &quot;int&quot;,
&quot;name&quot; =&gt; &quot;string&quot;,
...
];</code></pre>
</li>
<li>
<p>设置模型字段,只能对模型有效,对于 Db::name() 查询无法作用。</p>
</li>
<li>
<p>要让模型和Db查询都支持字段类型设置,分三步:</p>
<ul>
<li>
<p>把上面的$schema先注释掉;</p>
</li>
<li>在 config/database.php 开启缓存字段;</li>
</ul>
<pre><code class="language-php">// 开启字段缓存
&quot;fields_cache&quot; =&gt; true,</code></pre>
<ul>
<li>在根目录命令行执行命令:</li>
</ul>
<pre><code>php think optimize:schema</code></pre>
</li>
</ul>
<h3>2. <strong>废弃字段</strong></h3>
<ul>
<li>
<p>由于历史遗留问题,我们不再想使用某些字段,可以在模型里设置;</p>
</li>
<li>
<p>设置后,我们在查询和写入时将忽略这些字段;</p>
<pre><code class="language-php">// 设置废弃字段
protected $disuse = [&quot;age&quot;, &quot;details&quot;];</code></pre>
</li>
</ul>
<h3>3. <strong>只读字段</strong></h3>
<ul>
<li>
<p>只读字段用来保护某些特殊的字段值不被更改,这个字段的值一旦写入,就无法更改;</p>
<pre><code class="language-php">// 设置只读字段
protected $readonly = [&quot;age&quot;, &quot;details&quot;];</code></pre>
</li>
<li>
<p>然后在控制器端进行修改测试:</p>
<pre><code class="language-php">// 修改查看只读字段
return User::update([&quot;id&quot;=&gt;19, &quot;age&quot;=&gt;22, &quot;name&quot;=&gt;&quot;李逍遥2&quot;, &quot;details&quot;=&gt;&quot;可笑&quot;]);</code></pre>
</li>
</ul>
<h1>23. <strong>获取器和修改器</strong></h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>获取器</strong></h3>
<ul>
<li>
<p>获取器的作用是对模型实例的数据做出自动处理;</p>
</li>
<li>
<p>一个获取器对应模型的一个特殊方法,该方法为 public;</p>
</li>
<li>
<p>方法名的命名规范为:<strong>getFieldAttr</strong>();</p>
</li>
<li>
<p>举个例子,数据库表示状态 status 字段采用的是数值;</p>
</li>
<li>
<p>而页面上,我们需要输出 status 字段希望是中文,就可以使用获取器;</p>
</li>
<li>
<p>在 User 模型端,我创建一个对外的方法,如下:</p>
<pre><code class="language-php">// 获取器,改变字段的值
public function getStatusAttr($value)
{
$status = [-1=&gt;&quot;删除&quot;, 0=&gt;&quot;冻结&quot;, 1=&gt;&quot;正常&quot;, 2=&gt;&quot;待审核&quot;];
return $status[$value];
}</code></pre>
</li>
<li>
<p>控制器端,正常输出数据:</p>
<pre><code class="language-php">public function attr()
{
$user = User::select();
return json($user);
}</code></pre>
</li>
<li>
<p>如果你定义了获取器,并且想获取原始值,可以使用 getData()方法;</p>
<pre><code class="language-php">$user = User::find(1);
echo $user-&gt;getData(&quot;status&quot;);</code></pre>
</li>
<li>
<p>使用 withAttr 在控制器端实现动态获取器,比如让年龄+100岁;</p>
<pre><code class="language-php">// 可以传入参数二 $data,获得所有数据,方便数据获取和判断
$user = User::select()-&gt;withAttr(&quot;age&quot;, function($value) {
return $value + 100;
});</code></pre>
</li>
</ul>
<h3>2. <strong>修改器</strong></h3>
<ul>
<li>
<p>模型修改器的作用,就是对模型设置对象的值进行处理;</p>
</li>
<li>
<p>比如,我们要新增数据的时候,对数据就行格式化、过滤、转换等处理;</p>
</li>
<li>
<p>模型修改器的命名规则为:<strong>setFieldAttr</strong>;</p>
</li>
<li>
<p>我们要设置一个新增,规定输入的年龄都自动+100岁,修改器如下:</p>
<pre><code class="language-php">// 修改器,写入时改变字段的值
public function setAgeAttr($value)
{
return $value + 100;
}</code></pre>
<pre><code class="language-php">return User::create([
&quot;name&quot; =&gt; &quot;酒剑仙&quot;,
&quot;age&quot; =&gt; 58,
&quot;gender&quot;=&gt; &quot;男&quot;,
&quot;details&quot; =&gt; &quot;我是隐藏主角!&quot;
]);</code></pre>
</li>
<li>除了新增,会调用修改器,修改更新也会触发修改器;</li>
<li>模型修改器只对模型方法有效,调用数据库的方法是无效的,比如->insert();</li>
</ul>
<h1>24. <strong>搜索器和自动时间戳</strong></h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>搜索器</strong></h3>
<ul>
<li>
<p>搜索器是用于封装字段(或搜索标识)的查询表达式,类似查询范围;</p>
</li>
<li>
<p>一个搜索器对应模型的一个特殊方法,该方法为 public;</p>
</li>
<li>
<p>方法名的命名规范为:<strong>searchFieldAttr()</strong>;</p>
</li>
<li>
<p>举个例子,我们要封装一个 <strong>name</strong> 字段的模糊查询,然后封装一个时间限定查询;</p>
</li>
<li>
<p>在 User 模型端,我创建两个对外的方法,如下:</p>
<pre><code class="language-php">// 搜索器,模糊查找姓名
public function searchNameAttr($query, $value, $data)
{
$query-&gt;where(&quot;name&quot;, &quot;like&quot;, &quot;%&quot;.$value.&quot;%&quot;);
}
// 搜索器,限定时间
public function searchCreateTimeAttr($query, $value, $data)
{
$query-&gt;whereBetweenTime(&quot;create_time&quot;, $value[0], $value[1]);
}</code></pre>
</li>
<li>
<p>在控制器端,通过 withSearch()方法实现模型搜索器的调用;</p>
<pre><code class="language-php">$user = User::withSearch([&quot;name&quot;, &quot;create_time&quot;],[
&quot;name&quot; =&gt; &quot;李&quot;,
&quot;create_time&quot; =&gt; [&quot;2023-10-19&quot;, &quot;2023-10-20 23:59:59&quot;]
])-&gt;select();</code></pre>
</li>
<li>
<p>withSearch()中第一个数组参数,限定搜索器的字段,第二个则是表达式值;</p>
</li>
<li>
<p>如果想在搜索器查询的基础上再增加查询条件,直接使用链式查询即可;</p>
<pre><code class="language-php">User::withSearch(...)-&gt;where(&quot;gender&quot;, &quot;女&quot;)-&gt;select();</code></pre>
</li>
<li>
<p>在获取器和修改器都有一个 <strong>$data</strong> 参数,它的作用是什么?</p>
<pre><code class="language-php">// 搜索器,模糊查找姓名
public function searchNameAttr($query, $value, $data)
{
$query-&gt;where(&quot;name&quot;, &quot;like&quot;, &quot;%&quot;.$value.&quot;%&quot;);
// 按年龄排序
if (isset($data[&quot;sort&quot;])) {
$query-&gt;order($data[&quot;sort&quot;]);
}
}</code></pre>
</li>
</ul>
<h3>2. <strong>自动时间戳</strong></h3>
<ul>
<li>
<p>如果你想全局开启,在 database.php 中,设置为 true;</p>
</li>
<li>
<p>此时,写入操作时,会自动对 <strong>create_time</strong> 和 <strong>update_time</strong> 进行写入;</p>
<pre><code class="language-php">&quot;auto_timestamp&quot; =&gt; true,</code></pre>
</li>
<li>
<p>如果你只想设置某一个模型开启,需要设置特有字段;</p>
<pre><code class="language-php">protected $autoWriteTimestamp = true;</code></pre>
</li>
<li>
<p>自动时间戳只能在模型下有效,数据库方法不可以使用;</p>
</li>
<li>
<p>如果创建和修改时间戳不是默认定义的,也可以自定义;</p>
<pre><code class="language-php">protected $createTime = &quot;create_at&quot;;
protected $updateTime = &quot;update_at&quot;;</code></pre>
</li>
<li>
<p>如果业务中只需要 create_time 而不需要 update_time,可以关闭它;</p>
<pre><code class="language-php">protected $updateTime = false;</code></pre>
</li>
<li>
<p>也可以动态实现不修改 update_time,具体如下:</p>
<pre><code class="language-php">$user-&gt;isAutoWriteTimestamp(false)-&gt;save();</code></pre>
</li>
</ul>
<h1>25. <strong>软删除和事件</strong></h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>软删除</strong></h3>
<ul>
<li>
<p>软删除也称为逻辑删除,只是给数据标记 “已删除” 的状态,不是真实的物理删除;</p>
</li>
<li>
<p>为何要对数据进行软删除,因为真实的物理删除,删了就没了呀。</p>
</li>
<li>
<p>在模型端设置软删除的功能,引入 <strong>SoftDelete</strong>,它是 <strong>trait</strong>;</p>
<pre><code class="language-php">// 会自动引入SoftDelete
use think\model\concern\SoftDelete;
// 开启软删除,创建delete_time字段,并设置默认为 NULL
use SoftDelete;
protected $deleteTime = &quot;delete_time&quot;;</code></pre>
</li>
<li>
<p>delete_time 默认设置的是 null,如果你想更改这个默认值,可以设置:</p>
<pre><code class="language-php">protected $defaultSoftDelete = 0;</code></pre>
</li>
<li>
<p>由于我们之前演示过字段缓存,会导致无法软删除,你可以删除字段缓存,或者重新更新下:</p>
<pre><code class="language-php">php think optimize:schema</code></pre>
</li>
<li>
<p>删除分为两种:destroy() 和 delete(),具体如下:</p>
<pre><code class="language-php">// 软删除
User::destroy(1);
// 真实删除
User::destroy(1,true);
$user = User::find(1);
// 软删除
$user-&gt;delete();
// 真实删除
$user-&gt;force()-&gt;delete();</code></pre>
</li>
<li>
<p>软删除后,数据库内的数据只是被标记了删除时间,而搜索数据时,会自动屏蔽这些数据;</p>
</li>
<li>
<p>在开启软删除功能的前提下,使用 <strong>withTrashed()</strong> 方法取消屏蔽软删除的数据;</p>
<pre><code class="language-php">User::withTrashed()-&gt;select();</code></pre>
</li>
<li>
<p>如果只想查询被软删除的数据,使用 onlyTrashed()方法即可;</p>
<pre><code class="language-php">User::onlyTrashed()-&gt;select()</code></pre>
</li>
<li>
<p>如果想让某一条被软删除的数据恢复到正常数据,可以使用 restore()方法;</p>
<pre><code class="language-php">$user = User::onlyTrashed()-&gt;find(23);
$user-&gt;restore();</code></pre>
</li>
<li>如何要将软删除后的数据库真实的物理删除,需要先将它恢复,再真实删除;</li>
</ul>
<h3>2. 事件</h3>
<ul>
<li>
<p>在手册 -> 模型 -> 模型事件中罗列了所有的模型,这里演示常用的意思一下;</p>
</li>
<li>
<p>在模型端使用静态方法调用即可完成事件触发,比如执行了查询方法后调用事件:</p>
<pre><code class="language-php">// 当执行了查询方法,则调用我
protected static function onAfterRead($user)
{
// $query是数据集,可用于数据判断
echo &quot;执行了查询!&quot;.$user-&gt;id;
}</code></pre>
</li>
</ul>
<h1>26. 关联模型入门</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>关联表</strong></h3>
<ul>
<li>
<p>我们已经有了一张 <strong>tp_user</strong> 表,主键为:<strong>id</strong>;我们需要一个附属表,来进行关联;</p>
</li>
<li>
<p>附属表:<strong>tp_profile</strong>,建立两个字段:<strong>user_id</strong> 和 <strong>hobby</strong>,外键是 <strong>user_id</strong>;</p>
<p><img src="ThinkPHP8.x.assets/26-1.jpg" style="zoom:60%;margin:0" /></p>
</li>
</ul>
<h3>2. <strong>关联查询</strong></h3>
<ul>
<li>
<p>关联模型,顾名思义,就是将表与表之间进行关联和对象化,更高效的操作数据;</p>
</li>
<li>
<p>创建 <strong>User</strong> 模型和 <strong>Profile</strong> 模型,均为空模型;如果已有User,改名UserBak备份起来;</p>
<pre><code class="language-php">namespace app\model;
use think\Model;
class User extends Model {}</code></pre>
<pre><code class="language-php">namespace app\model;
use think\Model;
class Profile extends Model {}</code></pre>
</li>
<li>
<p><strong>User</strong> 模型端,需要关联 <strong>Profile</strong>,具体方式如下:</p>
<pre><code class="language-php">class User extends Model {
public function profile()
{
// 一对一关联,
// 参数1:关联的表模型
// 参数2:默认为 user_id (外键)
return $this-&gt;hasOne(Profile::class);
}
}</code></pre>
</li>
<li>
<p>创建一个控制器用于测试输出:Link.php;</p>
<pre><code class="language-php">public function index()
{
// 主表
$user = User::find(19);
// 访问关联从表
return json($user-&gt;profile);
}</code></pre>
</li>
</ul>
<h1>27. 一对一关联查询</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>hasOne 模式</strong></h3>
<ul>
<li>
<p><strong>hasOne</strong> 模式,适合主表关联附表,具体设置方式如下:</p>
<pre><code class="language-php">hasOne(&quot;关联模型&quot;,[&quot;外键&quot;,&quot;主键&quot;]);
return $this-&gt;hasOne(Profile::class,&quot;user_id&quot;, &quot;id&quot;);
//关联模型(必须):关联的模型名或者类名
//外键:默认的外键规则是当前模型名(不含命名空间,下同)+_id ,例如 user_id
//主键:当前模型主键,默认会自动获取也可以指定传入</code></pre>
</li>
<li>
<p>在上一节课,我们了解了表与表关联后,实现的查询方案:</p>
<pre><code class="language-php">// 主表
$user = User::find(19);
// 访问关联从表
return json($user-&gt;profile-&gt;hobby);</code></pre>
</li>
<li>
<p>使用 save()方法,可以设置关联修改,通过主表修改附表字段的值:</p>
<pre><code class="language-php">$user = User::find(19);
return $user-&gt;profile-&gt;save([&quot;hobby&quot;=&gt;&quot;和蛇妖玩耍!&quot;]);</code></pre>
</li>
<li>
<p>-><strong>profile</strong> 属性方式可以修改数据,-><strong>profile</strong>()方法方式可以新增数据:</p>
<pre><code class="language-php">// 新增附表数据,先找到主表数据
$user = User::find(1);
// 然后通过profile()方法实现新增
return $user-&gt;profile()-&gt;save([&quot;hobby&quot;=&gt;&quot;不喜欢吃青椒!&quot;]);</code></pre>
</li>
</ul>
<h3>2. <strong>belongsTo 模式</strong></h3>
<ul>
<li>
<p>belongsTo 模式,适合附表关联主表,具体设置方式如下:</p>
<pre><code class="language-php">// 注意:此时绑定需要Profile模型创建user()方法执行
belongsTo(&quot;关联模型&quot;,[&quot;外键&quot;,&quot;关联主键&quot;]);
class Profile extends Model {
public function user()
{
return $this-&gt;belongsTo(User::class);
}
}</code></pre>
</li>
<li>
<p>查询方式,和主附查询方式一致:</p>
<pre><code class="language-php">// 附表
$profile = Profile::find(1);
// 访问关联主表
return $profile-&gt;user-&gt;name;</code></pre>
</li>
<li>
<p>使用 hasOne()也能模拟 belongsTo() 来进行查询,这样就可以不用在 Profile 模型进行设置:</p>
<pre><code class="language-php">// hasWhere
// 注意:参数1的profile是方法名,不是模型名
$user = User::hasWhere(&quot;profile&quot;, [&quot;id&quot;=&gt;2])-&gt;find();
return json($user);
// 闭包方式
$user = User::hasWhere(&quot;profile&quot;, function ($query) {
$query-&gt;where(&quot;id&quot;, 2);
})-&gt;find();
return json($user);</code></pre>
</li>
</ul>
<h1>28. 一对多关联查询</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>hasMany 模式</strong></h3>
<ul>
<li>
<p>hasMany 模式,适合主表关联附表,实现一对多查询,具体设置方式如下:</p>
<pre><code class="language-php">// 由于是一对多,需要在附表添加多个相同user_id的数据测试
hasMany(&quot;关联模型&quot;,[&quot;外键&quot;,&quot;主键&quot;]);
return $this-&gt;hasMany(Profile::class,&quot;user_id&quot;, &quot;id&quot;);</code></pre>
</li>
<li>
<p>查询方案和一对一相同:</p>
<pre><code class="language-php">// 主表一对多
$user = User::find(1);
return json($user-&gt;profile);</code></pre>
</li>
<li>
<p>使用->profile()方法模式,可以进一步进行数据的筛选;</p>
<pre><code class="language-php">// 主表一对多
$user = User::find(1);
// 进一步筛选数据,保留实际顺序的下标
$data = $user-&gt;profile-&gt;where(&quot;id&quot;, &quot;&gt;&quot;, 10);
// 进一步筛选数据,下标重新从0开始,需要连缀select()
$data = $user-&gt;profile()-&gt;where(&quot;id&quot;, &quot;&gt;&quot;, 10)-&gt;select();
return json($data);</code></pre>
</li>
<li>
<p>使用 has()方法,查询关联附表的主表内容,比如大于等于 2 条的主表记录;</p>
<pre><code class="language-php">// 参数1:profile是方法名
$user = User::has(&quot;profile&quot;, &quot;&gt;=&quot;, 2)-&gt;select();
return json($user);</code></pre>
</li>
<li>
<p>使用 hasWhere()方法,查询附表中,可见兴趣的主表记录;</p>
<pre><code class="language-php">// 查询附表profile中visible为1的兴趣关联主表的记录
$user = User::hasWhere(&quot;profile&quot;, [&quot;visible&quot;=&gt;1])-&gt;select();
return json($user);</code></pre>
</li>
<li>
<p>使用 save()和 saveAll()进行关联新增和批量关联新增,方法如下:</p>
<pre><code class="language-php">// 主表数据
$user = User::find(24);
// 新增附表关联数据
$user-&gt;profile()-&gt;save([&quot;hobby&quot;=&gt;&quot;测试喜欢1&quot;, &quot;visible&quot;=&gt;1]);
// 批量新增
$user-&gt;profile()-&gt;saveAll([
[&quot;hobby&quot;=&gt;&quot;测试喜欢2&quot;, &quot;visible&quot;=&gt;1],
[&quot;hobby&quot;=&gt;&quot;测试喜欢3&quot;, &quot;visible&quot;=&gt;1],
]);</code></pre>
</li>
<li>
<p>使用 together()方法,可以删除主表内容时,将附表关联的内容全部删除;</p>
<pre><code class="language-php">// 删除主数据,并清空关联附表数据
$user = User::with(&quot;profile&quot;)-&gt;find(29);
$user-&gt;together([&quot;profile&quot;])-&gt;delete();</code></pre>
</li>
<li>
<p>特别注意:由于外键约束设置问题,默认情况下,关联操作可能会导致1451错误;</p>
</li>
<li>
<p>解决方案:在 profile 表中 设置外键 删除和修改时 为:CASCADE即可,详细阅读如下:</p>
<pre><code class="language-php">https://blog.csdn.net/qq_23994787/article/details/86063623</code></pre>
</li>
</ul>
<h1>29. 模型预载入和统计</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>预载入</strong></h3>
<ul>
<li>
<p>在普通的关联查询下,我们循环数据列表会执行 n+1 次 SQL 查询;</p>
<pre><code class="language-php">// 主表三条记录
$list = User::select([10, 11, 12]);
// 遍历附表
foreach ($list as $user)
{
dump($user-&gt;profile);
}</code></pre>
</li>
<li>
<p>上面继续采用普通关联查询的构建方式,打开 trace 调试工具,会得到四次查询;</p>
</li>
<li>
<p>如果采用关联预载入的方式,将会减少到两次,也就是起步一次,循环一次;</p>
<pre><code class="language-php">$list = User::with([&quot;profile&quot;])-&gt;select([10, 11, 12, 17, 18, 19]);
foreach ($list as $user)
{
dump($user-&gt;profile);
}</code></pre>
<pre><code class="language-php">// 查看显示结构
$list = User::with([&quot;profile&quot;])-&gt;select([1, 10, 22]);
return json($list);</code></pre>
</li>
<li>
<p>关联预载入减少了查询次数提高了性能,但是不支持多次调用;</p>
</li>
<li>
<p>如果你有主表关联了多个附表,都想要进行预载入,可以传入多个模型方法即可;</p>
<pre><code class="language-php">User::with([&quot;profile&quot;, &quot;book&quot;])</code></pre>
</li>
<li>
<p>上面显示结构中,主表和附表的字段已经非常多了,需要对两个表字段进行筛略:</p>
<pre><code class="language-php">// 注意1:withField对应另一个是withoutField
// 注意2:关联字段一定要包含外键:user_id,否则空
$list = User::field(&quot;id,age,gender,details&quot;)-&gt;with([&quot;profile&quot; =&gt; function($query) {
$query-&gt;withField([&quot;user_id, hobby&quot;]);
}])-&gt;select([1, 10, 22]);
return json($list);
// 或者简单些
$list = User::with(&quot;profile&quot;)-&gt;select();
// 直接字段,隐藏主表,加上profile隐藏附表,除了hidden,还有对应的visible方法
return json($list-&gt;hidden([&quot;status&quot;, &quot;profile.visible&quot;]));</code></pre>
</li>
<li>还有一些 预载入缓存、延迟预载入,可以参考手册;</li>
</ul>
<h3>2. <strong>关联统计</strong></h3>
<ul>
<li>
<p>使用 withCount()方法,可以统计主表关联附表的个数,输出用 profile_count;</p>
<pre><code class="language-php">// 统计这三条数据关联的附表数据的个数
$list = User::withCount([&quot;profile&quot;])-&gt;select([1, 10, 22]);
foreach ($list as $user)
{
echo $user-&gt;profile_count.&quot;&lt;br&gt;&quot;;
}</code></pre>
</li>
<li>
<p>关联统计的输出采用“关联方法名” <strong>_count</strong>,这种结构输出;</p>
</li>
<li>
<p>不单单支持 Count,还有如下统计方法,均可支持;</p>
</li>
<li>
<p><strong>withMax()</strong>、<strong>withMin()</strong>、<strong>withSum()</strong>、<strong>withAvg()</strong>等;</p>
</li>
<li>
<p>除了 withCount()不需要指定字段,其它均需要指定统计字段;</p>
<pre><code class="language-php">// 统计附表关联字段的累加和
$list = User::withSum([&quot;profile&quot;], &quot;visible&quot;)-&gt;select([1, 10, 22]);
foreach ($list as $user) {
echo $user-&gt;profile_sum.&quot;&lt;br&gt;&quot;;
}</code></pre>
</li>
<li>
<p>对于输出的属性,可以自定义:</p>
<pre><code class="language-php">User::withSum([&quot;profile&quot;=&gt;&quot;p_s&quot;], &quot;visible&quot;)
$user-&gt;p_s</code></pre>
</li>
</ul>
<h1>30. 多对多关联查询</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>建立三张表</strong></h3>
<ul>
<li>
<p>复习一下一对一,一个用户对应一个用户档案资料,是一对一关联;</p>
</li>
<li>
<p>复习一下一对多,一篇文章对应多个评论,是一对多关联;</p>
</li>
<li>
<p>多对多怎么理解,分解来看,一个用户对应多个角色,而一个角色对应多个用户;</p>
</li>
<li>
<p>那么这种对应关系,就是多对多关系,最经典的应用就是权限控制;</p>
<p><img src="ThinkPHP8.x.assets/30-1.jpg" style="zoom:65%;margin:0" /></p>
</li>
<li>
<p>tp_user:用户表;tp_role:角色表;tp_access:中间表;</p>
</li>
<li>
<p>access 表包含了 user 和 role 表的关联 id,多对多模式;</p>
<pre><code class="language-php">class User extends Model {
public function role()
{
// belongsToMany(&quot;关联模型&quot;,&quot;中间表&quot;,[&quot;外键&quot;,&quot;关联键&quot;]);
return $this-&gt;belongsToMany(Role::class, Access::class);
}
}</code></pre>
<pre><code class="language-php">class Role extends Model{}</code></pre>
<pre><code class="language-php">// 这里继承的是Pivot,它本身也继承了Model
// Pivoit是中间表基类,多对多专用模型
class Access extends Pivot{}</code></pre>
</li>
</ul>
<h3>2. <strong>权限控制</strong></h3>
<ul>
<li>
<p>在控制器段,我们查询一下id为1的用户,并关联查询它的权限:</p>
<pre><code class="language-php">// 获取一个用户,张三
$user = User::find(1);
// 获取这个用户所有角色
$role = $user-&gt;role;
return json($role);</code></pre>
</li>
<li>
<p>当我们要给一个用户创建一个角色时,用到多对多关联新增;</p>
<pre><code class="language-php">// 如果这个角色不存在,则会给角色表增加一条信息
// 并且,会在中间表关联角色和用户
$user-&gt;role()-&gt;save([&quot;type&quot;=&gt;&quot;测试管理专员&quot;]);
// 也支持批量
$user-&gt;role()-&gt;saveAll([[...],[...]]);</code></pre>
</li>
<li>
<p>一般来说,上面的这种新增方式,用于初始化角色比较合适;</p>
</li>
<li>
<p>但是,很多情况下,角色权限是初始化好的,只需要添加中间表,而不是角色表;</p>
</li>
<li>
<p>那么,我们真正需要就是通过用户表新增到中间表关联即可;</p>
<pre><code class="language-php">// 给张三添加一个已经存在的角色,直接传角色ID即可
$user-&gt;role()-&gt;save(1);
// 或
$user-&gt;role()-&gt;save(Role::find(1));
$user-&gt;role()-&gt;saveAll([1,2,3]);
// 或,如果有其它字段,可以通过中括号添加
$user-&gt;role()-&gt;attach(1);
$user-&gt;role()-&gt;attach(2, [&quot;details&quot;=&gt;&quot;测试详情&quot;]);</code></pre>
</li>
<li>
<p>除了新增,还有直接删除中间表数据的方法:</p>
<pre><code class="language-php">// 取消掉张三的所有,1,2,6,这里的值是角色的ID,不是中间表ID
$user-&gt;role()-&gt;detach(1);
$user-&gt;role()-&gt;detach([2, 6]);</code></pre>
</li>
</ul>
<h1>31. 路由定义入门</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>路由入门</strong></h3>
<ul>
<li>
<p>路由的作用就是让 URL 地址更加的规范和优雅,或者说更加简洁;</p>
</li>
<li>
<p>设置路由对 URL 的检测、验证等一系列操作提供了极大的便利性;</p>
</li>
<li>
<p>路由是默认开启的,如果想要关闭路由,在 <strong>config/app.php</strong> 配置;</p>
<pre><code class="language-php">// 是否启用路由
&quot;with_route&quot; =&gt; true,</code></pre>
</li>
<li>
<p>路由的配置文件在 <strong>config/route.php</strong> 中,定义文件在 <strong>route/app.php</strong>;</p>
</li>
<li>
<p>我们还回到最初的 Index 控制器,创建一个 details 带 参数的方法;</p>
<pre><code class="language-php">public function details($id)
{
return &quot;details ID:&quot;.$id;
}</code></pre>
<pre><code class="language-php">http://www.tp.com:8000/index/details/id/5</code></pre>
</li>
<li>
<p>此时,我们在根目录 route 下的 app.php 里配置路由:</p>
<pre><code class="language-php">// 参数1:url/参数
// 参数2:控制器/方法
Route::rule(&quot;details/:id&quot;, &quot;Index/details&quot;);
// 访问地址
http://www.tp.com:8000/details/5</code></pre>
</li>
<li>
<p>rule()方法是默认请求是 any,即任何请求类型均可,第三参数可以限制:</p>
<pre><code class="language-php">Route::rule(&quot;details/:id&quot;, &quot;Index/xxx&quot;, &quot;GET&quot;);
Route::rule(&quot;details/:id&quot;, &quot;Index/xxx&quot;, &quot;POST&quot;);
Route::rule(&quot;details/:id&quot;, &quot;Index/xxx&quot;, &quot;GET|POST&quot;);</code></pre>
</li>
<li>
<p>所有的请求方式均有快捷方式,比如 <strong>::get()</strong> 、<strong>::post()</strong> 等,具体查看手册:路由 -> 路由定义;</p>
<pre><code class="language-php">// 快捷方式,无须第三参数了
Route::get(...)
Route::post(...)
Route::delete(...)</code></pre>
</li>
<li>
<p>在路由的规则表达式中,有多种地址的配置规则:</p>
<pre><code class="language-php">// 静态路由
Route::rule(&quot;test&quot;, &quot;Index/test&quot;);
// 带一个参数
Route::rule(&quot;details/:id&quot;, &quot;Index/details&quot;);
// 带两个参数
Route::rule(&quot;details/:id/:uid&quot;, &quot;Index/details&quot;);
// 带可选参数,一般在后面
Route::rule(&quot;details/:id/[:uid]&quot;, &quot;Index/details&quot;);
// 全动态地址,不限定details,url可以是:abc/5/6,后面details也可以动态
Route::rule(&quot;:details/:id/:uid&quot;, &quot;Index/details&quot;);
// 正则规则,完全匹配
Route::rule(&quot;details/:id/:uid$&quot;, &quot;Index/details&quot;);</code></pre>
</li>
</ul>
<h3>2. <strong>强制路由</strong></h3>
<ul>
<li>
<p>目前来说,路由访问模式和URL访问模式都可以使用,但我们强制路由访问;</p>
</li>
<li>
<p>开始强制路由,需要在 route.php 里面进行配置:</p>
<pre><code class="language-php">// 是否强制使用路由
&quot;url_route_must&quot; =&gt; true,</code></pre>
<pre><code class="language-php">// 首页也必须设置路由
Route::rule(&quot;/&quot;, &quot;Index/index&quot;);</code></pre>
</li>
</ul>
<h1>32. 路由闭包.变量规则</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>闭包</strong></h3>
<ul>
<li>
<p>闭包支持我们可以通过 URL 直接执行,而不需要通过控制器和方法;</p>
<pre><code class="language-php">// 闭包
Route::get(&quot;think&quot;, function () {
return &quot;hello, ThinkPHP8!&quot;;
});</code></pre>
</li>
<li>
<p>闭包支持也可以传递参数和动态规则:</p>
<pre><code class="language-php">// 带参数闭包,如果不带:version,那么地址:php?version=8
Route::get(&quot;php/:version&quot;, function ($version) {
return &quot;PHP&quot;.$version;
});</code></pre>
</li>
</ul>
<h3>2. <strong>变量规则</strong></h3>
<ul>
<li>
<p>系统默认的路由变量规则为\w+,即字母、数字、中文和下划线;</p>
</li>
<li>
<p>如果你想更改默认的匹配规则,可以修改 <strong>config/route.php</strong> 配置;</p>
<pre><code class="language-php">// 默认的路由变量规则
&quot;default_route_pattern&quot; =&gt; &quot;[\w\.]+&quot;,</code></pre>
</li>
<li>
<p>如果我们需要对于具体的变量进行单独的规则设置,则需要通过 <strong>pattern()</strong> 方法;</p>
</li>
<li>
<p>将 details 方法里的 id 传值,严格限制必须只能是数字\d+;</p>
<pre><code class="language-php">// 正则规则 \d+ 限定id为数字
Route::rule(&quot;details/:id&quot;, &quot;Index/details&quot;)
-&gt;pattern([&quot;id&quot;=&gt;&quot;\d+&quot;]);
// 多个参数
-&gt;pattern([
&quot;id&quot; =&gt; &quot;\d+&quot;,
&quot;uid&quot; =&gt; &quot;\d+&quot;
]);</code></pre>
</li>
<li>
<p>如果让指定的参数统一限定为数字,比如id和uid,也就是全局设置,在app.php顶部设置:</p>
<pre><code class="language-php">Route::pattern([
&quot;id&quot; =&gt; &quot;\d+&quot;,
&quot;uid&quot; =&gt; &quot;\d+&quot;
]);</code></pre>
</li>
<li>
<p>不喜欢斜杠怎么办?能换成减号吗?可以的:</p>
<pre><code class="language-php">// 支持替换斜杠
Route::rule(&quot;details-:id&quot;, &quot;Index/details&quot;);
// 支持组合变量&lt;id&gt;方式
Route::rule(&quot;details-&lt;id&gt;&quot;, &quot;Index/details&quot;);</code></pre>
</li>
</ul>
<h1>33. 路由参数.域名.MISS</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>参数</strong></h3>
<ul>
<li>
<p>设置路由的时候,可以设置相关方法进行,从而实施匹配检测和行为执行;</p>
</li>
<li>
<p>ext 方法作用是检测 URL 后缀,比如:我们强制所有 URL 后缀为.html;</p>
<pre><code class="language-php">Route::rule(&quot;test&quot;, &quot;Index/test&quot;)-&gt;ext(&quot;html&quot;);
Route::rule(&quot;test&quot;, &quot;Index/test&quot;)-&gt;ext(&quot;html|shtml&quot;);</code></pre>
</li>
<li>
<p>https 方法作用是检测是否为 https 请求,结合 ext 强制 html 如下:</p>
<pre><code class="language-php">Route::rule(&quot;test&quot;, &quot;Index/test&quot;)-&gt;ext(&quot;html&quot;)-&gt;https();</code></pre>
</li>
<li>
<p>如果想让全局统一配置 URL 后缀的话,可以在 config/route.php 中设置;</p>
</li>
<li>
<p>具体值可以是单个或多个后缀,也可以是空字符串(任意后缀),false 禁止后缀;</p>
<pre><code class="language-php">// URL伪静态后缀
&quot;url_html_suffix&quot; =&gt; &quot;html&quot;,</code></pre>
</li>
<li>
<p>denyExt 方法作用是禁止某些后缀的使用,使用后直接报错;</p>
<pre><code class="language-php">// 可以将url_html_suffix 设置为空测试
Route::rule(&quot;test&quot;, &quot;Index/test&quot;)-&gt;denyExt(&quot;jpg|gif|png&quot;);</code></pre>
</li>
<li>
<p>domain 方法作用是检测当前的域名是否匹配,完整域名和子域名均可;</p>
<pre><code class="language-php">Route::rule(&quot;test&quot;, &quot;Index/test&quot;)-&gt;domain(&quot;localhost&quot;);
Route::rule(&quot;test&quot;, &quot;Index/test&quot;)-&gt;domain(&quot;new.tp.com&quot;);
Route::rule(&quot;test&quot;, &quot;Index/test&quot;)-&gt;domain(&quot;new&quot;);</code></pre>
</li>
<li>
<p>还有ajax/pjax/json检测、filter 额外参数检测、append追加额外参数、option统一管理检测,可参考手册;</p>
<pre><code class="language-php">Route::rule(&quot;test&quot;, &quot;Index/test&quot;)-&gt;option([
&quot;ext&quot; =&gt; &quot;html&quot;,
&quot;https&quot; =&gt; false,
&quot;domain&quot; =&gt; &quot;www.tp.com&quot;
]);</code></pre>
</li>
</ul>
<h3>2. <strong>域名</strong></h3>
<ul>
<li>
<p>如果想限定的某个域名下生效的路由,比如 <strong>news.tp.com</strong> 可以通过域名闭包方式:</p>
<pre><code class="language-php">Route::domain(&quot;news.tp.com&quot;, function () {
Route::rule(&quot;details/:id&quot;, &quot;Index/details&quot;);
});
// 或
Route::domain(&quot;news&quot;, function () {
Route::rule(&quot;details/:id&quot;, &quot;Index/details&quot;);
});</code></pre>
</li>
<li>路由域名也支持:ext、pattern、append 等路由参数方法的操作;</li>
</ul>
<h3>3. <strong>MISS</strong></h3>
<ul>
<li>
<p>全局 MISS,类似开启强制路由功能,匹配不到相应规则时自动跳转到 MISS;</p>
<pre><code class="language-php">Route::miss(&quot;Error/miss&quot;);
// 闭包模式
Route::miss(function () {
return &quot;MISS 404&quot;;
});</code></pre>
</li>
</ul>
<h1>34. 路由分组.URL生成</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>分组</strong></h3>
<ul>
<li>
<p>路由分组,即将相同前缀的路由合并分组,这样可以简化路由定义,提高匹配效率;</p>
</li>
<li>
<p>使用 group()方法,来进行分组路由的注册;</p>
<pre><code class="language-php">Route::group(&quot;index&quot;, function () {
Route::rule(&quot;:id&quot;, &quot;Index/details&quot;);
Route::rule(&quot;:name&quot;, &quot;Index/hello&quot;);
})-&gt;ext(&quot;html&quot;)-&gt;pattern([&quot;id&quot;=&gt;&quot;\d+&quot;]);
// URL1: http://www.tp.com:8000/index/5.html
// URL2: http://www.tp.com:8000/index/world.html</code></pre>
</li>
<li>
<p>也可以省去第一参数,让分组路由更灵活一些;</p>
<pre><code class="language-php">Route::group(function() {
Route::rule(&quot;test&quot;, &quot;Index/test&quot;);
Route::rule(&quot;h/:name&quot;, &quot;Index/hello&quot;);
})-&gt;ext(&quot;html&quot;);
// URL1: http://www.tp.com:8000/test.html
// URL2: http://www.tp.com:8000/h/world.html</code></pre>
</li>
<li>
<p>使用 prefix()方法,可以省略掉分组地址里的控制器;</p>
<pre><code class="language-php">Route::group(&quot;index&quot;, function () {
Route::rule(&quot;test&quot;, &quot;test&quot;);
Route::rule(&quot;:name&quot;, &quot;hello&quot;);
})-&gt;prefix(&quot;Index/&quot;)-&gt;ext(&quot;html&quot;);
// URL1: http://www.tp.com:8000/index/test.html
// URL2: http://www.tp.com:8000/index/world.html</code></pre>
</li>
</ul>
<h3>2. <strong>URL生成</strong></h3>
<ul>
<li>
<p>使用 <strong>url()</strong> 助手函数来生成 定义好的路由地址,放在在控制器使用;</p>
<pre><code class="language-php">// 静态不带参数的
Route::rule(&quot;test&quot;, &quot;Index/test&quot;)-&gt;ext(&quot;html&quot;);
// 控制器段获取url:/test.html
return url(&quot;Index/test&quot;);
// 动态带参数的
Route::rule(&quot;details/:id&quot;, &quot;Index/details&quot;)-&gt;ext(&quot;html&quot;);
// 控制器段获取url:/details/5.html
return url(&quot;Index/details&quot;, [&quot;id&quot;=&gt;5]);
// url参数一和路由的rule的参数二是一致的,可以通过name方法复刻;
Route::rule(&quot;details/:id&quot;, &quot;Index/details&quot;)-&gt;name(&quot;de&quot;)-&gt;ext(&quot;html&quot;);
// 控制器段获取url:/details/5.html
return url(&quot;de&quot;, [&quot;id&quot;=&gt;5]);
// 完整带域名的地址:http://www.tp.com:8000/details/5.html
return url(&quot;de&quot;, [&quot;id&quot;=&gt;5])-&gt;domain(true);
return url(&quot;de&quot;, [&quot;id&quot;=&gt;5])-&gt;domain(&quot;www.tp.com&quot;);</code></pre>
</li>
<li>在手册 -> 路由 -> URL 生成 有 <strong>Route::buildUrl()</strong> 源方法,只不过助手函数,更方便;</li>
</ul>
<h1>35. 资源路由</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>创建资源</strong></h3>
<ul>
<li>
<p>资源路由,采用固定的常用方法来实现简化 URL 的功能;</p>
<pre><code class="language-php">Route::resource(&quot;blog&quot;, &quot;Blog&quot;);</code></pre>
</li>
<li>
<p>系统提供了一个命令,方便开发者快速生成一个资源控制器;</p>
<pre><code class="language-php">php think make:controller Blog</code></pre>
</li>
<li>
<p>以上的两部操作,创建了一个控制器Blog类,并生成了增删改查操作的方法,而且还实现了全部路由:</p>
<table>
<thead>
<tr>
<th style="text-align: center;">标识</th>
<th style="text-align: center;">请求类型</th>
<th style="text-align: center;">路由规则</th>
<th style="text-align: center;">操作方法</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">index</td>
<td style="text-align: center;">GET</td>
<td style="text-align: center;">blog</td>
<td style="text-align: center;">index</td>
</tr>
<tr>
<td style="text-align: center;">create</td>
<td style="text-align: center;">GET</td>
<td style="text-align: center;">blog/create</td>
<td style="text-align: center;">create</td>
</tr>
<tr>
<td style="text-align: center;">save</td>
<td style="text-align: center;">POST</td>
<td style="text-align: center;">blog</td>
<td style="text-align: center;">save</td>
</tr>
<tr>
<td style="text-align: center;">read</td>
<td style="text-align: center;">GET</td>
<td style="text-align: center;">blog/:id</td>
<td style="text-align: center;">read</td>
</tr>
<tr>
<td style="text-align: center;">edit</td>
<td style="text-align: center;">GET</td>
<td style="text-align: center;">blog/:id/edit</td>
<td style="text-align: center;">edit</td>
</tr>
<tr>
<td style="text-align: center;">update</td>
<td style="text-align: center;">PUT</td>
<td style="text-align: center;">blog/:id</td>
<td style="text-align: center;">update</td>
</tr>
<tr>
<td style="text-align: center;">delete</td>
<td style="text-align: center;">DELETE</td>
<td style="text-align: center;">blog/:id</td>
<td style="text-align: center;">delete</td>
</tr>
</tbody>
</table>
</li>
</ul>
<h3>2. <strong>地址URL</strong></h3>
<ul>
<li>
<p>资源路由注册好后,所有地址都是全自动生成,具体如下:</p>
<pre><code class="language-php">http://www.tp.com:8000/blog //GET 访问的是index方法,用于显示数据
http://www.tp.com:8000/blog/create //GET 访问的是create方法,新增数据的表单页面
http://www.tp.com:8000/blog/5 //GET 访问的是read方法,读取指定id的一条数据
http://www.tp.com:8000/blog/5/edit //GET 访问的是edit方法,读取指定id数据并显示修改表单</code></pre>
<pre><code class="language-php">http://www.tp.com:8000/blog //POST 访问的是save方法,用于接收表单提交的新增数据
http://www.tp.com:8000/blog/5 //PUT 访问的是update方法,用于接收表单提交的修改数据
http://www.tp.com:8000/blog/5 //DELETE 访问的是delete方法,用于接收数据删除信息</code></pre>
</li>
<li>
<p>默认的参数采用 id 名称,如果你想别的,比如:blog_id;</p>
<pre><code class="language-php">//相应的 delete($blog_id)
...-&gt;vars([&quot;blog&quot;=&gt;&quot;blog_id&quot;]); </code></pre>
</li>
<li>
<p>也可以通过 <strong>only()</strong> 方法限定系统提供的资源方法:</p>
<pre><code class="language-php">// 只允许指定的这些操作
...-&gt;only([&quot;index&quot;,&quot;save&quot;,&quot;create&quot;]);</code></pre>
</li>
<li>
<p>还可以通过 <strong>except()</strong> 方法排除系统提供的资源方法:</p>
<pre><code class="language-php">// only相反操作
...-&gt;except([&quot;read&quot;,&quot;delete&quot;,&quot;update&quot;])</code></pre>
</li>
<li>
<p>使用 <strong>rest()</strong> 方法,更改系统给予的默认方法,1.请求方式;2.地址;3.操作;</p>
<pre><code class="language-php">Route::rest(&quot;create&quot;, [&quot;GET&quot;, &quot;/add&quot;, &quot;add&quot;]);
// 批量
Route::rest([
&quot;save&quot; =&gt; [&quot;POST&quot;, &quot;&quot;, &quot;store&quot;],
&quot;update&quot; =&gt; [&quot;PUT&quot;, &quot;/:id&quot;, &quot;save&quot;],
&quot;delete&quot; =&gt; [&quot;DELETE&quot;, &quot;/:id&quot;, &quot;destory&quot;],
]);</code></pre>
</li>
<li>支持嵌套资源路由,类似于一对多关联的感觉,实战中用到再操作,详情查看手册:</li>
</ul>
<h1>36. 视图.变量.渲染</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>视图操作</strong></h3>
<ul>
<li>
<p>首先,为了方便后续课程学习,先把路由给关闭了;并创建一个用于测试视图的控制器:</p>
<pre><code class="language-php">// 是否启用路由
&quot;with_route&quot; =&gt; false,
// 视图控制器
class ViewPage extends BaseController
{
public function index()
{
return &quot;view&quot;;
}
}</code></pre>
</li>
<li>
<p>由于我们不用模板引擎,直接使用php原生,就需要使用 <strong>engine()</strong> 方法,载入 test 模板;</p>
<pre><code class="language-php">// 载入原生php模板
return View::engine(&quot;php&quot;)-&gt;fetch(&quot;test&quot;);
// 模板地址为:view/view_page/test.html
// 或修改配置文件,将Think改为php就可以使用助手函数
return view(&quot;test&quot;);</code></pre>
</li>
<li>
<p>如果希望模板后缀为 <strong>.php</strong>,方便 php + html5 混编,在 <strong>config/view</strong> 设置:</p>
<pre><code class="language-php">// 模板后缀
&quot;view_suffix&quot; =&gt; &quot;php&quot;,</code></pre>
</li>
<li>
<p>在 fetch() 方法的第二参数,通过数组方式给模板传递变量:</p>
<pre><code class="language-php">return View::engine(&quot;php&quot;)-&gt;fetch(&quot;test&quot;, [
&quot;name&quot; =&gt; &quot;ThinkPHP8&quot;
]);
// 或
return view(&quot;test&quot;,[
&quot;name&quot; =&gt; &quot;ThinkPHP8&quot;
]);</code></pre>
</li>
</ul>
<h3>2. <strong>表单提交</strong></h3>
<ul>
<li>
<p>先载入一个表单页面:</p>
<pre><code class="language-php">return View::engine(&quot;php&quot;)-&gt;fetch(&quot;input&quot;);</code></pre>
</li>
<li>
<p>创建一个表单:</p>
<pre><code class="language-html">&lt;form action=&quot;/view_page/save&quot; method=&quot;post&quot;&gt;
&lt;input type=&quot;text&quot; name=&quot;username&quot;&gt;
&lt;input type=&quot;submit&quot; value=&quot;提交&quot;&gt;&lt;/input&gt;
&lt;/form&gt;</code></pre>
</li>
<li>
<p>接受数据:</p>
<pre><code class="language-php">public function save()
{
return $this-&gt;request-&gt;post(&quot;username&quot;);
}</code></pre>
</li>
</ul>
<h1>37. 请求对象.变量.信息</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>请求对象</strong></h3>
<ul>
<li>
<p>上一节课中表单提交时,我们接受数据使用了 <strong>$this->request->post()</strong> 方法,这哪里来的?</p>
</li>
<li>
<p>因为我们的控制器继承了 <strong>BaseController</strong> 追踪进去,可以看到 <strong>$request</strong> 成员字段;</p>
</li>
<li>
<p>关于这个知识点的源知识点,可以参考:手册 -> 架构 -> 容器和依赖注入,TP6讲过,8不讲了;</p>
</li>
<li>
<p>在没有继承 BaseController 时,我们需要自己手动注入请求:</p>
<pre><code class="language-php">namespace app\controller;
use think\Request;
class Rely
{
protected $request;
// 依赖注入
public function __construct(Request $request)
{
$this-&gt;request = $request;
}
public function index()
{
halt($this-&gt;request-&gt;get());
}
}
// 上面的请求方式比较原始,过于麻烦,不推荐了</code></pre>
</li>
<li>
<p>第二种方式:<strong>门面Facade</strong>,它相关的知识点在手册 -> 架构 -> 门面:</p>
<pre><code class="language-php">namespace app\controller;
use think\facade\Request;
class Rely
{
public function index()
{
halt(Request::get());
}
}</code></pre>
</li>
<li>
<p>第三种方式:继承 <strong>BaseController</strong>,其实就是第一种,只不过被封装到基类中去了:</p>
<pre><code class="language-php">namespace app\controller;
use app\BaseController;
class Rely extends BaseController
{
public function index()
{
halt($this-&gt;request-&gt;get());
}
}</code></pre>
</li>
<li>
<p>第四种方式:终极方法 <strong>request()</strong> 助手函数:</p>
<pre><code class="language-php">halt(request()-&gt;get());</code></pre>
</li>
</ul>
<h3>2. <strong>请求信息</strong></h3>
<ul>
<li>
<p>在手册 请求 -> 请求信息 里有全部的请求方法模块,这里列举几个意思一下:</p>
<table>
<thead>
<tr>
<th style="text-align: center;">方法</th>
<th style="text-align: left;">含义</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">host</td>
<td style="text-align: left;">当前访问域名或者IP</td>
</tr>
<tr>
<td style="text-align: center;">port</td>
<td style="text-align: left;">当前访问的端口</td>
</tr>
<tr>
<td style="text-align: center;">url</td>
<td style="text-align: left;">当前完整URL</td>
</tr>
<tr>
<td style="text-align: center;">root</td>
<td style="text-align: left;">URL访问根地址</td>
</tr>
<tr>
<td style="text-align: center;">method</td>
<td style="text-align: left;">当前请求类型</td>
</tr>
</tbody>
</table>
</li>
<li>
<p>我们三种方法演示一遍,最终选一种你喜欢的即可:</p>
<pre><code class="language-php">// 当前url
echo $this-&gt;request-&gt;url();
echo Request::url();
echo request()-&gt;url();
// 请求方法
echo request()-&gt;method();
// 更多的对照手册自行测试即可</code></pre>
</li>
</ul>
<h3>3. <strong>请求变量</strong></h3>
<ul>
<li>
<p>Request 对象支持全局变量的检测、获取和安全过滤,支持$_GET、$_POST...等;</p>
</li>
<li>
<p>使用 <strong>has()</strong> 方法,可以检测全局变量是否已经设置:</p>
<pre><code class="language-php">// 判断是否有GET模式下id的值
echo request()-&gt;has(&quot;id&quot;, &quot;get&quot;);</code></pre>
</li>
<li>
<p>更多方法,参看手册 请求 -> 输入变量, 这里意思几个:</p>
<table>
<thead>
<tr>
<th style="text-align: center;">方法</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">param</td>
<td>获取当前请求变量</td>
</tr>
<tr>
<td style="text-align: center;">get</td>
<td>获取 $_GET 变量</td>
</tr>
<tr>
<td style="text-align: center;">post</td>
<td>获取 $_POST 变量</td>
</tr>
<tr>
<td style="text-align: center;">put</td>
<td>获取 PUT 变量</td>
</tr>
<tr>
<td style="text-align: center;">delete</td>
<td>获取 DELETE 变量</td>
</tr>
<tr>
<td style="text-align: center;">session</td>
<td>获取 SESSION 变量</td>
</tr>
</tbody>
</table>
</li>
<li>
<p><strong>param()</strong> 方法是框架推荐的方法,可以自动识别诸如 get、post等数据信息;</p>
<pre><code class="language-php">// url: http://www.tp.com:8000/rely/index?id=5
// 可以获取 get 模式 id 的值
echo request()-&gt;param(&quot;id&quot;);
echo request()-&gt;get(&quot;id&quot;);
// url: http://www.tp.com:8000/rely/index/id/5
// 此时,只能是param获取
echo request()-&gt;param(&quot;id&quot;);</code></pre>
<pre><code class="language-php">// 默认值
request()-&gt;param(&quot;name&quot;) // null,实际上页面也转行成空,判断null也成立
request()-&gt;param(&quot;name&quot;, &quot;&quot;) // 空字符串
request()-&gt;param(&quot;name&quot;, &quot;无名氏&quot;); // 无名氏</code></pre>
</li>
<li>
<p>可在 <strong>app\Request.php</strong> 配置过滤器:</p>
<pre><code class="language-php">http://www.tp.com:8000/rely/index?name=我&lt;b&gt;你&lt;/b&gt;
// 将特殊字符转换HTML实体
protected $filter = [&quot;htmlspecialchars&quot;];
// 如果不想要全局过滤器,可以直接局部
request()-&gt;param(&quot;name&quot;, &quot;&quot;, &quot;htmlspecialchars&quot;);
// 设置了全局过滤器,但某个不想用
request()-&gt;param(&quot;name&quot;, &quot;&quot;, null)
// 使用变量修饰符,可以将参数强制转换成指定的类型;
// /s(字符串)、/d(整型)、/b(布尔)、/a(数组)、/f(浮点);
request()-&gt;param(&quot;id/d&quot;);</code></pre>
</li>
<li>
<p><strong>only()</strong>、<strong>except()</strong> 设置允许和排查可接受的变量:</p>
<pre><code class="language-php">// 允许id和name变量
request()-&gt;only([&quot;id&quot;,&quot;name&quot;]);
// 默认值设置
request()-&gt;only([&quot;id&quot;=&gt;1,&quot;name&quot;=&gt;&quot;默认值&quot;]);
// 参数二可设置GET还是POST等
request()-&gt;only([&quot;id&quot;,&quot;name&quot;], &quot;GET&quot;);</code></pre>
</li>
<li>
<p>以上所有,都封装到助手函数 <strong>input()</strong> 里了:</p>
<pre><code class="language-php">input(&quot;?get.id&quot;); //判断 get 下的 id 是否存在
input(&quot;?post.name&quot;); //判断 post 下的 name 是否存在
input(&quot;param.name&quot;); //获取 param 下的 name 值
input(&quot;param.name&quot;, &quot;默认值&quot;); //默认值
input(&quot;param.name&quot;, &quot;&quot;, &quot;htmlspecialchars&quot;); //过滤器
input(&quot;param.id/d&quot;); //设置强制转换</code></pre>
</li>
</ul>
<h1>38. 请求类型.输出.重定向</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>请求类型</strong></h3>
<ul>
<li>
<p><strong>Request</strong> 对象提供了一个方法 <strong>method()</strong> 来获取当前请求类型,也提供了判断当前的请求类型:</p>
<table>
<thead>
<tr>
<th style="text-align: center;">方法</th>
<th style="text-align: left;">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">method</td>
<td style="text-align: left;">获取当前请求类型</td>
</tr>
<tr>
<td style="text-align: center;">isGet</td>
<td style="text-align: left;">判断是否GET请求</td>
</tr>
<tr>
<td style="text-align: center;">isPost</td>
<td style="text-align: left;">判断是否POST请求</td>
</tr>
<tr>
<td style="text-align: center;">isPut</td>
<td style="text-align: left;">判断是否PUT请求</td>
</tr>
<tr>
<td style="text-align: center;">isDelete</td>
<td style="text-align: left;">判断是否DELETE请求</td>
</tr>
<tr>
<td style="text-align: center;">isAjax</td>
<td style="text-align: left;">判断是否AJAX请求</td>
</tr>
</tbody>
</table>
</li>
<li>
<p>使用请求类型伪装,可以提交 PUT、DELETE 类型:</p>
<pre><code class="language-php">// 载入表单模板
public function create()
{
return View::engine(&quot;php&quot;)-&gt;fetch(&quot;create&quot;);
}
// 表单
&lt;form action=&quot;/rely&quot; method=&quot;post&quot;&gt;
&lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;
&lt;input type=&quot;hidden&quot; name=&quot;_method&quot; value=&quot;PUT&quot;&gt;
&lt;input type=&quot;submit&quot; value=&quot;提交&quot;&gt;
&lt;/form&gt;
// 判断是否PUT请求
if (request()-&gt;isPut()) {
echo input(&quot;put.name&quot;);
}
// 直接ajax、pjax伪装,在url后续添加?_ajax=1即可,结合前段时再研究</code></pre>
</li>
</ul>
<h3>2. <strong>响应输出</strong></h3>
<ul>
<li>
<p>响应输出,有好几种:包括 <strong>return</strong>、<strong>json()</strong> 和 <strong>view()</strong> 等等;</p>
</li>
<li>
<p>默认输出方式是以 html 格式输出,如果你发起 json 请求,则输出 json;</p>
</li>
<li>
<p>而背后是 response 对象,可以用 <strong>response()</strong> 输出达到相同的效果;</p>
<pre><code class="language-php">$data = &quot;Hello,TP8!&quot;;
// 等同于 return $data;
return response($data);</code></pre>
</li>
<li>
<p>response()方法可以设置第二参数,状态码,或调用 code()方法;</p>
<pre><code class="language-php">// 参数二,发送状态码
return response($data, 201);
//或
return response($data)-&gt;code(201);
// json()和view()均支持状态码</code></pre>
</li>
</ul>
<h3>3. <strong>重定向</strong></h3>
<ul>
<li>
<p>使用 redirect()方法可以实现页面重定向,需要 return 执行;</p>
<pre><code class="language-php">// 首页
return redirect(&quot;/&quot;);
// 访问路由页面,外加状态码
return redirect(&quot;details/5&quot;, 303);
// 访问url生成的地址
return redirect(url(&quot;Index/index&quot;));</code></pre>
</li>
<li>还支持session跳转和记住上一次地址的跳转,实战时再研究;</li>
</ul>
<h1>39. Session和Cookie</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>Session</strong></h3>
<ul>
<li>
<p>TP8不支持原生 $_ SESSION 的获取方式,也不支持 session_开头的函数,默认是关闭的;</p>
</li>
<li>
<p>在使用 Session 之前,需要开启初始化,在中间件文件 middleware.php;</p>
<pre><code class="language-php">// 去掉注释,开始Session
\think\middleware\SessionInit::class</code></pre>
</li>
<li>
<p>使用 <strong>::set()</strong> 和 <strong>::get()</strong> 方法去设置 Session 的存取:</p>
<pre><code class="language-php">// 设置session use
Session::set(&quot;user&quot;, &quot;Mr.Lee&quot;);
// 获取session use
echo Session::get(&quot;user&quot;);
// 获取不存在的session时,参数二默认值
echo Session::get(&quot;abc&quot;, &quot;不存在&quot;);
// 获取全部session,数组形式
dump(Session::all());</code></pre>
</li>
<li>
<p><strong>::has()</strong> 判断是否赋值,<strong>::delete()</strong> 删除,<strong>::pull()</strong> 取值后删除,<strong>::clear()</strong> 清空整个 session;</p>
<pre><code class="language-php">// 判断是否存在
Session::has(&quot;user&quot;);
// 删除
Session::delete(&quot;user&quot;);
// 取值后删除,不存在返回null
Session::pull(&quot;user&quot;);
// 清空所有
Session::clear(&quot;&quot;);</code></pre>
</li>
<li>
<p>Request 对象可以直接对Session进行存取,自行查看手册,这里再罗列常用的助手函数:</p>
<pre><code class="language-php">//赋值
session(&quot;user&quot;, &quot;Mr.Wang&quot;);
//has 判断
session(&quot;?user&quot;);
//delete 删除
session(&quot;user&quot;, null);
//清理全部
session(null);
//输出
echo session(&quot;user&quot;);</code></pre>
</li>
</ul>
<h3>2. <strong>Cookie</strong></h3>
<ul>
<li>
<p>Cookie 是客户端存储,默认情况下是开启初始化的,在 config/cookie.php;</p>
<pre><code class="language-php">// 设置cookie user value 过期时间,不设过期时间,则为临时关闭浏览器后自动删除
Cookie::set(&quot;user&quot;, &quot;Mr.Lee&quot;, 3600);
// 获取cookie,注意,会慢一拍
echo Cookie::get(&quot;user&quot;);
// 获取全部cookie
dump(Cookie::get());
// 永久保持
Cookie::forever(&quot;user&quot;, &quot;Mr.Lee&quot;);</code></pre>
</li>
<li>
<p><strong>::has()</strong> 判断是否存在,<strong>::delete()</strong> 删除 cookie;</p>
<pre><code class="language-php">Cookie::has(&quot;user&quot;);
Cookie::delete(&quot;user&quot;);</code></pre>
</li>
<li>
<p>助手函数,更加方便操作:</p>
<pre><code class="language-php">echo cookie(&quot;user&quot;); //输出
cookie(&quot;user&quot;, &quot;Mr.Lee&quot;, 3600); //设置
cookie(&quot;user&quot;, null); //删除</code></pre>
</li>
</ul>
<h1>40. 缓存功能</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>常规方法</strong></h3>
<ul>
<li>
<p>系统内置了很多类型的缓存,除了 File,其它均需要结合相关产品;</p>
</li>
<li>
<p>我们这里主要演示 File 文本缓存,其它的需要学习相关产品,比如Redis将单独出;</p>
</li>
<li>
<p>配置文件 cache.php 进行缓存配置,默认生成在 runtime/cache 目录;</p>
<pre><code class="language-php">// 生成缓存,和cookie一样的设置,但注意,过期时间不设,将是永久,和cookie有区别
Cache::set(&quot;user&quot;, &quot;Mr.Lee&quot;, 3600);
// 读取缓存,无数据返回null
echo Cache::get(&quot;user&quot;);
// 判断是否存在
echo Cache::has(&quot;user&quot;);
// 删除缓存
Cache::delete(&quot;user&quot;);
// 先获取,再删掉,没有值返回null
Cache::pull(&quot;user&quot;);
// 清空缓存
Cache::clear();</code></pre>
</li>
<li>
<p><strong>::inc()</strong> 和 <strong>::dec()</strong> 实现缓存数据的自增和自减操作:</p>
<pre><code class="language-php">// 创建数值缓存
Cache::set(&quot;num&quot;, 1);
// 默认自增+1
Cache::inc(&quot;num&quot;);
// 参数二自增值
Cache::inc(&quot;num&quot;, 3);
echo Cache::get(&quot;num&quot;);</code></pre>
</li>
<li>
<p><strong>::push()</strong> 实现缓存的数组数据追加的功能:</p>
<pre><code class="language-php">// 数组缓存
Cache::set(&quot;arr&quot;, [1, 2, 3]);
// 追加数据
Cache::push(&quot;arr&quot;, 4);
halt(Cache::get(&quot;arr&quot;));</code></pre>
</li>
</ul>
<h3>2. <strong>助手函数</strong></h3>
<ul>
<li>
<p>最常用的创建和读取缓存的助手函数:</p>
<pre><code class="language-php">//设置缓存
cache(&quot;user&quot;, &quot;Mr.Lee&quot;, 3600);
//输出缓存
echo cache(&quot;user&quot;);
//删除指定缓存
cache(&quot;user&quot;, null);</code></pre>
</li>
</ul>
<h1>41. 验证器的使用</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>定义方式</strong></h3>
<ul>
<li>
<p>验证器的使用,我们必须先定义它,系统提供了一条命令直接生成想要的类;</p>
<pre><code class="language-php">// 生成一个 validate 文件夹,并生成 User.php 验证类;
// 自动生成了两个属性:$rule 表示定义规则,$message 表示错误提示信息;
php think make:validate User</code></pre>
<pre><code class="language-php">// 规则
protected $rule = [
&quot;name&quot; =&gt; &quot;require|max:20&quot;, //不得为空,不得大于 20 位
&quot;price&quot; =&gt; &quot;number|between:1,100&quot;, //必须是数值,1-100 之间
&quot;email&quot; =&gt; &quot;email&quot; //邮箱格式要正确
];
// 提示,不设置则默认错误提示
protected $message = [
&quot;name.require&quot; =&gt; &quot;姓名不得为空&quot;,
&quot;name.max&quot; =&gt; &quot;姓名不得大于 20 位&quot;,
&quot;price.number&quot; =&gt; &quot;价格必须是数字&quot;,
&quot;price.between&quot; =&gt; &quot;价格必须 1-100 之间&quot;,
&quot;email.email&quot; =&gt; &quot;邮箱的格式错误&quot;
];</code></pre>
</li>
<li>
<p>验证器定义好了之后,我们需要进行调用测试,创建一个 Verify.php 控制器;</p>
<pre><code class="language-php">public function vali()
{
try {
validate(User::class)-&gt;check([
// 可以将姓名设置空,或大于20位
// 将邮箱写错,来测试
&quot;name&quot; =&gt; &quot;Mr.Lee&quot;,
&quot;email&quot; =&gt; &quot;123163.com&quot;
]);
} catch (ValidateException $e) {
dump($e-&gt;getError());
}
}</code></pre>
</li>
<li>
<p>默认情况下,出现一个错误就会停止后面字段的验证,我们也可以设置批量验证;</p>
<pre><code class="language-php">validate(User::class)-&gt;batch(true)...</code></pre>
</li>
<li>
<p>手册提供了大量内置的规则,查看 手册 -> 验证 -> 内置规则,但我们也可以自定义规则:</p>
<pre><code class="language-php">// 过滤:李炎恢 这三个字
protected $rule = [
&quot;name&quot; =&gt; &quot;require|max:20|checkName:李炎恢&quot;
]
// 自定义规则checkName
protected function checkName($value, $rule)
{
// 判断不等返回true,否则返回错误提示
return $value != $rule ? true : &quot;名字是违禁词!&quot;;
}
// 规则函数五个参数
protected function checkName($value, $rule, $data, $field, $title)
{
dump($data); //所有数据信息
dump($field); //当前字段名
dump($title); //字段描述,没有就是字段名
}
// 设置字段描述
protected $rule = [
&quot;name|用户名&quot;
]</code></pre>
</li>
</ul>
<h3>2. <strong>规则.错误信息</strong></h3>
<ul>
<li>
<p>当规则体量较大时,可以采用数组的方式,让规则的可读性变高;</p>
<pre><code class="language-php">// 只不过,体量小时,反而感觉乱
protected $rule = [
&quot;name&quot; =&gt; [
&quot;require&quot;,
&quot;max&quot; =&gt; 20,
&quot;checkName&quot; =&gt; &quot;李炎恢&quot;
],
&quot;email&quot; =&gt; &quot;email&quot;
];</code></pre>
</li>
<li>
<p>较少的验证时,或想只在控制器独立验证,不想创建验证类,也是支持的;</p>
<pre><code class="language-php">// 规则
$validate = Validate::rule([
&quot;name&quot; =&gt; &quot;require|max:20&quot;,
&quot;email&quot; =&gt; &quot;email&quot;
]);
// 验证
$result = $validate-&gt;check([
&quot;name&quot; =&gt; &quot;李炎恢&quot;,
&quot;email&quot; =&gt; &quot;123@163.com&quot;
]);
// 判断
if (!$result) {
dump($validate-&gt;getError());
}</code></pre>
</li>
</ul>
<h1>42. 验证场景.内置规则</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>验证场景</strong></h3>
<ul>
<li>
<p>验证场景设置,即特定的场景下是否进行验证,独立验证不存在场景验证;</p>
</li>
<li>
<p>举一个简单的例子,新增数据需要验证用户名,而修改更新时不验证用户名;</p>
</li>
<li>
<p>可以在验证类 User.php 中,设置一个$scene 属性,用来限定场景验证;</p>
<pre><code class="language-php">// 验证场景
protected $scene = [
&quot;insert&quot; =&gt; [&quot;name&quot;, &quot;email&quot;], // 新增验证name和email
&quot;edit&quot; =&gt; [&quot;email&quot;] // 修改验证email
];</code></pre>
</li>
<li>
<p>在控制器端,验证时,根据不同的验证手段,绑定相关场景进行验证即可;</p>
<pre><code class="language-php">validate(User::class)-&gt;scene(&quot;edit&quot;)</code></pre>
</li>
<li>
<p>在验证类端,可以设置一个公共方法对场景的细节进行定义,方法名为 scene+场景;</p>
<pre><code class="language-php">// 除了only,还支持移出remove规则和append增加规则,请查看手册
public function sceneEdit()
{
return $this-&gt;only([&quot;email&quot;]);
}</code></pre>
</li>
</ul>
<h3>2. <strong>内置规则</strong></h3>
<ul>
<li>
<p>验证器提供了大量的内置规则:格式验证、长度区间、字段比较以及其它等验证;</p>
</li>
<li>
<p>格式验证类:(更多可查看手册 验证 -> 验证规则)</p>
<pre><code class="language-php">// 规则
$validate = Validate::rule([
&quot;id&quot; =&gt; &quot;number&quot;,
]);
// 验证
$result = $validate-&gt;check([
&quot;id&quot; =&gt; &quot;abc&quot;,
]);
// 判断
if (!$result) {
dump($validate-&gt;getError());
}</code></pre>
</li>
<li>
<p>可以使用静态 <strong>facade</strong> 模式单一验证来快捷对字段数据进行判断:</p>
<pre><code class="language-php">// 格式为Validate::is+Rule,返回 true 或 false
dump(Validate::isNumber(10));</code></pre>
</li>
</ul>
<h1>43. Token表单令牌</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>设置Token</strong></h3>
<ul>
<li>
<p>表单令牌就是在表单中增加一个隐藏字段,随机生成一串字符,确定不是伪造;</p>
</li>
<li>
<p>这种随机产生的字符和服务器的 session(开启)进行对比,通过则是合法表单;</p>
<pre><code class="language-php">// 随机生成一个Token,并交给模板
public function index()
{
return view(&quot;index&quot;, [
&quot;token&quot; =&gt; $this-&gt;request-&gt;buildToken(&quot;__token__&quot;, &quot;sha1&quot;)
]);
}</code></pre>
<pre><code class="language-php">// 表单插入一个隐藏域,value是token
&lt;input type=&quot;hidden&quot; name=&quot;__token__&quot; value=&quot;&lt;?php echo $token;?&gt;&quot;&gt;
// 至于Aajx提交,不方便测试,等实战时再演练,具体参看 手册 -&gt; 验证 -&gt; 表单令牌</code></pre>
<pre><code class="language-php">public function save()
{
// 可以看到 token 一并写入了session
echo $this-&gt;request-&gt;post(&quot;__token__&quot;).&quot;&lt;br&gt;&quot;;
echo session(&quot;__token__&quot;);
}</code></pre>
</li>
</ul>
<h3>2. <strong>验证Token</strong></h3>
<ul>
<li>
<p>一般来说提交跳转有两种形式:路由模式和常规模式,先看下常规模式;</p>
<pre><code class="language-php">// 验证__token__是否通过
$check = $this-&gt;request-&gt;checkToken(&quot;__token__&quot;);
// 不通过,这里手动抛出指定异常即可
if ($check === false) {
throw new ValidateException(&quot;无效令牌!&quot;);
}</code></pre>
</li>
<li>
<p>路由模式,需要开启路由,并设置路由:</p>
<pre><code class="language-php">// Token测试
Route::rule(&quot;v/i&quot;, &quot;Verify/index&quot;);
Route::rule(&quot;v/s&quot;, &quot;Verify/save&quot;)-&gt;token();</code></pre>
</li>
<li>
<p>当然也支持验证器模式,具体如下:</p>
<pre><code class="language-php">protected $rule = [
'name' =&gt; 'require|max:25|token'
}</code></pre>
</li>
</ul>
<h1>44. 上传文件</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>文件上传</strong></h3>
<ul>
<li>
<p>如果要实现上传功能,首先需要建立一个上传表单:</p>
<pre><code class="language-php">// 载入模板
public function up()
{
return View::engine(&quot;php&quot;)-&gt;fetch(&quot;up&quot;);
}
// 接受上传数据
public function save() {} </code></pre>
<pre><code class="language-html">&lt;form action=&quot;/file/save&quot; enctype=&quot;multipart/form-data&quot; method=&quot;post&quot;&gt;
&lt;input type=&quot;file&quot; name=&quot;image&quot;&gt;
&lt;input type=&quot;submit&quot; value=&quot;上传&quot;&gt;
&lt;/form&gt;</code></pre>
</li>
<li>
<p>使用 <strong>Request::file()</strong> 或 <strong>request()->file</strong> 来获取上传数据:</p>
<pre><code class="language-php">// 获取上传数据
halt(Request::file(&quot;image&quot;));
// 或
halt(request()-&gt;file(&quot;image&quot;));
// 上传到本地服务器,参数1是设置的分类目录
$saveName = Filesystem::putFile(&quot;topic&quot;, $file);
halt($saveName);
// 生成的规则还支持另外两种方式:md5 和 sha1;
Filesystem::putFile(&quot;topic&quot;, $file, &quot;sha1&quot;);</code></pre>
</li>
<li>
<p>在 <strong>config/filesystem.php</strong> 里可以修改上次规则和目录;</p>
</li>
<li>
<p>如果要批量上传,表单改成数组形式即可:</p>
<pre><code class="language-html">&lt;form action=&quot;/verify/upload&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;
&lt;input type=&quot;file&quot; name=&quot;image[]&quot;&gt;
&lt;input type=&quot;file&quot; name=&quot;image[]&quot;&gt;
&lt;input type=&quot;file&quot; name=&quot;image[]&quot;&gt;
&lt;input type=&quot;submit&quot; value=&quot;上传&quot;&gt;
&lt;/form&gt;</code></pre>
<pre><code class="language-php">// 获取表单上传数据
$files = Request::file(&quot;image&quot;);
// 批量
$saveNames = [];
foreach ($files as $file) {
$saveNames[] = Filesystem::putFile(&quot;topic&quot;, $file);
}
halt($saveNames);</code></pre>
</li>
</ul>
<h3>2. <strong>上传验证</strong></h3>
<ul>
<li>
<p>如果要对上传的内容进行验证,比如后缀、大小等,直接用验证器:</p>
</li>
<li>
<p>手册上本身给出的例子是批量上传验证,我这里写一个单一上传验证;</p>
<pre><code class="language-php">// 获取表单上传数据
$file = Request::file(&quot;image&quot;);
// 独立验证规则,也可以写到验证类里
$validate = Validate::rule([
// 10240000 等于 1.28MB
&quot;image&quot; =&gt; &quot;file|fileExt:jpg,png,gif|fileSize:10240000&quot;
]);
// 验证上传
$result = $validate-&gt;check([
&quot;image&quot; =&gt; $file
]);
// 判断
if ($result) {
$saveName = Filesystem::putFile(&quot;topic&quot;, $file);
halt($saveName);
} else {
dump($validate-&gt;getError());
}</code></pre>
</li>
</ul>
<h1>45. 验证码</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>生成验证码</strong></h3>
<ul>
<li>
<p>验证码功能不是系统内置的功能了,需要通过 composer 引入进来;</p>
<pre><code class="language-php">// 安装后使用需要开启 session
composer require topthink/think-captcha</code></pre>
</li>
<li>
<p>引入进来之后,我们在模版中,验证一下验证码是否能正常显示;</p>
<p>```php+HTML
<div><?php echo captcha_img() ?></div>
<div><img src="{:captcha_src()}" alt="captcha" /></div></p>
<pre><code></code></pre>
</li>
<li>
<p>验证码不显示图片原因是伪静态问题,无法加在404错误:</p>
<ul>
<li>开启路由即可;</li>
<li>避免麻烦,可以取消强制路由;</li>
</ul>
<pre><code class="language-php">// 强制路由时,需要设置访问路由,非强制不需要
//Route::rule(&quot;v/c&quot;, &quot;Verify/code&quot;);</code></pre>
</li>
<li>
<p>手动验证码,需要手动设置点击刷新:</p>
<p>```php+HTML
<div><img src="<?php echo captcha_src() ?>" alt="" onclick='this.src="/captcha.html?"+Math.random();'></div></p>
<pre><code></code></pre>
</li>
<li>
<p>在 <strong>config/captcha.php</strong> 文件中, 可以配置验证码的各种参数:</p>
<pre><code class="language-php">//验证码位数
'length' =&gt; 4,
// 是否使用中文验证码
'useZh' =&gt; true,
// 是否使用算术验证码
'math' =&gt; false,</code></pre>
</li>
<li>手册提供了自建验证码,具体参看:手册 -> 扩展库 -> 验证码;</li>
</ul>
<h3>2. <strong>验证码验证</strong></h3>
<ul>
<li>
<p>直接利用验证器功能,对验证码进行验证即可;</p>
<pre><code class="language-php">// 获取表单值
$code = $this-&gt;request-&gt;param(&quot;code&quot;);
// 规则
$validate = Validate::rule([
&quot;captcha|验证码&quot; =&gt; &quot;require|captcha&quot;
]);
// 验证
$result = $validate-&gt;check([
&quot;captcha&quot; =&gt; $code
]);
// 判断
if (!$result) {
dump($validate-&gt;getError());
}</code></pre>
</li>
<li>
<p>手册上提供了继承基类后依赖注入的方法,就是将规则和验证合二为一:</p>
<pre><code class="language-php">// 通过依赖入驻,需要继承BaseController
$this-&gt;validate([
&quot;captcha&quot; =&gt; $code
], [
&quot;captcha|验证码&quot; =&gt; &quot;require|captcha&quot;
]);</code></pre>
</li>
<li>
<p>极简方案 -> 助手函数:</p>
<pre><code class="language-php">// 助手函数
if (!captcha_check(input(&quot;post.code&quot;)))
{
// 抛异常
throw new ValidateException(&quot;验证码错误&quot;);
//return &quot;验证码错误!&quot;;
}</code></pre>
</li>
</ul>
<h1>46. 图像处理</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>生成图像</strong></h3>
<ul>
<li>
<p>图像处理功能不是系统内置的功能了,需要通过 composer 引入进来;</p>
<pre><code class="language-php">composer require topthink/think-image</code></pre>
</li>
<li>
<p>为了更方便的测试,我们在 <strong>public/Image.png</strong> 放置一张图片,这样不用考虑 url 了;</p>
<pre><code class="language-php">$image = Image::open(&quot;image.png&quot;);
//图片宽度
echo $image-&gt;width();
//图片高度
echo $image-&gt;height();
//图片类型
echo $image-&gt;type();
//图片 mime
echo $image-&gt;mime();
//图片大小
dump($image-&gt;size());</code></pre>
</li>
<li>
<p>使用 <strong>crop()</strong> 方法可以裁剪图片,并使用 save() 方法保存到指定路径;</p>
<pre><code class="language-php">// 裁剪图片,默认从左上角裁,并保存到指定位置
$image-&gt;crop(550, 400)-&gt;save(&quot;crop1.png&quot;);</code></pre>
</li>
<li>
<p>使用 <strong>thumb()</strong> 方法,可以生成缩略图,配合 save() 把缩略图保存下来;</p>
<pre><code class="language-php">// 生成缩略图
$image-&gt;thumb(550, 400)-&gt;save(&quot;thumb1.png&quot;);</code></pre>
<pre><code class="language-php">// 手册上并没有Image这个组件功能,应该是没更新,PHP8.2会报错,修改源码
// 256行,添加 (int)
$img = imagecreatetruecolor((int)$width, (int)$height);
// 269行,添加 (int),如果设置参数3,x,y,w,h都需要 (int)
imagecopyresampled($img, $this-&gt;im, 0, 0, $x, $y, (int)$width, (int)$height, $w, $h);</code></pre>
</li>
</ul>
<h3>2. <strong>图像参数</strong></h3>
<ul>
<li>
<p><strong>thumb()</strong> 方法拥有第三个参数,可以追踪进去查看;</p>
<pre><code class="language-php">/* 缩略图相关常量定义 */
const THUMB_SCALING = 1; //常量,标识缩略图等比例缩放类型
const THUMB_FILLED = 2; //常量,标识缩略图缩放后填充类型
const THUMB_CENTER = 3; //常量,标识缩略图居中裁剪类型
const THUMB_NORTHWEST = 4; //常量,标识缩略图左上角裁剪类型
const THUMB_SOUTHEAST = 5; //常量,标识缩略图右下角裁剪类型
const THUMB_FIXED = 6; //常量,标识缩略图固定尺寸缩放类型</code></pre>
<pre><code class="language-php">$image-&gt;thumb(550, 400, 3)-&gt;save(&quot;thumb1.png&quot;);</code></pre>
</li>
<li>
<p>使用 rotate()方法,可以旋转图片,默认是 90 度,参数可以设置;</p>
<pre><code class="language-php">$image-&gt;rotate(180)-&gt;save(&quot;rotate1.png&quot;);</code></pre>
</li>
<li>
<p>save('路径',['类型','质量','是否隔行扫描']),追踪到方法查看;</p>
<pre><code class="language-php">save($pathname, $type = null, $quality = 80, $interlace = true)</code></pre>
</li>
<li>
<p>water()方法,可以给图片增加一个图片水印,默认位置为右下角,可看源码常量;</p>
<pre><code class="language-php">// 加水印
$image-&gt;water(&quot;Logo.png&quot;)-&gt;save(&quot;water1.png&quot;);</code></pre>
</li>
<li>
<p>text()方法,可以给图片增加一个文字水印;</p>
<pre><code class="language-php">$image-&gt;text('Mr.Lee',getcwd().'/1.ttf',20,'#ffffff')-&gt;save('text1.png');</code></pre>
</li>
</ul>
<h1>47. 分页处理</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>简易分页</strong></h3>
<ul>
<li>
<p>不管是数据库操作还是模型操作,都使用 paginate()方法来实现;</p>
<pre><code class="language-php">// 数据集
$list = User::paginate(3);
// 单独分页
$page = $list-&gt;render();
return view(&quot;index&quot;, [
&quot;list&quot; =&gt; $list,
&quot;page&quot; =&gt; $page
]);</code></pre>
<p>```php+HTML
<table border="1">
<tr>
<th>id</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
</tr>
<?php foreach ($list as $user) {?>
<tr>
<td><?=$user["id"]?></td>
<td><?=$user["name"]?></td>
<td><?=$user["age"]?></td>
<td><?=$user["gender"]?></td>
</tr>
<?php }?>
</table></p>
<p><div><?=$page?></div></p>
<pre><code></code></pre>
</li>
<li>
<p>可以通过浏览器的源码检查,或者使用输出html实体的方式来了解 page 构造,方便设计 css;</p>
<p>```php+HTML
<div><?=htmlentities($page)?></div></p>
<pre><code></code></pre>
</li>
<li>
<p><strong>paginate()</strong> 可以限定总记录数,比如,限定总记录数只有 10 条的页码;</p>
<pre><code class="language-php">-&gt;paginate(5, 10);</code></pre>
</li>
<li>
<p>可以设置分页的页码为简洁分页,就是没有 1,2,3,4 这种,只有上下页;</p>
<pre><code class="language-php">-&gt;paginate(5, true);</code></pre>
</li>
</ul>
<h3>2. <strong>分页参数</strong></h3>
<ul>
<li>
<p>如果想自行制作个性化分页,或获取分页中的各种参数,这里也提供了:</p>
<table>
<thead>
<tr>
<th style="text-align: center;">参数</th>
<th style="text-align: center;">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">list_rows</td>
<td style="text-align: center;">每页数量</td>
</tr>
<tr>
<td style="text-align: center;">page</td>
<td style="text-align: center;">当前页</td>
</tr>
<tr>
<td style="text-align: center;">path</td>
<td style="text-align: center;">url路径</td>
</tr>
<tr>
<td style="text-align: center;">query</td>
<td style="text-align: center;">url额外参数</td>
</tr>
<tr>
<td style="text-align: center;">fragment</td>
<td style="text-align: center;">url锚点</td>
</tr>
<tr>
<td style="text-align: center;">var_page</td>
<td style="text-align: center;">分页变量</td>
</tr>
</tbody>
</table>
<pre><code class="language-php">$list = User::paginate([
&quot;list_rows&quot; =&gt; 3,
&quot;var_page&quot; =&gt; &quot;p&quot;,
]);</code></pre>
</li>
<li>
<p>分页提供了 <strong>total()</strong> 方法,来获取所有总记录:</p>
<pre><code class="language-php">// 获取总记录
$total = $list-&gt;total();</code></pre>
</li>
</ul>
<h3>3. <strong>其它分页</strong></h3>
<ul>
<li>
<p>重写 <strong>Paginate</strong> 类,复制 <strong>Bootstrap</strong> 类,开启重写:</p>
<pre><code class="language-php">// 路径:vendor/topthink/think-orm/src/paginator/driver/Bootstrap.php
// 开启:app/common/Bootstrap.php, provider.php
return [
'think\Paginator' =&gt; 'app\common\Bootstrap'
];
// 复制后,修改上一页和下一页中文即可
protected function getPreviousButton(string $text = '上一页'): string</code></pre>
</li>
<li>手册 提供了 大数据分页 :<strong>PaginateX()</strong> 方法,用于百万级提高性能的。嗯!</li>
</ul>
<h1>48. 中间件入门</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>定义中间件</strong></h3>
<ul>
<li>
<p>中间件的主要用于拦截和过滤 HTTP 请求,并进行相应处理;</p>
</li>
<li>
<p>这些请求的功能可以是 URL 重定向、权限验证等等;</p>
</li>
<li>
<p>为了进一步了解中间件的用法,我们首先定义一个基础的中间件;</p>
</li>
<li>
<p>可以通过命令行模式,在应用目录下生成一个中间件文件和文件夹;</p>
<pre><code class="language-php">php think make:middleware Check</code></pre>
<pre><code class="language-php">public function handle($request, \Closure $next)
{
// 拦截请求
if ($request-&gt;param(&quot;name&quot;) == &quot;index&quot;)
{
return redirect(&quot;../&quot;);
}
// 继续往执行
return $next($request);
}</code></pre>
</li>
<li>
<p>然后将这个中间件进行注册,在应用目录下创建 middleware.php 中间件配置;</p>
<pre><code class="language-php">// 注册中间件
app\middleware\Check::class</code></pre>
</li>
<li>中间件的入口执行方法必须是:handle()方法,第一参数请求,第二参数是闭包;</li>
<li>业务代码判断请求的 name 如果等于 index,就拦截住,执行中间件,跳转到首页;</li>
<li>但如果请求的 name 是 lee,那需要继续往下执行才行,不能被拦死;</li>
<li>那么就需要$next($request)把这个请求去调用回调函数;</li>
<li>中间件 handle()方法规定需要返回 response 对象,才能正常使用;</li>
<li>而$next($request)执行后,就是返回的 response 对象;</li>
<li>为了测试拦截后,无法继续执行,可以 return response()助手函数测试;</li>
</ul>
<h3>2. <strong>前后置中间件</strong></h3>
<ul>
<li>
<p>将$next($request)放在方法底部的方式,属于前置中间件;</p>
</li>
<li>
<p>前置中间件就是请求阶段来进行拦截验证,比如登录判断、跳转、权限等;</p>
</li>
<li>
<p>而后置中间件就是请求完毕之后再进行验证,比如写入日志等等;</p>
<pre><code class="language-php">public function handle($request, \Closure $next)
{
//中间件代码,前置
return $next($request);
}</code></pre>
<pre><code class="language-php">public function handle($request, \Closure $next)
{
$response = $next($request);
//中间件代码,后置
return $response;
}</code></pre>
<pre><code class="language-php">// 先执行内容,再执行中间件
$response = $next($request);
// 拦截请求
if ($request-&gt;param(&quot;name&quot;) == &quot;index&quot;)
{
return redirect(&quot;../&quot;);
}
return $response;</code></pre>
</li>
</ul>
<h1>49. 中间件操作</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>路由中间件</strong></h3>
<ul>
<li>
<p>创建一个给路由使用的中间件,判断路由的 ID 值实现相应的验证;</p>
<pre><code class="language-php">php think make:middleware Auth</code></pre>
</li>
<li>
<p>路由方法提供了一个 <strong>middleware()</strong> 方法,让指定的路由采用指定的中间件;</p>
<pre><code class="language-php">// 限定这个路由采用了中间件
Route::rule('/', 'Index/index')-&gt;middleware(\app\middleware\Auth::class);</code></pre>
<pre><code class="language-php">// 导入后,可以省略
use app\middleware\Auth;
use app\middleware\Check;
// 路由采用多个中间件
Route::rule('/', 'Index/index')-&gt;middleware([Auth::class, Check::class]);</code></pre>
</li>
<li>
<p>也可以在 config/middleware.php 配置文件加中,配置别名支持;</p>
<pre><code class="language-php">// 别名或分组
'alias' =&gt; [
&quot;Auth&quot; =&gt; \app\middleware\Auth::class,
&quot;Check&quot; =&gt; \app\middleware\Check::class,
],</code></pre>
<pre><code class="language-php">// 当然,Route::group() 路由分组也支持
Route::rule('/', 'Index/index')-&gt;middleware([&quot;Auth&quot;, &quot;Check&quot;]);</code></pre>
</li>
</ul>
<h3>2. <strong>控制器中间件</strong></h3>
<ul>
<li>
<p>如果不用路由,怎么用局部化的中间件呢?当然也可以直接在控制器上定义;</p>
<pre><code class="language-php">// 这里是别名方式,和路由一样,另外两种均支持
protected $middleware = [&quot;Auth&quot;, &quot;Check&quot;];</code></pre>
</li>
<li>
<p>默认是控制器全体方法有效,如果需要限制,还是 <strong>only</strong> 和 <strong>except</strong>;</p>
<pre><code class="language-php">protected $middleware = [
&quot;Auth&quot; =&gt; [&quot;only&quot; =&gt; [&quot;hello&quot;]],
&quot;Check&quot; =&gt; [&quot;only&quot; =&gt; [&quot;index&quot;]]
];</code></pre>
</li>
</ul>
<h1>50. 助手函数和工具库</h1>
<p><div style="text-align:right;color:#666;font-size:20px;">主讲老师 - 李炎恢</div></p>
<h3>1. <strong>助手函数</strong></h3>
<ul>
<li>
<p>前面,我们已经学习了不少实用的助手函数,这里再总结一些:手册 -> 附录 -> 助手函数</p>
<table>
<thead>
<tr>
<th>助手函数</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>env</td>
<td>支持 Env:: 模式,环境变量</td>
</tr>
<tr>
<td>config</td>
<td>支持 Config:: 模式,配置信息</td>
</tr>
<tr>
<td>app_path</td>
<td>一组六个,自行参考手册,当前应用目录</td>
</tr>
</tbody>
</table>
<pre><code class="language-php">// 获取所有环境变量信息
halt(env());
// 获取HOST
return env(&quot;DB_HOST&quot;);
// 获取所有配置信息
halt(config());
// 获取默认应用名
return config(&quot;app.default_app&quot;);
// 当前应用目录
return app_path();</code></pre>
</li>
</ul>
<h3>2. <strong>助手工具库</strong></h3>
<ul>
<li>
<p>实用助手工具库,需要安装扩展:手册 -> 扩展库 -> think助手工具库</p>
<pre><code class="language-php">composer require topthink/think-helper</code></pre>
<pre><code class="language-php">// 将字母转换为小写
return Str::lower(&quot;ABCDEFG&quot;);</code></pre>
</li>
</ul>