回顧之前寫完的專案,突然發現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 協議》,轉載必須註明作者和本文連結