152 lines
4.7 KiB
PHP
152 lines
4.7 KiB
PHP
<?php
|
|
namespace app\common\traits;
|
|
|
|
use Illuminate\Pagination\LengthAwarePaginator;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Support\Collection;
|
|
|
|
/**
|
|
* 数据转换器 Trait
|
|
* 用于将原始数据(如模型、集合、分页等)统一转换为数组格式,
|
|
* 并支持字段级与结果级的灵活后置加工。
|
|
*/
|
|
trait DataTransformer
|
|
{
|
|
/**
|
|
* 核心转换方法
|
|
*
|
|
* @param mixed $rawData 原始数据:Model、Collection、Paginator、数组等
|
|
* @param array $opts 可选配置:
|
|
* - 'fieldFormatter' => callable 字段名格式化回调,如 fn($f)=>snake_case($f)
|
|
* - 'postFormatter' => callable 结果级加工回调,接收整个数组并返回
|
|
*
|
|
* @return array 转换并加工后的纯 PHP 数组
|
|
*/
|
|
protected function transformData(
|
|
mixed $rawData,
|
|
?callable $postFormatter = null,
|
|
?callable $fieldFormatter = null
|
|
): array {
|
|
try {
|
|
$fieldFormatter = $fieldFormatter ?: fn($field) => $field;
|
|
if ($rawData instanceof LengthAwarePaginator) {
|
|
$arr = $this->formatPaginated($rawData, $postFormatter, $fieldFormatter);
|
|
} elseif ($rawData instanceof Model) {
|
|
$arr = $this->formatModel($rawData, $fieldFormatter);
|
|
} elseif ($rawData instanceof Collection) {
|
|
$arr = $this->formatCollection($rawData, $fieldFormatter);
|
|
} elseif (is_array($rawData)) {
|
|
$arr = $this->formatArray($rawData, $fieldFormatter);
|
|
} else {
|
|
$arr = (array)$rawData;
|
|
}
|
|
|
|
if (is_callable($postFormatter)) {
|
|
$arr = $postFormatter($arr);
|
|
}
|
|
|
|
return $arr;
|
|
} catch (\Throwable $e) {
|
|
return [
|
|
'error' => true,
|
|
'message' => $e->getMessage(),
|
|
// 'message' => $e->getMessage()."\n".$e->getLine()."\n".$e->getFile(),
|
|
'code' => $e->getCode(),
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 格式化分页数据:转换每条记录
|
|
*
|
|
* @param LengthAwarePaginator $p
|
|
* @param callable $fmt
|
|
* @return array{records: array, total: int, current: int, size: int}
|
|
*/
|
|
protected function formatPaginated(
|
|
LengthAwarePaginator $p,
|
|
?callable $postFormatter,
|
|
callable $fmt
|
|
): array {
|
|
$records = $p->getCollection()
|
|
->map(fn($item) => $this->transformData($item, $postFormatter, $fmt))
|
|
->toArray();
|
|
|
|
return [
|
|
'records' => $records,
|
|
'total' => $p->total(),
|
|
'current' => $p->currentPage(),
|
|
'size' => $p->perPage(),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 格式化 Eloquent 模型:主表属性 + 关联递归
|
|
*
|
|
* @param Model $model
|
|
* @param callable $fmt
|
|
* @return array
|
|
*/
|
|
protected function formatModel(
|
|
Model $model,
|
|
callable $fmt
|
|
): array {
|
|
$out = [];
|
|
// 1) 主表字段
|
|
foreach ($model->getAttributes() as $key => $value) {
|
|
$out[$fmt($key)] = $value;
|
|
}
|
|
// 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 $out;
|
|
}
|
|
|
|
/**
|
|
* 格式化集合数据:对每个元素递归调用 transformData
|
|
*
|
|
* @param Collection $c
|
|
* @param callable $fmt
|
|
* @return array
|
|
*/
|
|
protected function formatCollection(
|
|
Collection $c,
|
|
callable $fmt
|
|
): array {
|
|
return $c
|
|
->map(fn($item) => $this->transformData($item, null, $fmt))
|
|
->toArray();
|
|
}
|
|
|
|
/**
|
|
* 格式化普通数组:递归处理可能出现的模型或集合
|
|
*
|
|
* @param array $arr
|
|
* @param callable $fmt
|
|
* @return 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);
|
|
}
|
|
} |