This commit is contained in:
mzl
2025-07-18 17:32:42 +08:00
parent 94938d0586
commit 228408149b
23 changed files with 1880 additions and 1008 deletions
+80 -64
View File
@@ -1,47 +1,51 @@
<?php
namespace app\common\traits;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
/**
* 数据转换器 Trait
* 用于将原始数据(如模型、集合、分页等)统一转换为数组格式,
* 并支持字段级与结果级的灵活后置加工。
*/
trait DataTransformer
{
/**
* 核心方法:对任意数据进行转换并返回数组
* 核心转换方法
*
* @param mixed $rawData 原始数据,可为 Paginator / Model / Collection / array 等
* @param callable|null $postProcessor 可选的后置处理回调,接受处理完的数组再返回新的数组
* @param callable|null $fieldFormatter 可选的字段命名转换回调,如 snake_case
* @param mixed $rawData 原始数据:Model、Collection、Paginator、数组等
* @param array $opts 可选配置:
* - 'fieldFormatter' => callable 字段名格式化回调,如 fn($f)=>snake_case($f)
* - 'postFormatter' => callable 结果级加工回调,接收整个数组并返回
*
* @return array
* @return array 转换并加工后的纯 PHP 数组
*/
protected function transformData(
mixed $rawData,
?callable $postProcessor = null,
?callable $postFormatter = null,
?callable $fieldFormatter = null
): array {
try {
$fieldFormatter = $fieldFormatter ?: fn($field) => $field;
if ($rawData instanceof LengthAwarePaginator) {
$formatted = $this->formatPaginated($rawData, $postProcessor, $fieldFormatter);
$arr = $this->formatPaginated($rawData, $postFormatter, $fieldFormatter);
} elseif ($rawData instanceof Model) {
$formatted = $this->formatModel($rawData, $fieldFormatter);
$arr = $this->formatModel($rawData, $fieldFormatter);
} elseif ($rawData instanceof Collection) {
$formatted = $this->formatCollection($rawData, $fieldFormatter);
$arr = $this->formatCollection($rawData, $fieldFormatter);
} elseif (is_array($rawData)) {
$formatted = $this->formatArray($rawData, $fieldFormatter);
$arr = $this->formatArray($rawData, $fieldFormatter);
} else {
$formatted = (array) $rawData;
$arr = (array)$rawData;
}
if (is_callable($postProcessor)) {
$formatted = $postProcessor($formatted);
if (is_callable($postFormatter)) {
$arr = $postFormatter($arr);
}
return $formatted;
return $arr;
} catch (\Throwable $e) {
return [
'error' => true,
@@ -52,84 +56,96 @@ trait DataTransformer
}
/**
* 格式化分页数据:将每条记录也进行 transformData 递归处理
* 格式化分页数据:转换每条记录
*
* @param LengthAwarePaginator $paginator
* @param callable|null $postProcessor
* @param callable $fieldFormatter
* @return array
* @param LengthAwarePaginator $p
* @param callable $fmt
* @return array{records: array, total: int, current: int, size: int}
*/
protected function formatPaginated(
LengthAwarePaginator $paginator,
?callable $postProcessor,
callable $fieldFormatter
LengthAwarePaginator $p,
?callable $postFormatter,
callable $fmt
): array {
$list = $paginator->getCollection()->map(function ($item) use ($postProcessor, $fieldFormatter) {
return $this->transformData($item, $postProcessor, $fieldFormatter);
})->toArray();
$records = $p->getCollection()
->map(fn($item) => $this->transformData($item, $postFormatter, $fmt))
->toArray();
return [
'records' => $list,
'total' => $paginator->total(),
'current' => $paginator->currentPage(),
'size' => $paginator->perPage()
'records' => $records,
'total' => $p->total(),
'current' => $p->currentPage(),
'size' => $p->perPage(),
];
}
/**
* 格式化 Eloquent 模型:递归嵌套关联
* 格式化 Eloquent 模型:主表属性 + 关联递归
*
* @param Model $model
* @param callable $fieldFormatter
* @param callable $fmt
* @return array
*/
protected function formatModel(Model $model, callable $fieldFormatter): array
{
$formatted = [];
// 主表字段
protected function formatModel(
Model $model,
callable $fmt
): array {
$out = [];
// 1) 主表字段
foreach ($model->getAttributes() as $key => $value) {
$formatted[$fieldFormatter($key)] = $value;
$out[$fmt($key)] = $value;
}
// 关联数据,递归嵌套
foreach ($model->getRelations() as $relation => $relationData) {
if ($relationData instanceof Model) {
// 单个关联模型
$formatted[$relation] = $this->formatModel($relationData, $fieldFormatter);
} elseif ($relationData instanceof Collection) {
// 关联集合
$formatted[$relation] = $relationData
->map(fn($item) => $this->transformData($item, null, $fieldFormatter))
// 2) 关联递归
foreach ($model->getRelations() as $rel => $relData) {
if ($relData instanceof Model) {
$out[$rel] = $this->formatModel($relData, $fmt);
} elseif ($relData instanceof Collection) {
$out[$rel] = $relData
->map(fn($item) => $this->transformData($item, null, $fmt))
->toArray();
} else {
$out[$rel] = $relData;
}
}
return $formatted;
return $out;
}
/**
* 格式化集合
* 格式化集合数据:对每个元素递归调用 transformData
*
* @param Collection $collection
* @param callable $fieldFormatter
* @param Collection $c
* @param callable $fmt
* @return array
*/
protected function formatCollection(Collection $collection, callable $fieldFormatter): array
{
return $collection->map(fn($item) => $this->transformData($item, null, $fieldFormatter))
->toArray();
protected function formatCollection(
Collection $c,
callable $fmt
): array {
return $c
->map(fn($item) => $this->transformData($item, null, $fmt))
->toArray();
}
/**
* 格式化普通数组
* 格式化普通数组:递归处理可能出现的模型或集合
*
* @param array $array
* @param callable $fieldFormatter
* @param array $arr
* @param callable $fmt
* @return array
*/
protected function formatArray(array $array, callable $fieldFormatter): array
{
return array_map(fn($item) => $this->transformData($item, null, $fieldFormatter), $array);
protected function formatArray(
array $arr,
callable $fmt
): array {
return array_map(function($item) use ($fmt) {
if (is_array($item)
|| $item instanceof Model
|| $item instanceof Collection
|| $item instanceof LengthAwarePaginator
) {
return $this->transformData($item, null, $fmt);
}
return $item;
}, $arr);
}
}
+154
View File
@@ -0,0 +1,154 @@
<?php
namespace app\common\traits;
use support\Request;
trait FormatKvTrait
{
/**
* 主入口:一行把前端扁平化参数变成完整的查询配置
*
* @param Request $request
* @param string $modelClass Eloquent 模型类名
* @return array{
* model: string,
* where: array,
* order: array<string,string>,
* fields: string[],
* include: string[]
* }
*/
protected function buildQueryConfig(Request $request, string $modelClass): array
{
$all = $request->all();
return [
'model' => $modelClass,
'where' => $this->parseFilters($all),
'order' => $this->parseSort($all['sort'] ?? ''),
'fields' => $this->parseCsv($all['fields'] ?? ''),
'include' => $this->parseCsv($all['include'] ?? ''),
];
}
/**
* 把除 sort/fields/include/page/limit 以外的所有参数
* 智能转换为 where 条件
*
* 支持:
* - 等值:foo=bar
* - NOT:foo=!bar 或 foo=!bar1,bar2
* - 范围:foo=2025-07-01,2025-07-15 → between
* - 列表:foo=a,b,c → in
* - NULL:foo=null
* - NOT NULL:foo=!null
* - 通配符:foo=xia* → like 'xia%'
*
* 关联字段直接写 relation.field=xxx
*/
protected function parseFilters(array $params): array
{
$where = [];
$reserved = ['sort','fields','include','page','limit'];
foreach ($params as $key => $val) {
if (in_array($key, $reserved, true)) {
continue;
}
// NULL / NOT NULL
if ($val === 'null') {
$where[$key] = ['is null'];
continue;
}
if ($val === '!null') {
$where[$key] = ['is not null'];
continue;
}
// NOT equal or NOT IN
if (str_starts_with($val, '!')) {
$v = substr($val, 1);
if (str_contains($v, ',')) {
$list = array_map('trim', explode(',', $v));
$where[$key] = ['not in', $list];
} else {
$where[$key] = ['<>', $this->cast($v)];
}
continue;
}
// BETWEEN or IN
if (str_contains($val, ',')) {
$parts = array_map('trim', explode(',', $val));
if (count($parts) === 2 && $this->isRangeValue($parts[0], $parts[1])) {
$where[$key] = ['between', $parts];
} else {
$where[$key] = ['in', $this->castArray($parts)];
}
continue;
}
// LIKE
if (str_contains($val, '*') || str_contains($val, '%')) {
$pattern = str_replace('*', '%', $val);
$where[$key] = ['like', $pattern];
continue;
}
// 默认 EQ
$where[$key] = $this->cast($val);
}
return $where;
}
/**
* 解析 sort=id,-created_at → ['id'=>'asc','created_at'=>'desc']
*/
protected function parseSort(string $sort): array
{
$order = [];
foreach (explode(',', $sort) as $piece) {
$f = trim($piece);
if ($f === '') {
continue;
}
if ($f[0] === '-') {
$order[substr($f, 1)] = 'desc';
} else {
$order[$f] = 'asc';
}
}
return $order;
}
/**
* 逗号分割成数组
*/
protected function parseCsv(string $csv): array
{
if ($csv === '') {
return [];
}
return array_filter(array_map('trim', explode(',', $csv)));
}
/** 简单类型转换:全数字→int/float,其余保留原字符串 */
protected function cast(string $v): mixed
{
if (is_numeric($v)) {
return strpos($v, '.') !== false ? (float)$v : (int)$v;
}
return $v;
}
/** 批量类型转换 */
protected function castArray(array $arr): array
{
return array_map(fn($v) => $this->cast($v), $arr);
}
/** 判断两端是否可做 BETWEEN(数字或 YYYY-MM-DD 日期) */
protected function isRangeValue(string $a, string $b): bool
{
return (is_numeric($a) && is_numeric($b))
|| (preg_match('/^\d{4}-\d{2}-\d{2}$/', $a)
&& preg_match('/^\d{4}-\d{2}-\d{2}$/', $b));
}
}
@@ -0,0 +1,126 @@
<?php
namespace app\common\traits\crud;
use support\Request;
/**
* CRUD 操作控制器 Trait
* 提供通用的增删改查控制器方法,通过调用 service 层实现业务逻辑解耦。
*/
trait CrudControllerTrait
{
/**
* 获取资源列表(GET /resource)
*
* @param Request $request 请求对象
* @return mixed 返回分页数据及总数
*/
public function index(Request $request)
{
$raw = $this->service->serviceList($request);
$data = $this->transformData($raw, method_exists($this, 'indexFormat')
? [$this, 'indexFormat']
: null);
return $this->success($data);
}
/**
* 获取指定资源详情(GET /resource/{id})
*
* @param Request $request 请求对象
* @param int|string $id 资源 ID
* @return mixed 返回资源详情数据
*/
public function show(Request $request)
{
$raw = $this->service->serviceShow($request);
$data = $this->transformData($raw, method_exists($this, 'showFormat')
? [$this, 'showFormat']
: null);
return $this->success($data);
}
/**
* 创建新资源(POST /resource)
*
* @param Request $request 请求对象
* @return mixed 返回创建成功的数据或信息
*/
public function store(Request $request)
{
$res = $this->service->serviceStore($request);
return $this->success($res, '创建成功');
}
/**
* 更新指定资源(PUT/PATCH /resource/{id})
*
* @param Request $request 请求对象
* @return mixed 返回更新成功的数据或信息
*/
public function update(Request $request)
{
$res = $this->service->serviceUpdate($request);
return $this->success($res, '更新成功');
}
/**
* 删除指定资源(DELETE /resource/{id})
*
* @param Request $request 请求对象
* @return mixed 返回删除操作结果
*/
public function destroy(Request $request)
{
$count = $this->service->serviceDestroy($request);
return $this->success([], "删除了 {$count} 条");
}
// ----------------------------------------------------------------
// 以下三个钩子方法,**在使用此 Trait 的 Controller** 中实现
// ----------------------------------------------------------------
/**
* 字段名格式化:例如全部下划线
*
* @param string $field
* @return string
*/
// protected function fieldFormatter(string $field): string
// {
// return snake_case($field);
// }
/**
* 字段级后置加工规则
*
* @return array<string,callable>
* 键为 “点号嵌套”或 “通配符” 字段路径,
* 值为 fn($value, $row)=>mixed
*/
// protected function getFieldTransforms(): array
// {
// return [
// 'name' => fn($v,$row)=>strtoupper($v),
// 'orders.*.total' => fn($v,$row)=>number_format($v,2),
// ];
// }
/**
* 结果级后置加工:接收整个数组,可任意增删改
*
* @param array $data
* @return array
*/
// protected function postFormat(array $data): array
// {
// // 例如重命名 records 为 items
// if (isset($data['records'])) {
// $data['items'] = $data['records'];
// unset($data['records']);
// }
// return $data;
// }
}
+96
View File
@@ -0,0 +1,96 @@
<?php
namespace app\common\traits\crud;
use app\common\utils\CrudUtil;
/**
* Trait CrudLogicTrait
*
* 提供最基础的 CRUD 和复杂查询方法,基于 CrudUtil。
* 使用此 Trait 的类,须定义:
* protected string $modelClass = \App\common\model\YourModel::class;
*/
trait CrudLogicTrait
{
/**
* 分页列表
*/
public function logicList(array $config, int $limit, int $page)
{
$config['model'] = $this->modelClass;
return CrudUtil::paginate(CrudUtil::buildQuery($config), $limit, $page);
}
/**
* 全量列表
*/
public function logicListAll(array $config)
{
$config['model'] = $this->modelClass;
return CrudUtil::getAll(CrudUtil::buildQuery($config));
}
/**
* 查询单条
*/
public function logicShow(array $config)
{
return CrudUtil::findOneBy(CrudUtil::buildQuery($config));
}
/**
* 任意条件查询单条(可带 dataLimit)
*/
// public function logicShowBy(array $where, array $with = [], array $dataLimit = [])
// {
// return CrudUtil::findOneBy($this->modelClass, $where, $with, $dataLimit);
// }
/**
* 创建(可带 dataLimit)
*/
public function logicCreate(array $data, array $dataLimit = [])
{
return CrudUtil::create($this->modelClass, $data, $dataLimit);
}
/**
* 主键更新(可带 dataLimit)
*/
// public function logicUpdateByPk($id, array $data, array $dataLimit = [])
// {
// return CrudUtil::updateByPk($this->modelClass, $id, $data, $dataLimit);
// }
/**
* 任意条件更新(可带 dataLimit)
*/
public function logicUpdateBy(array $config, array $data)
{
return CrudUtil::updateBy(CrudUtil::buildQuery($config), $data);
}
/**
* 主键删除(批量)
*/
// public function logicDeleteByPk(array $ids)
// {
// return CrudUtil::deleteByPk($this->modelClass, $ids);
// }
/**
* 任意条件删除(可带 dataLimit)
*/
public function logicDeleteBy(array $config)
{
return CrudUtil::deleteBy(CrudUtil::buildQuery($config));
}
/**
* 复杂查询回调
*/
public function logicComplex(callable $callback)
{
return CrudUtil::executeComplex($this->modelClass, $callback);
}
}
+209
View File
@@ -0,0 +1,209 @@
<?php
namespace app\common\traits\crud;
use support\Request;
/**
* Trait CrudServiceTrait
*
* 提供标准的增删改查 Service 接口,依赖 CrudLogicTrait。
* 使用此 Trait 的 Service 类,须:
* use CrudLogicTrait, CrudServiceTrait;
* protected string $modelClass = \App\common\model\YourModel::class;
*/
trait CrudServiceTrait
{
use CrudLogicTrait;
/** 具体 Service 里定义:protected string $modelClass = YourModel::class; */
/** 分页列表 */
public function serviceList(Request $request)
{
if (method_exists($this, 'complexQuery')) {
$result = $this->complexQuery($request->get());
} else {
$config = $this->buildQueryConfig($request, 'index');
$limit = (int)$request->get('limit', 15);
$page = (int)$request->get('page', 1);
$result = $this->logicList($config, $limit, $page);
}
return $this->postProcess('List', $result);
}
/** 全量列表 */
public function serviceListAll(Request $request)
{
if (method_exists($this, 'complexQuery')) {
$result = $this->complexQuery($request->get());
} else {
$config = $this->buildQueryConfig($request, 'default');
$result = $this->logicListAll($config);
}
return $this->postProcess('List', $result);
}
/** 查看单条 */
public function serviceShow(Request $request)
{
$config = $this->buildQueryConfig($request, 'show');
$item = $this->logicShow($config);
return $this->postProcess('Show', $item);
}
/** 创建 */
public function serviceStore(Request $request)
{
$data = $request->post();
$dl = $this->getDataLimitFromRequest($request);
$created = $this->logicCreate($data, $dl);
return $this->postProcess('Store', $created);
}
/** 更新 */
public function serviceUpdate(Request $request)
{
$data= $request->post();
$config = $this->buildQueryConfig($request, 'update');
$updated = $this->logicUpdateBy($config, $data);
return $this->postProcess('Update', $updated);
}
/** 删除 */
public function serviceDestroy(Request $request)
{
$config = $this->buildQueryConfig($request, 'destroy');
$deleted = $this->logicDeleteBy($config);
return $this->postProcess('Destroy', $deleted);
}
/** ----------------- 公共辅助方法 ----------------- */
/**
* 构造 CrudUtil 的查询配置1
*
* 前端可以通过以下参数控制:
* - filter_<column>=<value> → where
* - sort_by=<column>&order=<asc|desc>
* - fields=id,name,email → select
* - with=roles,posts → 关联
* - withFields=roles:id,name;... → 关联字段
* - dataLimitType=personal|auth
* - dataLimitField=admin_id
*/
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
{
$p = $request->all();
$config = [];
$config['model'] = $this->modelClass;
switch ($scene) {
case 'default':
$config['where']['status'] = 1;
$config['order']['sort'] = ['desc'];
$config['order']['id'] = ['desc'];
break;
case 'index':
$config['where']['status'] = 1;
$config['order']['sort'] = ['desc'];
$config['order']['id'] = ['desc'];
break;
case 'show':
$config['where']['id'] = $p['id'];
$config['where']['status'] = 1;
break;
case 'store':
break;
case 'update':
$config['where']['id'] = $p['id'];
break;
case 'destroy':
$config['where']['id'] = $p['id'];
break;
}
return $config;
// // 1. where 从 filter_ 前缀自动映射
// $where = [];
// foreach ($p as $key => $val) {
// if (strpos($key, 'filter_') === 0 && $val !== '') {
// $col = substr($key, 7);
// // 支持 %like% 查询
// $where[$col] = strpos($val, '%') !== false
// ? ['like', $val]
// : $val;
// }
// }
// // 2. 其余配置
// return [
// 'model' => $this->modelClass,
// 'where' => $where,
// 'order' => [
// $p['sort_by'] ?? 'id' => $p['order'] ?? 'desc'
// ],
// 'fields' => !empty($p['fields'])
// ? explode(',', $p['fields'])
// : [],
// 'with' => !empty($p['with'])
// ? explode(',', $p['with'])
// : [],
// 'withFields' => $this->parseWithFields(
// $p['withFields'] ?? ''
// ),
// 'dataLimit' => $this->getDataLimitFromRequest($request),
// ];
}
/** 解析 withFields 字符串 "rel:field,field;..." */
protected function parseWithFields(string $str): array
{
if (!$str) {
return [];
}
$out = [];
foreach (explode(';', $str) as $pair) {
[$rel, $flds] = explode(':', $pair);
$out[$rel] = explode(',', $flds);
}
return $out;
}
/** 从请求读取 dataLimitType, dataLimitField */
protected function getDataLimitFromRequest(Request $req): array
{
$type = $req->get('dataLimitType');
if (!$type) {
return [];
}
return [
'type' => $type,
'field' => $req->get('dataLimitField', 'admin_id'),
];
}
/**
* 处理复杂查询
* 覆盖此方法,实现复杂查询逻辑
*/
// protected function complexQuery(array $params): array
// {
// throw new \LogicException('请在子类实现 complexQuery');
// }
/**
* 根据操作类型,调用对应的后置钩子
*
* @param string $op 操作:List, Show, Store, Update, Destroy
* @param mixed $data 原始返回值
* @return mixed 处理后的返回值
*/
protected function postProcess(string $op, mixed $data): mixed
{
$hook = "afterService{$op}";
return method_exists($this, $hook)
? $this->{$hook}($data)
: $data;
}
}
@@ -0,0 +1,270 @@
<?php
namespace app\common\traits;
use support\Request;
/**
* CrudServiceTrait
*
* 合并了:
* - 扁平化 Request → 查询配置(where/with/order/fields/withFields/dataLimit)
* - 标准增删改查 Service 接口(serviceList/Show/Store/Update/Destroy)
* - 复杂查询入口(complexQuery)
* - 统一后置钩子(postProcess)
*
* 使用方式:
* class XxxService {
* use CrudLogicTrait, CrudServiceTrait;
* protected string $modelClass = \app\xxx\model\Xxx::class;
* }
*/
trait CrudServiceTrait_bak2
{
// use CrudLogicTrait;
// ---------------- Service 接口 ---------------- //
/**
* 列表接口
* - 支持 complex_query=1 → 调用 complexQuery()
* - 否则自动扁平化构建查询配置并分页调用 logicList()
*/
public function serviceList(Request $request)
{
if ($request->get('complex_query') && method_exists($this, 'complexQuery')) {
$data = $this->complexQuery($request->get());
} else {
$cfg = $this->buildQueryConfig($request, 'list');
$limit = (int)$request->get('limit', 15);
$page = (int)$request->get('page', 1);
$data = $this->logicList($cfg, $limit, $page);
}
return $this->postProcess('List', $data);
}
/** 查看单条 */
public function serviceShow(Request $request, $id)
{
$item = $this->logicShow($id, []);
return $this->postProcess('Show', $item);
}
/** 创建 */
public function serviceStore(Request $request)
{
$data = $request->post();
$dl = $this->getDataLimitFromRequest($request);
$item = $this->logicCreate($data, $dl);
return $this->postProcess('Store', $item);
}
/** 更新 */
public function serviceUpdate(Request $request)
{
$key = (new $this->modelClass)->getKeyName();
$id = (int)$request->post($key);
$data = $request->post();
$dl = $this->getDataLimitFromRequest($request);
$item = $this->logicUpdateByPk($id, $data, $dl);
return $this->postProcess('Update', $item);
}
/** 删除 */
public function serviceDestroy(Request $request)
{
$key = (new $this->modelClass)->getKeyName();
$ids = (array)$request->post($key, []);
$result = $this->logicDeleteByPk($ids);
return $this->postProcess('Destroy', $result);
}
// ---------------- 统一后置钩子 ---------------- //
/**
* 各操作后触发 afterService{Op}()
* @param string $op List|Show|Store|Update|Destroy
*/
protected function postProcess(string $op, mixed $data): mixed
{
$hook = "afterService{$op}";
return method_exists($this, $hook)
? $this->{$hook}($data)
: $data;
}
// ---------------- 复杂查询入口 ---------------- //
/**
* 子类 override 实现完全定制的复杂查询
* @param array $params 全部请求参数
*/
protected function complexQuery(array $params)
{
throw new \LogicException('请在子类实现 complexQuery()');
}
// ------------- 辅助:数据权限 & withFields 解析 ------------- //
/** 从请求读取 dataLimitType & dataLimitField → ['type'=>..,'field'=>..] */
protected function getDataLimitFromRequest(Request $req): array
{
$type = $req->get('dataLimitType');
if (!$type) {
return [];
}
return [
'type' => $type,
'field' => $req->get('dataLimitField', 'admin_id'),
];
}
/** 解析 withFields 字符串 "rel:field,field;rel2:..." */
protected function parseWithFields(string $str): array
{
if (!$str) {
return [];
}
$out = [];
foreach (explode(';', $str) as $pair) {
[$rel, $flds] = explode(':', $pair);
$out[$rel] = explode(',', $flds);
}
return $out;
}
// ------------- 扁平化 Request → 查询配置 ------------- //
/**
* 构建完整配置
* 返回 [
* where=>[], with=>[], order=>[], withFields=>?, fields=>?, dataLimit=>[]
* ]
*/
protected function buildQueryConfig(Request $request, string $scene = 'list'): array
{
return [
'where' => $this->buildFilters($request, $scene),
'with' => $this->buildRelations($request, $scene),
'order' => $this->buildOrder($request, $scene),
'withFields' => $this->buildWithFields($request,$scene),
'fields' => $this->buildFields($request, $scene),
'dataLimit' => $this->buildDataLimit($request, $scene),
];
}
/**
* 通用合并方法:取公共配置 + 场景专属配置,若均为 array 则 array_merge
*
* @param mixed $common 公共配置
* @param Request $request
* @param string $scene
* @param string $type Filters|Relations|Order|WithFields|Fields|DataLimit
*/
private function applySceneConfig(
mixed $common,
Request $request,
string $scene,
string $type
): mixed {
$method = 'apply' . ucfirst($scene) . $type;
if (! method_exists($this, $method)) {
return $common;
}
$sceneCfg = $this->{$method}($common, $request);
if (is_array($common) && is_array($sceneCfg)) {
return array_merge($common, $sceneCfg);
}
return $sceneCfg;
}
/** 构建 WHERE:commonFilters + apply{Scene}Filters */
protected function buildFilters(Request $r, string $scene): array
{
$common = $this->commonFilters($r);
return (array)$this->applySceneConfig($common, $r, $scene, 'Filters');
}
/** 构建 with 关联 */
protected function buildRelations(Request $r, string $scene): array
{
$common = $this->commonRelations();
return (array)$this->applySceneConfig($common, $r, $scene, 'Relations');
}
/** 构建 order 排序 */
protected function buildOrder(Request $r, string $scene): array
{
$common = $this->commonOrder();
return (array)$this->applySceneConfig($common, $r, $scene, 'Order');
}
/** 构建 withFields(关联模型字段白名单) */
protected function buildWithFields(Request $r, string $scene): ?array
{
$common = [];
return $this->applySceneConfig($common, $r, $scene, 'WithFields');
}
/** 构建 fields(主表字段白名单) */
protected function buildFields(Request $r, string $scene): ?array
{
$common = [];
return $this->applySceneConfig($common, $r, $scene, 'Fields');
}
/** 构建 dataLimit(数据权限) */
protected function buildDataLimit(Request $r, string $scene): array
{
$common = [];
return (array)$this->applySceneConfig($common, $r, $scene, 'DataLimit');
}
// ------------- 可重写的“公共 + 场景” 方法 ------------- //
// --- common* 系列(全局默认) ---
protected function commonFilters(Request $r): array { return []; }
protected function commonRelations(): array { return []; }
protected function commonOrder(): array { return []; }
// // --- list* 系列 ---
// protected function applyListFilters(array $c, Request $r): array { return $this->listFilters($r); }
// protected function listFilters(Request $r): array { return []; }
// protected function applyListRelations(array $c, Request $r): array { return $this->listRelations(); }
// protected function listRelations(): array { return []; }
// protected function applyListOrder(array $c, Request $r): array { return $this->listOrder($r); }
// protected function listOrder(Request $r): array { return ['created_at'=>'desc']; }
// protected function applyListWithFields(array $c, Request $r): array{ return $this->listWithFields($r); }
// protected function listWithFields(Request $r): array { return []; }
// protected function applyListFields(array $c, Request $r): array { return $this->listFields($r); }
// protected function listFields(Request $r): array { return []; }
// protected function applyListDataLimit(array $c, Request $r): array { return $this->listDataLimit($r); }
// protected function listDataLimit(Request $r): array { return []; }
// // --- detail* 系列 ---
// protected function applyDetailFilters(array $c, Request $r): array { return $this->detailFilters($r); }
// protected function detailFilters(Request $r): array { return ['id'=>$r->get('id')]; }
// protected function applyDetailRelations(array $c, Request $r): array { return $this->detailRelations(); }
// protected function detailRelations(): array { return []; }
// protected function applyDetailOrder(array $c, Request $r): array { return $this->detailOrder($r); }
// protected function detailOrder(Request $r): array { return []; }
// protected function applyDetailWithFields(array $c, Request $r): array{ return $this->detailWithFields($r); }
// protected function detailWithFields(Request $r): array { return []; }
// protected function applyDetailFields(array $c, Request $r): array { return $this->detailFields($r); }
// protected function detailFields(Request $r): array { return []; }
// protected function applyDetailDataLimit(array $c, Request $r): array { return $this->listDataLimit($r); }
// // --- export* 系列(如有需要可 override) ---
// protected function applyExportFilters(array $c, Request $r): array { return $this->exportFilters($r); }
// protected function exportFilters(Request $r): array { return []; }
}