在Laravel项目里,处理模型属性的状态流转是常见需求,比如用户账号状态、订单流程状态等场景都需要清晰的状态管理逻辑。通过实现Eloquent Attribute States属性状态,结合有限状态机的设计思路,可以让状态变更的规则更明确,代码更易维护。

状态与流转规则定义
首先需要明确模型支持的所有状态,以及每个状态允许流转到的下一个状态。以订单模型为例,我们先定义订单的所有可能状态,以及状态之间的合法流转路径。
<?php
namespace AppModels;
use IlluminateDatabaseEloquentModel;
class Order extends Model
{
// 定义所有可用状态
const STATUS_PENDING = 'pending';
const STATUS_PAID = 'paid';
const STATUS_SHIPPED = 'shipped';
const STATUS_COMPLETED = 'completed';
const STATUS_CANCELED = 'canceled';
// 状态流转规则:键为当前状态,值为允许流转到的下一个状态数组
protected $stateTransitions = [
self::STATUS_PENDING => [self::STATUS_PAID, self::STATUS_CANCELED],
self::STATUS_PAID => [self::STATUS_SHIPPED, self::STATUS_CANCELED],
self::STATUS_SHIPPED => [self::STATUS_COMPLETED],
self::STATUS_COMPLETED => [],
self::STATUS_CANCELED => [],
];
// 状态对应的中文描述
public static $statusLabels = [
self::STATUS_PENDING => '待支付',
self::STATUS_PAID => '已支付',
self::STATUS_SHIPPED => '已发货',
self::STATUS_COMPLETED => '已完成',
self::STATUS_CANCELED => '已取消',
];
protected $fillable = ['status', 'order_no', 'amount'];
}
状态流转方法实现
接下来在模型中实现状态变更的核心方法,包含流转合法性校验、状态更新以及变更后的回调触发逻辑。
<?php
namespace AppModels;
use IlluminateDatabaseEloquentModel;
use InvalidArgumentException;
class Order extends Model
{
// 上面的常量和属性定义省略...
/**
* 变更订单状态
* @param string $targetStatus 目标状态
* @return bool
* @throws InvalidArgumentException
*/
public function changeStatus(string $targetStatus): bool
{
$currentStatus = $this->status;
// 校验目标状态是否合法
if (!in_array($targetStatus, $this->stateTransitions[$currentStatus] ?? [])) {
throw new InvalidArgumentException(
"当前状态{$currentStatus}不允许流转到{$targetStatus}"
);
}
// 执行状态更新
$this->status = $targetStatus;
$result = $this->save();
// 触发状态变更后的回调
if ($result) {
$this->fireStatusChangedCallback($currentStatus, $targetStatus);
}
return $result;
}
/**
* 触发状态变更回调
* @param string $fromStatus 原状态
* @param string $toStatus 新状态
*/
protected function fireStatusChangedCallback(string $fromStatus, string $toStatus): void
{
$methodName = "onStatusTo" . ucfirst($toStatus);
if (method_exists($this, $methodName)) {
$this->$methodName($fromStatus);
}
}
/**
* 状态流转到已支付的回调
* @param string $fromStatus 原状态
*/
protected function onStatusToPaid(string $fromStatus): void
{
// 这里可以写支付成功后的逻辑,比如通知用户、记录支付日志等
// 示例中仅做日志记录
Log::info("订单{$this->order_no}从{$fromStatus}变更为已支付");
}
/**
* 状态流转到已发货的回调
* @param string $fromStatus 原状态
*/
protected function onStatusToShipped(string $fromStatus): void
{
Log::info("订单{$this->order_no}从{$fromStatus}变更为已发货,准备发送物流通知");
}
}
业务层调用示例
在控制器或者其他业务服务中,可以直接调用模型的状态变更方法,处理具体的业务流程。
<?php
namespace AppHttpControllers;
use AppModelsOrder;
use IlluminateHttpRequest;
class OrderController extends Controller
{
/**
* 处理订单支付成功逻辑
* @param Request $request
* @return IlluminateHttpJsonResponse
*/
public function paySuccess(Request $request)
{
$orderId = $request->input('order_id');
$order = Order::findOrFail($orderId);
try {
$order->changeStatus(Order::STATUS_PAID);
return response()->json(['code' => 0, 'msg' => '状态更新成功']);
} catch (InvalidArgumentException $e) {
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
}
}
/**
* 处理订单发货逻辑
* @param Request $request
* @return IlluminateHttpJsonResponse
*/
public function shipOrder(Request $request)
{
$orderId = $request->input('order_id');
$order = Order::findOrFail($orderId);
try {
$order->changeStatus(Order::STATUS_SHIPPED);
return response()->json(['code' => 0, 'msg' => '发货状态更新成功']);
} catch (InvalidArgumentException $e) {
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
}
}
}
扩展优化建议
上述实现是基础的状态机逻辑,实际项目中可以根据需求做进一步扩展:
- 可以把状态流转规则配置到数据库或者配置文件中,支持动态修改流转规则,不需要修改代码
- 增加状态变更的事件监听,通过Laravel的事件系统解耦状态变更后的业务逻辑,避免回调方法过于臃肿
- 添加状态变更的历史记录表,每次状态变更都记录操作人、操作时间、变更前后的状态,方便后续排查问题
- 对状态值做枚举类封装,避免使用字符串硬编码,减少状态值写错的概率
通过这种实现方式,Eloquent模型的属性状态管理会变得清晰可控,所有状态流转规则都集中在模型中定义,后续维护或者新增状态的时候只需要修改对应的配置和回调即可,不需要在业务代码中到处写状态判断逻辑。
PHPEloquent_Attribute_StatesLaravel有限状态机修改时间:2026-07-22 20:33:31