owl-admin: 利用 Pipeline 实现任意组件可全局重写
写在前面
本文只是借用
owl-admin
中组件封装的例子, 给大家分享一种Pipeline
的使用场景
欢迎大家探讨Pipeline
的其他使用场景
至于
owl-admin
, 只是一个基于laravel
和amis
开发的又快又灵活的框架, 大家不必过多关心, 当然, 如果有想了解的也可以 点击这里 了解更多内容 🤣
什么是管道
使用 Pipeline
你可以将一个实例对象(object)在多个类之间传递,就像流水顺着管道依次流淌一般,层层传递,你就得到了从头至尾一系列执行操作的 “最终” 结果。
laravel
中许多地方都用了 Pipeline
, 最典型的便是: Middleware
用一段简单的代码演示一下:
$pipeline = new \Illuminate\Pipeline\Pipeline();
// 原始传入数据
$user = [
'name' => '张三',
'age' => 18,
'gender' => '男',
];
$result = $pipeline
->send($user)
// ->via('run') // 你可以通过调用 via 方法指定一个方法名,Pipeline 会调用该方法, 替代默认的 handle 方法
->through([
// 这里如果传入的是一个类名, Pipeline 会默认调用该类中的 handle 方法
GenderTransition::class,
// handle 方法的参数是 $user (原始数据) 和 $next
function ($user, $next){ // 这里也可以直接传入 闭包
// 这里的 $user 是上一个类传递过来的数据
// 假设: GenderTransition 类中, 将 gender 设置为了 女
// 那么, 这个方法中 $user['gender'] = '女'
// 再次给 $user 添加属性
$user['address'] = 'M78 星云';
// 执行下一个类, 传递 $user
return $next($user);
},
// ...
])
->then(function ($user) {
// 最后一个类执行完后, 会执行 then 方法
// 你可以做你想做的事, 比如:
$user['address'] .= ' 123456';
// 最后返回 $user
return $user;
});
var_dump($result);
/*
array(4) {
["name"]=>
string(6) "张三"
["age"]=>
int(18)
["sex"]=>
string(1) "女"
["address"]=>
string(15) "M78 星云 123456"
}
*/
将管道应用到 owl-admin 的组件中
首先, 对 Pipeline 稍作封装
<?php
namespace Slowlyo\OwlAdmin\Support\Cores;
use Slowlyo\OwlAdmin\Admin;
class AdminPipeline
{
/**
* @param $key
* @param $passable
* @param callable|null $callback
*
* @return mixed
*/
public static function handle($key, $passable, callable $callback = null)
{
$do = fn($i) => $callback ? $callback($i) : $i; // 如果传入了回调函数, 则执行回调函数, 否则直接返回 $passable
$pipes = Admin::context()->get($key, []); // 从框架上下文中获取管道内容
// 管道为空, 则直接返回原始数据, 稍微提升些许性能
if (blank($pipes)) {
return $do($passable);
}
// 执行管道
return admin_pipeline($passable)->through($pipes)->then(fn($i) => $do($i));
}
/**
* 该方法用于设置管道, 它们通过 $key 来对应
*
* @param array|mixed $pipes
*
* @return void
*/
public static function through($key, $pipes)
{
Admin::context()->set($key, $pipes);
}
}
这样, 我们只需要在调用到 handle
方法之前, 给框架的上下文中写入对应的管道内容, 就实现了逻辑的注入
结合 owl-admin 的组件特性
基于
amis
通过后端返回组件的json
结构, 由前端渲染对应的组件
就可以实现以下逻辑:
// 所有组件在返回给前端之前, 都会执行这么一段代码
AdminPipeline::handle(static::class, $this->amisSchema);
// - 由于要给所有的组件都加上这行代码, 不可能重复写 100 多遍吧
// - 所以这里把逻辑写在了所有组件的基类中
// - 通过 static::class 来获取当前使用的组件的类名
// 那么, 我们只需要在返回之前, 给框架上下文中添加管道内容即可
AdminPipeline::through(
Button::class, // 这里对应到上面的 static::class
[
// something...
]
);
将逻辑完整的提取出来, 流程如下:
// 原本的结构
$schema = [
'type' => 'button',
'label' => '按钮',
'level' => 'primary'
];
// 通过 Pipeline 处理, 得到的新的结构
$lastSchema = app(Pipeline::class)
->send($schema)
->through([
function ($schema, $next){
$schema['level'] = 'success'; // 修改按钮颜色
$schema['label'] = '我是个按钮'; // 替换按钮文字
$schema['icon'] = 'fa fa-add'; // 添加按钮图标
// 甚至可以返回个新的结构
//$schema = [];
return $next($schema);
}
])
->then(fn($schema)=>$schema);
这么一来, 所有的基础组件就都支持了使用 Pipeline
来扩展, 或者是加一些基础属性
结合这个思路, 只需要给框架中做过一层封装的组件分别定义一些 key
, 用于在上下文中设置管道内容, 那么页面内容基本上就都可以自定义了 👏
// 基础页面
const PIPE_BASE_PAGE = 'pipe_schema_base_page';
// 返回按钮
const PIPE_BACK_ACTION = 'pipe_schema_back_action';
// 批量删除按钮
const PIPE_BULK_DELETE_ACTION = 'pipe_schema_bulk_delete_action';
// 添加按钮
const PIPE_CREATE_ACTION = 'pipe_schema_create_action';
// 编辑按钮
const PIPE_EDIT_ACTION = 'pipe_schema_edit_action';
// 查看按钮
const PIPE_SHOW_ACTION = 'pipe_schema_show_action';
// 删除按钮
const PIPE_DELETE_ACTION = 'pipe_schema_delete_action';
// 行操作按钮
const PIPE_ROW_ACTIONS = 'pipe_schema_row_actions';
// 基础筛选
const PIPE_BASE_FILTER = 'pipe_schema_base_filter';
// 基础CRUD
const PIPE_BASE_CRUD = 'pipe_schema_base_crud';
// 基础头部工具栏
const PIPE_BASE_HEADER_TOOLBAR = 'pipe_schema_base_header_toolbar';
// 基础表单
const PIPE_BASE_FORM = 'pipe_schema_base_form';
// 基础详情
const PIPE_BASE_DETAIL = 'pipe_schema_base_detail';
// 基础列表
const PIPE_BASE_LIST = 'pipe_schema_base_list';
// 导出按钮
const PIPE_EXPORT_ACTION = 'pipe_schema_export_action';
本作品采用《CC 协议》,转载必须注明作者和本文链接
看起来好复杂