Laravel框架中使用Service模式

若將商業(yè)邏輯都寫在controller谜嫉,會造成controller肥大而難以維護(hù),基于SOLID原則,我們應(yīng)該使用Service模式輔助controller,將相關(guān)的商業(yè)邏輯封裝在不同的service采桃,方便中大型項(xiàng)目的維護(hù)。

Version

Laravel 5.1.22

商業(yè)邏輯

商業(yè)邏輯中丘损,常見的如:

牽涉到外部行為:如發(fā)送Email普办,使用外部API…。

使用PHP寫的邏輯:如根據(jù)購買的件數(shù)徘钥,有不同的折扣衔蹲。

若將商業(yè)邏輯寫在controller,會造成controller肥大呈础,日后難以維護(hù)舆驶。

Service

牽涉到外部行為

如發(fā)送Email,初學(xué)者常會在controller直接調(diào)用Mail::queue():

publicfunctionstore(Request $request)

{

Mail::queue('email.index', $request->all(),function(Message $message){

$message->sender(env('MAIL_USERNAME'));

$message->subject(env('MAIL_SUBJECT'));

$message->to(env('MAIL_TO_ADDR'));

? ? });

}

在中大型項(xiàng)目猪落,會有幾個問題:

將牽涉到外部行為的商業(yè)邏輯寫在controller贞远,造成controller的肥大難以維護(hù)畴博。

違反SOLID的單一職責(zé)原則:外部行為不應(yīng)該寫在controller笨忌。

controller直接相依于外部行為,使得我們無法對controller做單元測試俱病。

比較好的方式是使用service:

將外部行為注入到service官疲。

在service使用外部行為袱结。

將service注入到controller。

EmailService.php

app/Services/EmailService.php

namespaceApp\Services;

useIlluminate\Mail\Mailer;

useIlluminate\Mail\Message;

classEmailService

{

/**@varMailer */

private$mail;

/**

? ? * EmailService constructor.

*@paramMailer $mail

? ? */

publicfunction__construct(Mailer $mail)

? ? {

$this->mail = $mail;

? ? }

/**

? ? * 發(fā)送Email

*@paramarray $request

? ? */

publicfunctionsend(array $request)

? ? {

$this->mail->queue('email.index', $request,function(Message $message){

$message->sender(env('MAIL_USERNAME'));

$message->subject(env('MAIL_SUBJECT'));

$message->to(env('MAIL_TO_ADDR'));

? ? ? ? });

? ? }

}

第 8 行

/**@varMailer */

private$mail;

/**

* EmailService constructor.

*@paramMailer $mail

*/

publicfunction__construct(Mailer $mail)

{

$this->mail = $mail;

}

將相依的Mailer注入到EmailService途凫。

20 行

/**

* 發(fā)送Email

*@paramarray $request

*/

publicfunctionsend(array $request)

{

$this->mail->queue('email.index', $request,function(Message $message){

$message->sender(env('MAIL_USERNAME'));

$message->subject(env('MAIL_SUBJECT'));

$message->to(env('MAIL_TO_ADDR'));

? ? });

}

將發(fā)送Emai的商業(yè)邏輯寫在send()垢夹。

不是使用Mail facade,而是使用注入的$this->mail

UserController.php

app/Http/Controllers/UserController.php

namespaceApp\Http\Controllers;

useApp\Http\Requests;

useIlluminate\Http\Request;

useMyBlog\Services\EmailService;

classUserControllerextendsController

{

/**@varEmailService */

protected$emailService;

/**

? ? * UserController constructor.

*@paramEmailService $emailService

? ? */

publicfunction__construct(EmailService $emailService)

? ? {

$this->emailService = $emailService;

? ? }

/**

? ? * Store a newly created resource in storage.

*@param\Illuminate\Http\Request? $request

*@return\Illuminate\Http\Response

? ? */

publicfunctionstore(Request $request)

? ? {

$this->emailService->send($request->all());

? ? }

}

第9行

/**@varEmailService */

protected$emailService;

/**

* UserController constructor.

*@paramEmailService $emailService

*/

publicfunction__construct(EmailService $emailService)

{

$this->emailService = $emailService;

}

將相依的EmailService注入到UserController维费。

22行

/**

* Store a newly created resource in storage.

*@param\Illuminate\Http\Request? $request

*@return\Illuminate\Http\Response

*/

publicfunctionstore(Request $request)

{

$this->emailService->send($request->all());

}

從原本直接相依于Mail facade果元,改成相依于注入的EmailService

改用這種寫法,有幾個優(yōu)點(diǎn)

將外部行為寫在service犀盟,解決controller肥大問題而晒。

符合SOLID的單一職責(zé)原則:外部行為寫在service,沒寫在controller阅畴。

符合SOLID的依賴反轉(zhuǎn)原則:controller并非直接相依于service倡怎,而是將service依賴注入進(jìn)controller。

使用PHP寫的邏輯

如根據(jù)購買的件數(shù)贱枣,有不同的折扣监署,初學(xué)者常會在controller直接寫if…else邏輯。

publicfunctionstore(Request $request)

{

$qty = $request->input('qty');

$price =500;

if($qty ==1) {

$discount =1.0;

}

elseif($qty ==2) {

$discount =0.9;

}

elseif($qty ==3) {

$discount =0.8;

}

else{

$discount =0.7;

}

$total = $price * $qty * $discount;

echo($total);

}

在中大型項(xiàng)目纽哥,會有幾個問題:

將PHP寫的商業(yè)邏輯直接寫在controller钠乏,造成controller的肥大難以維護(hù)。

違反SOLID的單一職責(zé)原則:商業(yè)邏輯不應(yīng)該寫在controller春塌。

違反SOLID的單一職責(zé)原則:若未來想要改變折扣與加總的算法缓熟,都需要改到此method,也就是說摔笤,此method同時包含了計(jì)算折扣與計(jì)算加總的職責(zé)够滑,因此違反SOLID的單一職責(zé)原則。

直接寫在controller的邏輯無法被其他controller使用吕世。

比較好的方式是使用service彰触。

將相依物件注入到service。

在service寫PHP邏輯使用相依物件命辖。

將service注入到controller况毅。

OrderService.php

app/Services/OrderService.php

namespaceApp\Services;

classOrderService

{

/**

? ? * 計(jì)算折扣

*@paramint $qty

*@returnfloat

? ? */

publicfunctiongetDiscount($qty)

? ? {

if($qty ==1) {

return1.0;

}elseif($qty ==2) {

return0.9;

}elseif($qty ==3) {

return0.8;

}else{

return0.7;

? ? ? ? }

? ? }

/**

? ? * 計(jì)算最後價錢

*@paraminteger $qty

*@paramfloat $discount

*@returnfloat

? ? */

publicfunctiongetTotal($qty, $discount)

? ? {

return500* $qty * $discount;

? ? }

}

第 5 行

/**

* 計(jì)算折扣

*@paramint $qty

*@returnfloat

*/

publicfunctiongetDiscount($qty)

{

if($qty ==1) {

return1.0;

}elseif($qty ==2) {

return0.9;

}elseif($qty ==3) {

return0.8;

}else{

return0.7;

? ? }

}

為了符合SOLID的單一職責(zé)原則,將計(jì)算折扣獨(dú)立成getDiscount()尔艇,將PHP寫的判斷邏輯寫在里面尔许。

23行

/**

* 計(jì)算最後價錢

*@paramint $qty

*@paramfloat $discount

*@returnfloat

*/

publicfunctiongetTotal($qty, $discount)

{

return500* $qty * $discount;

}

為了符合SOLID的單一職責(zé)原則,將計(jì)算加總獨(dú)立成getTotal()终娃,將PHP寫的計(jì)算邏輯寫在里面味廊。

OrderController.php

app/Http/Controllers/OrderController.php

namespaceApp\Http\Controllers;

useApp\Http\Requests;

useApp\MyBlog\Services\OrderService;

useIlluminate\Http\Request;

classOrderControllerextendsController

{

/**@varOrderService */

protected$orderService;

/**

? ? * OrderController constructor.

*@paramOrderService $orderService

? ? */

publicfunction__construct(OrderService $orderService)

? ? {

$this->orderService = $orderService;

? ? }

/**

? ? * Store a newly created resource in storage.

*@param\Illuminate\Http\Request? $request

*@return\Illuminate\Http\Response

? ? */

publicfunctionstore(Request $request)

? ? {

$qty = $request->input('qty');

$discount =$this->orderService->getDiscount($qty);

$total =$this->orderService->getTotal($qty, $discount);

echo($total);

? ? }

}

第 9 行

/**@varOrderService */

protected$orderService;

/**

* OrderController constructor.

*@paramOrderService $orderService

*/

publicfunction__construct(OrderService $orderService)

{

$this->orderService = $orderService;

}

將相依的OrderService注入到UserController。

21行

/**

* Store a newly created resource in storage.

*@param\Illuminate\Http\Request? $request

*@return\Illuminate\Http\Response

*/

publicfunctionstore(Request $request)

{

$qty = $request->input('qty');

$discount =$this->orderService->getDiscount($qty);

$total =$this->orderService->getTotal($qty, $discount);

echo($total);

}

將原本的if…else邏輯改成呼叫OrderService,controller變得非常干凈余佛,也達(dá)成原本controller接收HTTP request柠新,調(diào)用其他class的責(zé)任。

改用這種寫法辉巡,有幾個優(yōu)點(diǎn):

將PHP寫的商業(yè)邏輯寫在service恨憎,解決controller肥大問題。

符合SOLID的單一職責(zé)原則:商業(yè)邏輯寫在service郊楣,沒寫在controller憔恳。

符合SOLID的單一職責(zé)原則:計(jì)算折扣與計(jì)算加總分開在不同method,且歸屬于OrderService净蚤,而非OrderController喇嘱。

符合SOLID的依賴反轉(zhuǎn)原則:controller并非直接相依于service,而是將service依賴注入進(jìn)controller塞栅。

其他controller也可以重復(fù)使用此段商業(yè)邏輯者铜。

Controller

牽涉到外部行為

publicfunctionstore(Request $request)

{

$this->emailService->send($request->all());

}

使用PHP寫的邏輯

publicfunctionstore(Request $request)

{

$qty = $request->input('qty');

$discount =$this->orderService->getDiscount($qty);

$total =$this->orderService->getTotal($qty, $discount);

echo($total);

}

若使用了service輔助controller,再搭配依賴注入與service container放椰,則controller就非常干凈作烟,能專心處理接收HTTP request,調(diào)用其他class的職責(zé)了砾医。

Conclusion

實(shí)務(wù)上會有很多service拿撩,須自行依照SOLID原則去判斷是否該建立service。

Service使得商業(yè)邏輯從controller中解放如蚜,不僅更容易維護(hù)压恒、更容易擴(kuò)展、更容易重復(fù)使用错邦,且更容易測試探赫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市撬呢,隨后出現(xiàn)的幾起案子伦吠,更是在濱河造成了極大的恐慌,老刑警劉巖魂拦,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毛仪,死亡現(xiàn)場離奇詭異,居然都是意外死亡芯勘,警方通過查閱死者的電腦和手機(jī)箱靴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荷愕,“玉大人衡怀,你說我怎么就攤上這事棍矛。” “怎么了狈癞?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵茄靠,是天一觀的道長茂契。 經(jīng)常有香客問我蝶桶,道長,這世上最難降的妖魔是什么掉冶? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任真竖,我火速辦了婚禮,結(jié)果婚禮上厌小,老公的妹妹穿的比我還像新娘恢共。我一直安慰自己,他們只是感情好璧亚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布讨韭。 她就那樣靜靜地躺著,像睡著了一般癣蟋。 火紅的嫁衣襯著肌膚如雪透硝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天疯搅,我揣著相機(jī)與錄音濒生,去河邊找鬼。 笑死幔欧,一個胖子當(dāng)著我的面吹牛罪治,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播礁蔗,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼觉义,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了浴井?” 一聲冷哼從身側(cè)響起谁撼,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滋饲,沒想到半個月后厉碟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡屠缭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年箍鼓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呵曹。...
    茶點(diǎn)故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡款咖,死狀恐怖何暮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铐殃,我是刑警寧澤海洼,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站富腊,受9級特大地震影響坏逢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赘被,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一是整、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧民假,春花似錦浮入、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至野舶,卻和暖如春易迹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背筒愚。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工赴蝇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人巢掺。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓句伶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親陆淀。 傳聞我的和親對象是個殘疾皇子考余,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容