Laravel 事件系统(一)、小试牛刀
回顾之前写完的项目,突然发现laravel里面各种奇技淫巧,自己并没有使用到很多,甚至怀疑自己到底有没有发挥laravel的真正“优雅”之处,于是结合文档,重新review了下代码,发现正如标题所说,确实有些地方可以重构,并且可以使用到laravel的优雅之道。
首先贴一段我自己之前的代码;
protected function editStatus($id, $status)
{
$route = Route::find($id);
$this->beginTransaction();
try {
if ($route) {
if ($status == 9) {
if ($route->status < 4 && $route->start_date >= date('Ymd', strtotime("-1 days"))) {
$route->cancel_date = date('Y-m-d H:i:s');
} else {
return array('status' => false, 'case' => 1);
}
}
if ($status == 5) {
if ($route->end_date > date('Ymd')) {
return array('status' => false, 'case' => 2);
}
if ($route->end_date > date('Ymd')) {
return array('status' => false, 'case' => 2);
}
$order = $this->select("select distinct(oa.order_id) from snb_order_activity oa join snb_order o on oa.order_id=o.id where oa.route_id={$route->id} and oa.activity_id={$route->activity_id} and o.pay_status in(1,3)");
$activity = DB::table('activity')->where('id', $route->activity_id)->first();
if ($order) {
$insertUsageArray = [];
$insertOutdoorsArray = [];
$settleAmount = 0;
foreach ($order as $value) {
$orderInfo = $this->findOne("select id,order_num,trade_num,total_amount,create_by,return_rate,pay_status,discount_amount from snb_order where id={$value['order_id']}");
$point = $activity->point;
$name = $activity->activity_name;
$levelPoint = $this->findOne("select integration_level_total from snb_app_user where id={$orderInfo['create_by']}");
$countPoint = $levelPoint['integration_level_total'] + $point;
$guestCount = DB::table('order_activity')->where('order_id', $orderInfo['id'])->count();
if ($orderInfo['pay_status'] == 3) {
$settleAmount += $route->org_price * $guestCount * (1 - $orderInfo['return_rate'] * 0.01);
} else {
$settleAmount += $route->org_price * $guestCount - $orderInfo['discount_amount'];
}
if ($countPoint) {
$levelName = LevelConfig::getLevelByIntegral($countPoint);
DB::table('app_user')->where('id', $orderInfo['create_by'])->update(
array(
'integration_level_total' => $countPoint,
'level_name' => $levelName
));
$insertUsageArray[] = array(
'app_user_id' => $orderInfo['create_by'],
'order_id' => $orderInfo['id'],
'order_num' => $orderInfo['order_num'],
'trade_num' => $orderInfo['trade_num'],
'integral_usage_amount' => $point,
'integral_type' => 1,
'remark' => '完成活动奖励积分',
'create_by' => $this->authHelpers()->getUserId(),
'create_date' => date('Y-m-d H:i:s'),
'update_by' => $this->authHelpers()->getUserId(),
'update_date' => date('Y-m-d H:i:s')
);
}
$insertOutdoorsArray[] = array(
'user_id' => $orderInfo['create_by'],
'experience_cnt' => '完成活动' . $name,
'departure_date' => $route->start_date,
'create_by' => $this->authHelpers()->getUserId(),
'create_date' => date('Y-m-d H:i:s'),
);
}
$insertSettleArray = array(
'company_id' => $activity->company_id,
'route_id' => $route->id,
'settle_amount' => $settleAmount,
'settle_status' => 1,//待审核
);
DB::table('integral_usage')->insert($insertUsageArray);
DB::table('outdoor_experience')->insert($insertOutdoorsArray);
$result = Settle::saveRecord('', $insertSettleArray);
if (!$result) {
$this->rollback();
return array('status' => false);
}
DB::table('order')->whereIn('id', array_column($order, 'id'))->update(array('status' => 3));
}
}
$route->status = $status;
$route->save();
}
} catch (\Exception $e) {
var_dump($e->getMessage());
$this->rollback();
return array('status' => false);
}
$this->commit();
return array('status' => true);
}
可以看到这个方法中有一段逻辑非常的麻烦,而且只是其中的情况下对应的逻辑,这样的代码非常混乱,修改起来非常的麻烦,而且不符合laravel的优雅之说,利用php代码分析工具,可以看到此处
提示复杂度过高,因此这段代码有必要重构,这里就利用到了laravel的事件系统;
首先在项目下php artisan event:generate
生成相关事件的目录,这时可以看到在项目下生成了Events和Listenesr两个目录;
首先我们在Events目录下创建一个事件,名字叫做RouteEnd:
<?php
namespace App\Events;
use App\Models\Route;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\SerializesModels;
class RouteEnd
{
use Dispatchable, InteractsWithSockets,SerializesModels;
public $route;
public function __construct(Route $route)
{
$this->route = $route;
}
}
在初始化方法中,参数中接收了一个Eloquent 模型;并且设置该成员变量为接收的这个值;主要是为了传递这个变量到监听器中,接下来编写监听器,名字为RouteEndListener;
class RouteEndListener
{
public function handle(RouteEnd $event){
$route = $event->route;
$order = DB::select("select distinct(oa.order_id) from snb_order_activity oa join snb_order o on oa.order_id=o.id where oa.route_id={$route->id} and oa.activity_id={$route->activity_id} and o.pay_status in(1,3)");
$activity = DB::table('activity')->where('id', $route->activity_id)->first();
if ($order) {
$insertUsageArray = [];
$insertOutdoorsArray = [];
$settleAmount = 0;
foreach ($order as $value) {
$orderInfo = $this->findOne("select id,order_num,trade_num,total_amount,create_by,return_rate,pay_status,discount_amount from snb_order where id={$value['order_id']}");
$point = $activity->point;
$name = $activity->activity_name;
$levelPoint = $this->findOne("select integration_level_total from snb_app_user where id={$orderInfo['create_by']}");
$countPoint = $levelPoint['integration_level_total'] + $point;
$guestCount = DB::table('order_activity')->where('order_id', $orderInfo['id'])->count();
if ($orderInfo['pay_status'] == 3) {
$settleAmount += $route->org_price * $guestCount * (1 - $orderInfo['return_rate'] * 0.01);
} else {
$settleAmount += $route->org_price * $guestCount - $orderInfo['discount_amount'];
}
if ($countPoint) {
$levelName = LevelConfig::getLevelByIntegral($countPoint);
DB::table('app_user')->where('id', $orderInfo['create_by'])->update(
array(
'integration_level_total' => $countPoint,
'level_name' => $levelName
));
$insertUsageArray[] = array(
'app_user_id' => $orderInfo['create_by'],
'order_id' => $orderInfo['id'],
'order_num' => $orderInfo['order_num'],
'trade_num' => $orderInfo['trade_num'],
'integral_usage_amount' => $point,
'integral_type' => 1,
'remark' => '完成活动奖励积分',
'create_by' => $this->authHelpers()->getUserId(),
'create_date' => date('Y-m-d H:i:s'),
'update_by' => $this->authHelpers()->getUserId(),
'update_date' => date('Y-m-d H:i:s')
);
}
$insertOutdoorsArray[] = array(
'user_id' => $orderInfo['create_by'],
'experience_cnt' => '完成活动' . $name,
'departure_date' => $route->start_date,
'create_by' => $this->authHelpers()->getUserId(),
'create_date' => date('Y-m-d H:i:s'),
);
}
$insertSettleArray = array(
'company_id' => $activity->company_id,
'route_id' => $route->id,
'settle_amount' => $settleAmount,
'settle_status' => 1,//待审核
);
DB::table('integral_usage')->insert($insertUsageArray);
DB::table('outdoor_experience')->insert($insertOutdoorsArray);
$result = Settle::saveRecord('', $insertSettleArray);
if (!$result) {
throw new \Exception('保存失败',2000);
}
DB::table('order')->whereIn('id', array_column($order, 'id'))->update(array('status' => 3));
}
}
}
可以看到这个类主要实现了handle方法,而我把之前非常混乱的一段代码移到了这里,从而之前的代码,变成
protected function editStatus($id, $status)
{
$route = Route::find($id);
$this->beginTransaction();
try {
if ($route) {
if ($status == 9) {
if ($route->status < 4 && $route->start_date >= date('Ymd', strtotime("-1 days"))) {
$route->cancel_date = date('Y-m-d H:i:s');
} else {
return array('status' => false, 'case' => 1);
}
}
if ($status == 5) {
if ($route->end_date > date('Ymd')) {
return array('status' => false, 'case' => 2);
}
event(new RouteEnd($route));
}
$route->status = $status;
$route->save();
}
} catch (\Exception $e) {
var_dump($e->getMessage());
$this->rollback();
return array('status' => false);
}
$this->commit();
return array('status' => true);
}
可以看到代码缩减了一段,而且这段代码也实现了它的唯一职权,修改状态,而不用去做其他相关逻辑的事情,符合单一职责的设计模式,代码中 event(new RouteEnd($route));实现的功能是分发事件,简单一点来讲就是调用相应的处理方法,另外还有一点,要把事件对应的监听器写到EventServiceProvider下的$listen变量中去,这样才能生效,否则会报错,格式如下
protected $listen = [
'App\Events\RouteEnd'=>[
'App\Listeners\RouteEndListener'
]
];
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: