PipeService 是一个基于 Laravel Pipeline 模式的可插拔管道服务框架。它提供了一套标准化的业务处理流程,支持参数映射、数据校验、异常处理等功能,特别适用于复杂业务流程的模块化处理。
PipeService
├── Facades/
│ └── PipeRoute.php # Facade 门面类
├── Factories/
│ └── PipeHandlerFactory.php # 管道处理器工厂
├── Pipes/
│ ├── PipeInterface.php # 管道接口
│ └── AbstractPipe.php # 抽象管道基类
├── Mappers/
│ ├── MapperInterface.php # 映射器接口
│ └── AdvancedMapper.php # 高级映射器
├── ExceptionHandlers/
│ └── ExceptionHandlerInterface.php # 异常处理器接口
├── Exceptions/
│ ├── MapException.php # 映射异常
│ ├── EntityValidateException.php # 实体校验异常
│ └── MapValidateException.php # 映射校验异常
├── PipeRouteManager.php # 管道路由管理器
├── RequestManager.php # 请求管理器
└── RequestManagerServiceProvider.php # 服务提供者
- 管道线路 (Pipeline Line): 一系列按顺序执行的管道组合
- 管道 (Pipe): 单个业务处理单元
- 请求管理器 (RequestManager): 管理请求生命周期和依赖容器
- 处理器工厂 (PipeHandlerFactory): 管理各种处理器的创建和调用
- ✅ 修复了服务提供者继承错误(Carbon\Laravel\ServiceProvider → Illuminate\Support\ServiceProvider)
- ✅ 增强了映射逻辑功能,支持高级映射语法
- ✅ 兼容 PHP 7.3.3 语法要求
php artisan vendor:publish --provider=JoseChan\\Pipeline\\Framework\\RequestManagerServiceProvider提供便捷的静态方法调用接口:
/**
* @method static line(string $name, \Closure $group)
* @method static pipe($pipe, $mapValidate = null, $mapper = null, $entityValidate = null, $mapExceptionHandler = null, $entityExceptionHandler = null)
* @method static through(string $lineName, $requestManager)
*/
class PipeRoute extends Facadepublic function line(string $name, \Closure $group)功能: 定义一个命名的管道线路 参数:
$name: 线路名称,必须唯一$group: 闭包函数,在其中定义管道
注意事项:
- 不支持嵌套定义,如果已有活跃的线路定义会抛出 RuntimeException
- 线路定义期间,
$groupStack会被设置为当前线路名
public function pipe($pipe, $mapValidate = null, $mapper = null, $entityValidate = null, $mapExceptionHandler = null, $entityExceptionHandler = null)功能: 在当前线路中添加一个管道 参数详解:
- $pipe: 管道类名,必须实现 PipeInterface
- $mapValidate: 映射参数校验规则
- $mapper: 参数映射逻辑
- $entityValidate: 实体校验规则
- $mapExceptionHandler: 映射异常处理器
- $entityExceptionHandler: 实体异常处理器
public function through(string $lineName, $requestManager)功能: 执行指定的管道线路 参数:
$lineName: 要执行的线路名称$requestManager: RequestManager 实例
- 请求生命周期管理: 为每个请求生成唯一ID
- 依赖注入容器: 管理请求范围内的对象实例
- 实体工厂集成: 自动配置实体创建和验证
public function __construct($request, Application $application)
public function getContainer() // 获取依赖容器
public function getRequestId(): string // 获取请求ID// 生成唯一请求ID并创建专属容器
$this->container = $this->application->instance($this->makeRequestId(), new Container());
// 注入原始请求对象
$this->container->instance("input", $this->request);
// 绑定实体工厂
$this->container->bind(IEntityFactory::class, ContainerEntityFactory::class, true);public function handle($requestManager, Closure $next)
{
// 1. 校验是否需要处理
if (!$this->verification($requestManager)) {
return $next($requestManager);
}
try {
// 2. 映射参数前处理钩子
$this->beforeMap($requestManager);
// 3. 映射参数校验
$this->mapValidate($requestManager);
// 4. 映射参数
$params = $this->map($requestManager);
// 5. 映射参数后处理钩子
$this->afterMap($requestManager);
// 6. 获取实体
$entity = $this->getEntity($params);
// 7. 实体校验
$this->validateEntity($entity);
// 8. 注册实体到容器
$requestManager->getContainer()->instance($this->abstractName($entity), $entity);
// 9. 成功回调
$this->success($requestManager);
} catch (Throwable $e) {
// 10. 失败处理
$this->fails($requestManager, $e);
}
// 11. 继续下一个管道
return $next($requestManager);
}abstract protected function verification($requestManager): bool; // 校验是否需要处理
abstract protected function getEntity($params); // 获取实体对象protected function beforeMap($requestManager) {} // 映射前钩子
protected function afterMap($requestManager) {} // 映射后钩子
protected function success($requestManager) {} // 成功回调
protected function fails($requestManager, Throwable $throwable) {} // 失败处理protected function map($requestManager)
{
$mapper = $this->mapper(); // 获取映射配置
if (empty($mapper) || !is_array($mapper)) {
return [];
}
return collect($mapper)->flatMap(function ($to, $from) use ($requestManager) {
list($entry, $field) = explode(".", $from);
/** @var Entity $entryObject */
$entryObject = $requestManager->getContainer()->get($entry);
return [$to => Arr::get($entryObject->toArray(), $field)];
})->toArray();
}映射格式: "source.field" => "target_field"
source: 容器中的对象名称field: 对象中的字段名target_field: 映射后的字段名
高级映射支持 (需要在管道中启用 useAdvancedMapping()):
"object.field[0]"- 数组索引访问"object.field.nested.deep"- 深层嵌套访问"object.field|default_value"- 默认值支持"?object.field"- 可选字段(不存在时跳过)
新增的高级映射器提供了更强大的映射功能:
use App\Services\PipeService\Mappers\AdvancedMapper;
$mapper = new AdvancedMapper($requestManager);-
基础映射:
['input.name' => 'customer_name']
-
数组索引:
['input.items[0].price' => 'first_item_price']
-
深层嵌套:
['user.profile.address.city' => 'city']
-
默认值:
['input.currency|USD' => 'currency_code']
-
可选字段:
['?input.optional_field' => 'optional_data']
-
嵌套目标:
['input.name' => 'customer.profile.name']
$result = $mapper->conditionalMap(
'input.type=premium', // 条件
['input.amount' => 'premium_amount'], // 真值映射
['input.amount' => 'regular_amount'] // 假值映射
);支持的条件操作符:=, !=, >, <, >=, <=, ?(存在性检查)
$result = $mapper->mapArray(
'input.items', // 源数组
[ // 每个元素的映射规则
'$item.name' => 'product_name',
'$item.price' => 'amount'
],
'products' // 目标键名
);class MyPipe extends AbstractPipe
{
protected function useAdvancedMapping(): bool
{
return true; // 启用高级映射
}
// 其他方法...
}public const HANDLER_MAP_VALIDATE = 'mapValidate'; // 映射校验
public const HANDLER_MAPPER = 'mapper'; // 参数映射
public const HANDLER_ENTITY_VALIDATE = 'entityValidate'; // 实体校验
public const HANDLER_MAP_EXCEPTION = 'mapExceptionHandler'; // 映射异常处理
public const HANDLER_ENTITY_EXCEPTION = 'entityExceptionHandler'; // 实体异常处理-
类名字符串:
'App\Services\SomeService\Mapper::class' -
类@方法格式:
'SomeMapper@customMethod' -
闭包函数:
function ($requestManager) { return ['field' => 'required']; }
-
带参数的类数组:
[App\Services\SomeService\Mapper::class => ["param1" => "value1"]]
public function addHandler($pipe, $handler) // 添加处理器
public function getHandler($pipe, $handlerType) // 获取处理器
public function call($pipe, $handlerType, ...$args) // 调用处理器- 继承自 Exception
- 用于参数映射过程中的异常
- 继承自 Exception
- 包含验证失败的详细信息
- 提供
getFailed()方法获取失败详情
public function __construct($message = "", $code = 0, array $failed, \Throwable $previous = null)
public function getFailed(): array // 获取验证失败的字段信息在 config/app.php 中注册服务提供者:
'providers' => [
// ...
App\Services\PipeService\RequestManagerServiceProvider::class,
],在 piperoute/piperoute.php 中定义:
use App\Services\PipeService\Facades\PipeRoute;
PipeRoute::line("order.process", function () {
// 基础管道 - 只有管道类
PipeRoute::pipe(ValidateOrderPipe::class);
// 完整配置管道
PipeRoute::pipe(
GetCurrencyPipe::class,
null, // 映射校验(不校验)
GetCurrencyMapper::class, // 参数映射器
CurrencyEntity::class, // 实体校验
CurrencyMapExceptionHandler::class, // 映射异常处理
CurrencyEntityExceptionHandler::class // 实体异常处理
);
// 使用闭包的管道
PipeRoute::pipe(
CalculatePricePipe::class,
function($requestManager) { // 映射校验闭包
return ['amount' => 'required|numeric|min:0'];
},
function($requestManager) { // 映射闭包
$input = $requestManager->getContainer()->get('input');
return ['amount' => $input->get('total_amount')];
}
);
});use App\Services\PipeService\Pipes\AbstractPipe;
use App\Services\PipeService\RequestManager;
class GetCurrencyPipe extends AbstractPipe
{
/**
* 校验是否需要处理此管道
*/
protected function verification($requestManager): bool
{
$input = $requestManager->getContainer()->get('input');
return $input->has('currency_code');
}
/**
* 获取实体对象
*/
protected function getEntity($params)
{
return new CurrencyEntity($params);
}
/**
* 映射前处理
*/
protected function beforeMap($requestManager)
{
// 可以进行一些预处理
Log::info('开始处理货币信息');
}
/**
* 成功回调
*/
protected function success($requestManager)
{
Log::info('货币处理成功');
}
/**
* 实体校验规则(如果不使用外部校验器)
*/
protected function entityRule()
{
return [
'code' => 'required|string|size:3',
'rate' => 'required|numeric|min:0'
];
}
/**
* 校验错误消息
*/
protected function entityMessage()
{
return [
'code.required' => '货币代码不能为空',
'rate.numeric' => '汇率必须为数字'
];
}
}use App\Services\PipeService\Mappers\MapperInterface;
class GetCurrencyMapper implements MapperInterface
{
public function map()
{
return [
'input.currency_code' => 'code', // input对象的currency_code字段映射到code
'input.exchange_rate' => 'rate', // input对象的exchange_rate字段映射到rate
];
}
}use App\Services\PipeService\ExceptionHandlers\ExceptionHandlerInterface;
use App\Services\PipeService\RequestManager;
class CurrencyExceptionHandler implements ExceptionHandlerInterface
{
public function handle(RequestManager $requestManager, \Throwable $e)
{
Log::error('货币处理异常', [
'request_id' => $requestManager->getRequestId(),
'error' => $e->getMessage()
]);
// 可以设置默认值或者重新抛出异常
// throw new CustomException('货币处理失败');
}
}use App\Services\PipeService\Facades\PipeRoute;
use App\Services\PipeService\RequestManager;
use Illuminate\Http\Request;
class OrderController extends Controller
{
public function store(Request $request)
{
// 创建请求管理器
$requestManager = new RequestManager($request, app());
try {
// 执行管道线路
PipeRoute::through('order.process', $requestManager);
// 从容器中获取处理结果
$currency = $requestManager->getContainer()->get(CurrencyEntity::class);
$orderData = $requestManager->getContainer()->get(OrderEntity::class);
// 业务逻辑处理
$order = Order::create($orderData->toArray());
return response()->json(['success' => true, 'order_id' => $order->id]);
} catch (\Exception $e) {
return response()->json(['error' => $e->getMessage()], 400);
}
}
}PipeRoute::pipe(
ConditionalPipe::class,
function($requestManager) {
// 动态校验规则
$input = $requestManager->getContainer()->get('input');
if ($input->get('type') === 'premium') {
return ['amount' => 'required|numeric|min:100'];
}
return ['amount' => 'required|numeric|min:1'];
}
);PipeRoute::pipe(
NotificationPipe::class,
null,
[NotificationMapper::class => ['channel' => 'email', 'template' => 'order']]
);class ComplexMapper implements MapperInterface
{
public function map()
{
return [
'user.profile.email' => 'customer_email',
'user.profile.name' => 'customer_name',
'order.items.0.price' => 'first_item_price',
];
}
}class RecoveryExceptionHandler implements ExceptionHandlerInterface
{
public function handle(RequestManager $requestManager, \Throwable $e)
{
if ($e instanceof CurrencyNotFoundException) {
// 设置默认货币
$defaultCurrency = new CurrencyEntity(['code' => 'USD', 'rate' => 1.0]);
$requestManager->getContainer()->instance(CurrencyEntity::class, $defaultCurrency);
return; // 不重新抛出异常,使用默认值继续处理
}
throw $e; // 其他异常继续抛出
}
}- 单一职责: 每个管道只负责一个具体的业务逻辑
- 无副作用: 管道处理应该是幂等的,多次执行结果一致
- 容错性: 合理处理异常情况,避免整个流程崩溃
- 延迟加载: 在
verification()中进行轻量级检查 - 缓存策略: 对重复计算的结果进行缓存
- 异步处理: 对非关键路径使用队列异步处理
// 在管道中添加调试信息
protected function beforeMap($requestManager)
{
Log::debug('Pipeline Debug', [
'pipe' => static::class,
'request_id' => $requestManager->getRequestId(),
'container_bindings' => array_keys($requestManager->getContainer()->getBindings())
]);
}- 分层异常: 区分业务异常和系统异常
- 异常恢复: 对可恢复的异常提供默认值或备选方案
- 日志记录: 详细记录异常上下文信息
-
管道线路未定义错误
- 检查
piperoute/piperoute.php中是否正确定义 - 确认线路名称拼写正确
- 检查
-
处理器类不存在
- 检查类的命名空间是否正确
- 确认类已被正确加载
-
映射字段不存在
- 检查映射配置中的字段路径
- 确认容器中的对象已正确注册
-
验证失败
- 检查验证规则是否合理
- 确认输入数据格式正确
// 查看请求管理器状态
dd($requestManager->getContainer()->getBindings());
// 查看管道执行流程
PipeRoute::line("debug.test", function() {
PipeRoute::pipe(DebugPipe::class);
});class CustomPipeHandlerFactory extends PipeHandlerFactory
{
public function getHandlerCaller($pipe, $handlerType)
{
// 添加自定义处理器解析逻辑
if ($handlerType === 'custom_handler') {
// 自定义逻辑
}
return parent::getHandlerCaller($pipe, $handlerType);
}
}class BusinessLogicException extends Exception
{
protected $context;
public function __construct($message, array $context = [])
{
$this->context = $context;
parent::__construct($message);
}
public function getContext(): array
{
return $this->context;
}
}版本: 1.1.0
最后更新: 2024年10月
维护者: 开发团队
- ✅ 修复服务提供者继承错误
- ✅ 新增 AdvancedMapper 高级映射器
- ✅ 支持数组索引、嵌套字段、默认值、可选字段等高级映射语法
- ✅ 支持条件映射和数组批量映射
- ✅ 兼容 PHP 7.3.3+ 版本
- ✅ 保持向后兼容性
- 基础管道服务功能
- 参数映射和验证
- 异常处理机制
此文档涵盖了 PipeService 包的完整使用方法,从基础概念到高级用法,以及最佳实践和故障排除。如有疑问或建议,请联系开发团队。