码农的日常工作无非就是写一些增删改查,写一个后台 WEB 系统就需要有用户管理、分类管理、文章管理……诸如此类,然后我们就得一个个写控制器,查询就得要有查询参数,增删改操作还得判断是否允许增删改,这些都是十分琐碎的事情,全部堆积起来就是恐怖的事情,例如以优雅著称的 Laravel 在遇到查询参数的时候也是“不优雅”了,如下图,这是一个常见的列表数据:
顶部有一个搜索栏,用来搜索指定名称、邮箱或者注册 IP 的用户,因而就需要用到查询参数,所以在 Laravel 中,获取用户列表的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
public function getList(): \Hyperf\Contract\LengthAwarePaginatorInterface { $query = User::query(); $params = $this->request->all();
foreach ($queryFields as $field) { if (isset($params[$field])) { $query->where($field, 'like', '%' . $params[$field] . '%'); } }
ORM 的设计者们也发现到了这个问题,于是可以使用下面的方式进行查询:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
public function getList(): \Hyperf\Contract\LengthAwarePaginatorInterface { $request = $this->request; return (new User()) ->when($request->has('name'), function ($query) use ($request) { $query->where('name', 'like', '%' . $request->input('name') . '%'); }) ->when(!$request->has('email'), function ($query) use ($request) { $query->where('email', 'like', '%' . $request->input('email') . '%'); })->when(!$request->has('register_ip'), function ($query) use ($request) { $query->where('register_ip', 'like', '%' . $request->input('register_ip') . '%'); }) ->paginate(10); }
这种做法也就是将 if 条件优化了一下而已,治标不治本,这样一坨代码看着就头大,甚至还不如我上面用循环体的方式处理来得简洁。每有一个列表需要展示,我们就得再重复写一次这个循环体,那为什么不能封装起来呢?上一篇文章写到协程调度器,那为什么查询就不能写一个「查询解析器」呢?类的封装就是为了复用代码,先分析一下现在的需求:
这其实就是前面一篇文章提到的面向切面编程相似的原理,也就是生命周期的概念:删除前、删除、删除后……诸如此类,参照 Laravel 资源控制器,我们可以知道有哪些常规的增删改查方法,那直接提取出来即可,我们来创建一个简单的资源控制器父类,一个资源控制器无非就是显示视图以及提供 API 给前端调用,控制器管理的是一个模型,比如用户控制器就管理 user 表的数据,而管理 MYSQL 数据可以使用 Model 类来处理,因此一个控制器对应一个 Model,而所有的视图为了规范我们也会放在同一个路径,因而提取出两个参数:model 和 path:
use Hyperf\Contract\LengthAwarePaginatorInterface; use Hyperf\ViewEngine\Contract\FactoryInterface; use Hyperf\ViewEngine\Contract\ViewInterface; use Psr\Http\Message\ResponseInterface; use function Hyperf\ViewEngine\view;
显示列表、创建数据、编辑数据的视图都是统一的,我们只需要将唯一不同的地方抽取出来即可,每个资源控制器都有一个 Model 模型类,以及对应的视图路径,如果想创建一个资源控制器,只要让子类继承此父类即可,并且这个资源控制器父类是根据生命周期来进行自动化操作的,比如创建一个用户数据,因为邮箱是用来当做登录凭证的,并且也不允许用户有同名,所以在创建数据之前应该进行判断是否允许创建:
1 2 3 4
protected function checkEnableCreate(): bool|ResponseInterface { return true; }
use App\Controller\ResourceController; use App\Middleware\Admin\AuthPermission; use App\Model\User; use App\Query\QueryConstant; use App\Query\QueryHandler; use Hyperf\HttpServer\Annotation\AutoController; use Hyperf\HttpServer\Annotation\Middleware; use Psr\Http\Message\ResponseInterface;
#[AutoController(prefix: '/admin/user')] #[Middleware(AuthPermission::class)] class UserController extends ResourceController { public function __construct() { $this->model = User::class; $this->path = 'user'; }
protected function getList(): \Hyperf\Contract\LengthAwarePaginatorInterface { $handler = new QueryHandler([ 'name' => QueryConstant::LIKE, 'email' => QueryConstant::LIKE, 'register_ip' => QueryConstant::LIKE, ]);