owl-admin: 利用 Pipeline 实现任意组件可全局重写

写在前面

本文只是借用 owl-admin 中组件封装的例子, 给大家分享一种 Pipeline 的使用场景
欢迎大家探讨 Pipeline 的其他使用场景

至于 owl-admin , 只是一个基于 laravelamis 开发的又快又灵活的框架, 大家不必过多关心, 当然, 如果有想了解的也可以 点击这里 了解更多内容 🤣

什么是管道

使用 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 协议》,转载必须注明作者和本文链接
闲来无事码两行
slowlyo
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
讨论数量: 2

看起来好复杂

1个月前 评论
iwzh 1个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!