Files
bsh_sample/app/common/traits/DataTransformer.php
T
2025-12-08 17:37:34 +08:00

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);
}
}