目录
- 0. 多应用模式
- 1. 多应用模式下路由规则容易犯的错误
- 2. 架构分层
0. 多应用模式
怎么去理解?
假设现在的项目目录是:
mall/app/controller/Error.php ...
mall/app/model
这些场景都是基于单一应用模式,在实际工作当中,我们一般会使用多应用模式,怎么去理解?
比如说你的一个服务,既有后台管理模块,又有API模块,那么这个时候我们需要抽离出来形成两个模块,所以说我们需要做到多应用的一种模式,这样的话就有利于我们后续的项目维护和管理。
不然的话你的后台应用的控制器以及 API 控制器都放在 controller 下面,其实是非常混乱的,所以我们必须要使用到多应用的场景模式。
打开我们终端,我们来安装一个多应用模式的扩展,进入到mall
目录,
然后在这个目录下面我们直接composer require topthink/think-multi-app
,
回车一下,然后等待 扩展 安装成功。
安装好之后我们就可以去使用它,使用它 我们需要注意几点,首先我们会在 mall/app
目录下面我们需要创建一个目录比如叫 admin,【 mall/app/admin
】
这个 admin 就相当于一个 admin模块,或者说类似于一个应用,还比如说 API 的一个应用,【 mall/app/api
】,
这样的话我们的 controller 在这地方就不能用了,你可以先保留,后续的实践过程中可以把它删掉,比如说我在 admin 里面,admin 相当于是个模块,我需要创建一个什么?
创建一个 controller
,
比如 mall/app/admin/controller/index.php/abc
我怎么去访问多应用的场景下的数据?
mall/app/admin/controller/index.php
代码如下:
namespace app\admin\controller;
use app\BaseController;
class Index extends BaseController{
public function abc(){
return 123456;
}
}
浏览器输入:
127.0.0.1:8082/admin/index/abc
会显示 123456 的输出。
这样的话我们就很好管理,比如说后台的我只放在 admin 下面,API 的我只放 API 下面等等,
这就是多应用的模式。
1. 多应用模式下路由规则容易犯的错误
我们会围绕多应用模式来较好的去配置它的一些路由规则。初学者在刚开始接触 多应用模式 的时候,经常会遇到路由配置失败,然后无从下手的问题。
我们知道在我们根目录的下面其实是有一个路由的文件夹【mall/route
】,它里面就放了一些路由,如果我们是采用了多应用模式,这个时候,我们需要在当前应用或者说模块下面创建一个路由文件夹,
比如 mall/app/admin/route
然后创建文件,
在route文件夹下的文件名你可以随便取,比如说 API 或者取demo都可以。
然后在下面我需要去针对当前应用下面的一些路由配置,我要去使用这个路由,代码如下:
mall/app/admin/route/demo.php
use think\facade\Route;
Route::rule("test","index/hello","GET"); // 路由,函数,请求方式
路由跟函数的关系是:
请求该路由时,会跳转到函数并执行函数。
mall/app/admin/controller/index.php
代码如下:
namespace app\admin\controller;
use app\BaseController;
class Index extends BaseController {
public function abc(){
return 123456;
}
public function hello(){
return time();
}
}
按照正常的流程,我们应该是去访问,比如 text 让它定位到 hello方法,然后进入到 hello 的逻辑,这是常规的一种思想。
错误的请求路由方式,初学者常犯的:
127.0.0.1:8082/test
正确的请求路由方式:
127.0.0.1:8082/admin/test
为什么访问出错?
我们应该怎么去访问?
因为你这个地方 test 是在 admin 应用下面配的,所以说你路由必须这样写127.0.0.1:8082/admin/test
然后回车。必须要在 test 前面加 admin 才可以。
2. 架构分层
这个架构分层和我们之前讲解的框架的多应用是不一样的,那么它的分层意义其实是非常重大,我们是基于mvc的模式在做了一些其他的分层。那么我们先来看一下它有哪一些意义。
首先分层能够做到解耦,因为我们会把一些公共的内容会抽离到相应的某些层里边,我在其他地方用的时候就能够轻松的去调用,能够做到代码的解耦。
第二,能够统一规范。比如说我们把我们的架子搭好,我们支持比如说 控制器层、service层、lib层、model层、view层等等这些,其他人只需要根据我的分层来做相应的开发就可以了,哪个模块需要什么内容,我就在什么模块去进行相应的编码就可以了,所以说它能够统一规范,这样的话我们的开发速度就非常快了,所以这是我们的一些意义,在很多公司里面都会去用到类似这种分层的架构。
在这个分层架构之前,其实它都是会经历过一些坑的过程,也就是说我们会踩一些坑。比如说最初的时候,我们是通过最简的,比如说mvc,然后迭代到某一些分层,我们觉得这个分层才适合我们的场景,是这么一个过程。
那么接下来我们来看一下最初的或者说初学者他去使用我们框架的时候,去写业务的时候,他是如何去做的?
我们先来看一下一个非常简单的场景,比如说我们要去获取某一个栏目的内容,
比如说栏目ID等于1,或者说栏目ID等于2或者栏目ID等于3,我要把我表里面所有记录输出,并且我要把 category_id转换成相应的栏目名称,这样的话我的接口层就能够直接展示就可以了。
怎么去做?
一般来说,初学者或者说常规的一些公司,它是这样来写的,
我们创建一个控制器 M.php
位置 mall\app\admin\controller\M.php
初学者经常这样去写,当然它的效果也能够达到它的目的,把相应的数据呈现出来是可以的,但是它这样做是有很多弊端的,我们先来模拟一下初学者或者说常规的一些公司它是怎么去做的。
mall\app\admin\controller\M.php
代码如下:
namespace app\admin\controller;
use app\BaseController;
class M extends BaseController {
public function index(){
return 123456;
}
}
}
我们需要去获取数据表里的数据,然后把它组装好,返回一个API,是这么一个逻辑。
我们说了它是需要根据 category_id 去获取,所以说我们需要把 category_id 获取到,创建变量等于什么?我要去获取传递过来的 category_id ,
第二个参数,如果你没有,我们可以默认值是 0。
第三个参数我们需要作一个转换,这有什么目的?
因为我们的 category_id 它其实是一个int形式的,所以我们必须这样去做一个转换,不然的话比如说非法用户,他传一个相应的内容字符或者什么的,然后他就有可能会做到 sql 注入,严格意义上来说的话,必须要去加这个顾虑。
严格意义上来说的话,这地方我们还需要去做个判断,判断什么呢?
如果我们的 category_id 不存在,我们需要给个错误提示,或者说给一个API的记录提示,
我们拿到了ID,拿到ID之后我们就需要去库里获取数据,库里获取数据怎么去获取?
mall/app/admin/controller/M.php
完整代码如下:
namespace app\admin\controller;
use app\BaseController;
class M extends BaseController {
public function index(){
$categoryId = $this->request->param("category_id",0,"intval");
if(empty($categoryId)){
return show(config("status.error"),"参数错误");
}
$model = new Demo();
$results = $model->where("category_id",$categoryId)
-> limit(10)
-> order("id","desc")
-> select();
return show(config("status.success"),"ok",$results);
}
}
new 一个 model,因为我们是demo的表,直接通过我们 Demo,调用 where 条件,where 条件是什么?
就是 category_id,然后传递我们的值,然后我再传递,比如说最多让它获取10个,
然后再 order一下 ,然后传递我们的 id 倒序,
最后我们直接 select 就可以了,
这样的话我们就把我们数据表里面的 category_id 的数据全部获取到,但是我们做了个限制需要获取前 10 条。
因为它有返回值,所以赋给变量 $results。
然后 return。
我们来看一下有没有数据返回,打开我们的浏览器,浏览器输入:
127.0.0.1:8082/admin/m/index?category_id=1
我们在这地方 if(empty($categoryId)){...}
做了严格的判断,它需要传递这个参数,所以必须得传递一个参数。
它就会把我们数据库表里面 category_id 为 1 的数据获取到,然后呈现出来。
这个时候其实 category_id 为 1 它需要去做个转换,比如说我们给客户端或者给前端的时候,他获取到 API 数据的时候,我们必须要去把这个数据作为转化,因为有可能它会把这个数据,比如说咱们的 ID 的到底是什么名字,我们这个地方需要做个转换,那么转换什么?这个时候的话我们需要做个映射,我们可以这样来写,那么后续其实我们可以在这个地方有一张表来维护 category_id 的场景。
我在mall/config/category.php
这个地方直接写相应的配置文件,我们来看一下代码:
return [
1 => "科技",
2 => "衣服",
3 => "书籍"
];
1 对应科技,
2 对应衣服,
3 对应书籍,
要转换的话,怎么处理?
mall/app/admin/controller/M.php
完整代码如下:
namespace app\admin\controller;
use app\BaseController;
class M extends BaseController {
public function index(){
$categoryId = $this->request->param("category_id",0,"intval");
if(empty($categoryId)){
return show(config("status.error"),"参数错误");
}
$model = new Demo();
$results = $model->where("category_id",$categoryId)
-> limit(10)
-> order("id","desc")
-> select();
$categorys = config("category")
foreach($results as $key => $results){
$results[$key]['categoryName'] = $categorys[$result["category_id"]]??"其他";
}
return show(config("status.success"),"ok",$results);
}
}
我再给一个k值,
给它的k值是 categoryName,也就是说我让 category_id 的名字转换一下,然后转换什么,我需要去获取数据,那么怎么去获取?
直接在上面我有一个 $categorys,然后是等于我们配置文件里面的数据叫 category 就可以了。
因为它 foreach 循环之后,这个地方就是每一条数据记录,
所以说我这样的判断它,然后它下面存不存在这个ID,如果没有给一个默认值,比如说其他。
它会判断我们变量是否存在一个问号,如果存在就等于这个值,如果不存在就等于我们后面的其他。
我们来看一下有没有数据返回,打开我们的浏览器,浏览器输入:
127.0.0.1:8082/admin/m/index?category_id=1
这样的话我们 categoryName 就出来了,就把我们的中文的数据呈现出来,这样的话我们的客户端就直接展示这个数据就可以了,这是它的一种思想或者逻辑。
那么严格意义上来说的话,这个地方其实我们还需要去做一个判断,比如说当它不存在,因为它是获取我们的数据库的数据,如果你不存在,我们不能做这个foreach循环。
所以要加这样判断,
mall/app/admin/controller/M.php
完整代码如下:
namespace app\admin\controller;
use app\BaseController;
class M extends BaseController {
public function index(){
$categoryId = $this->request->param("category_id",0,"intval");
if(empty($categoryId)){
return show(config("status.error"),"参数错误");
}
$model = new Demo();
$results = $model->where("category_id",$categoryId)
-> limit(10)
-> order("id","desc")
-> select();
if(empty($results)){
return show(config("status.success"),"数据为空");
}
$categorys = config("category")
foreach($results as $key => $results){
$results[$key]['categoryName'] = $categorys[$result["category_id"]]??"其他";
}
return show(config("status.success"),"ok",$results);
}
}
这个时候我来访问一下,访问一下我们库里面没有的 ID,比如说是 34 回车,这个时候大家看到它并没有输出我们这个地方的内容体是吧?我们应该是数据为空,所以说它没有走到我们这个逻辑,那么这个是初学者容易犯的一个错误。
因为这个地方你这样去获取它其实返回的是一个对象,返回一个对象,这个对象当我们库里面没数据这个对象其实还是存在的,所以说你这样去写,它并不会往下面走,
我们应该这样写,把对象转换成数组,这样的话才没问题。
mall/app/admin/controller/M.php
完整代码如下:
namespace app\admin\controller;
use app\BaseController;
class M extends BaseController {
public function index(){
$categoryId = $this->request->param("category_id",0,"intval");
if(empty($categoryId)){
return show(config("status.error"),"参数错误");
}
$model = new Demo();
$results = $model->where("category_id",$categoryId)
-> limit(10)
-> order("id","desc")
-> select();
if(empty($results->toArray())){
return show(config("status.success"),"数据为空");
}
$categorys = config("category")
foreach($results as $key => $results){
$results[$key]['categoryName'] = $categorys[$result["category_id"]]??"其他";
}
return show(config("status.success"),"ok",$results);
}
}
ta为什么没有往下走?那是因为ta是对象,对象不管你是空的还是什么,对象的内容还是存在的,只不过是对象里的数据是空的,所以我们最后转换,这样的话它是个空数组,这就需要明确的,ok那么这样的话我们就能够很好的呈现出我们的需求。
我们的需求很简单,只需要把我们这个库里面,比如说根据 category_id 我把数据呈现出来,然后把我们 category_id 转换成 栏目的名称 就可以了。
但是在这个地方其实它是非常不友好的,虽然我们的功能实现了,但是你这样去做它是非常不友好的,因为你的控制器层,把我们的 model 层的逻辑全部都写在这里了,它是非常不友好的。
下一次比如说我其他控制器也需要去返回这个数据,或者说也需要去调取这里面的一些逻辑数据,你就非常痛苦,所以说这个逻辑我们必须要去优化,这种场景,或者说这种写法初学者喜欢这样去做,他把所有代码所有的逻辑都写在控制层,其实是非常不友好的。
所以说我们必须要去把它处理出来,把该放到 model 层的放到 model 层。