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代码分析工具,可以看到此处file
提示复杂度过高,因此这段代码有必要重构,这里就利用到了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 协议》,转载必须注明作者和本文链接
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!