若将商业逻辑都写在controller,会造成controller肥大而难以维护,基于SOLID原则,我们应该使用Service模式辅助controller,将相关的商业逻辑封装在不同的service,方便中大型项目的维护。

Version

Laravel 5.1.22

商业逻辑

商业逻辑中,常见的如:

  1. 牵涉到外部行为:如发送Email,使用外部API…。
  2. 使用PHP写的逻辑:如根据购买的件数,有不同的折扣。
    若将商业逻辑写在controller,会造成controller肥大,日后难以维护。

Service

牵涉到外部行为
如发送Email,初学者常会在controller直接调用Mail::queue():

public function store(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'));});
}
复制代码

在中大型项目,会有几个问题:

  1. 将牵涉到外部行为的商业逻辑写在controller,造成controller的肥大难以维护。
  2. 违反SOLID的单一职责原则:外部行为不应该写在controller。
  3. controller直接相依于外部行为,使得我们无法对controller做单元测试。

比较好的方式是使用service:

  1. 将外部行为注入到service。
  2. 在service使用外部行为。
  3. 将service注入到controller。

EmailService.php

app/Services/EmailService.php
namespace App\Services;
use Illuminate\Mail\Mailer;
use Illuminate\Mail\Message;
class EmailService
{/** @var Mailer */private $mail;/*** EmailService constructor.* @param Mailer $mail*/public function __construct(Mailer $mail){$this->mail = $mail;}/*** 發送Email* @param array $request*/public function send(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 行

/** @var Mailer */
private $mail;
/*** EmailService constructor.* @param Mailer $mail*/
public function __construct(Mailer $mail)
{$this->mail = $mail;
}
复制代码

将相依的Mailer注入到EmailService。
20 行

/*** 發送Email* @param array $request*/
public function send(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'));});
}
复制代码

将发送Emai的商业逻辑写在send()。
不是使用Mail facade,而是使用注入的$this->mail
UserController.php

app/Http/Controllers/UserController.php
namespace App\Http\Controllers;
use App\Http\Requests;
use Illuminate\Http\Request;
use MyBlog\Services\EmailService;
class UserController extends Controller
{/** @var EmailService */protected $emailService;/*** UserController constructor.* @param EmailService $emailService*/public function __construct(EmailService $emailService){$this->emailService = $emailService;}/*** Store a newly created resource in storage.* @param  \Illuminate\Http\Request  $request* @return \Illuminate\Http\Response*/public function store(Request $request){$this->emailService->send($request->all());}
}
复制代码

第9行

/** @var  EmailService */
protected $emailService;
/*** UserController constructor.* @param EmailService $emailService*/
public function __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*/
public function store(Request $request)
{$this->emailService->send($request->all());
}
复制代码

从原本直接相依于Mail facade,改成相依于注入的EmailService
改用这种写法,有几个优点

  1. 将外部行为写在service,解决controller肥大问题。
  2. 符合SOLID的单一职责原则:外部行为写在service,没写在controller。
  3. 符合SOLID的依赖反转原则:controller并非直接相依于service,而是将service依赖注入进controller。

    使用PHP写的逻辑

    如根据购买的件数,有不同的折扣,初学者常会在controller直接写if…else逻辑。

    public function store(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);
    }
    复制代码

在中大型项目,会有几个问题:

  1. 将PHP写的商业逻辑直接写在controller,造成controller的肥大难以维护。
  2. 违反SOLID的单一职责原则:商业逻辑不应该写在controller。
  3. 违反SOLID的单一职责原则:若未来想要改变折扣与加总的算法,都需要改到此method,也就是说,此method同时包含了计算折扣与计算加总的职责,因此违反SOLID的单一职责原则。
  4. 直接写在controller的逻辑无法被其他controller使用。

比较好的方式是使用service。

  1. 将相依物件注入到service。
  2. 在service写PHP逻辑使用相依物件。
  3. 将service注入到controller。

OrderService.php

app/Services/OrderService.php
namespace App\Services;
class OrderService
{/*** 計算折扣* @param int $qty* @return float*/public function getDiscount($qty){if ($qty == 1) {return 1.0;} elseif ($qty == 2) {return 0.9;} elseif ($qty == 3) {return 0.8;} else {return 0.7;}}/*** 計算最後價錢* @param integer $qty* @param float $discount* @return float*/public function getTotal($qty, $discount){return 500 * $qty * $discount;}
}
复制代码

第 5 行

/*** 計算折扣* @param int $qty* @return float*/
public function getDiscount($qty)
{if ($qty == 1) {return 1.0;} elseif ($qty == 2) {return 0.9;} elseif ($qty == 3) {return 0.8;} else {return 0.7;}
}
复制代码

为了符合SOLID的单一职责原则,将计算折扣独立成getDiscount(),将PHP写的判断逻辑写在里面。
23行

/*** 計算最後價錢* @param int $qty* @param float $discount* @return float*/
public function getTotal($qty, $discount)
{return 500 * $qty * $discount;
}
复制代码

为了符合SOLID的单一职责原则,将计算加总独立成getTotal(),将PHP写的计算逻辑写在里面。
OrderController.php

app/Http/Controllers/OrderController.php
namespace App\Http\Controllers;
use App\Http\Requests;
use App\MyBlog\Services\OrderService;
use Illuminate\Http\Request;class OrderController extends Controller
{/** @var OrderService */protected $orderService;/*** OrderController constructor.* @param OrderService $orderService*/public function __construct(OrderService $orderService){$this->orderService = $orderService;}/*** Store a newly created resource in storage.* @param  \Illuminate\Http\Request  $request* @return \Illuminate\Http\Response*/public function store(Request $request){$qty = $request->input('qty');$discount = $this->orderService->getDiscount($qty);$total = $this->orderService->getTotal($qty, $discount);echo($total);}
}
复制代码

第 9 行

/** @var OrderService */
protected $orderService;/*** OrderController constructor.* @param OrderService $orderService*/
public function __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*/
public function store(Request $request)
{$qty = $request->input('qty');$discount = $this->orderService->getDiscount($qty);$total = $this->orderService->getTotal($qty, $discount);echo($total);
}
复制代码

将原本的if…else逻辑改成呼叫OrderService,controller变得非常干净,也达成原本controller接收HTTP request,调用其他class的责任。
改用这种写法,有几个优点:

  1. 将PHP写的商业逻辑写在service,解决controller肥大问题。
  2. 符合SOLID的单一职责原则:商业逻辑写在service,没写在controller。
  3. 符合SOLID的单一职责原则:计算折扣与计算加总分开在不同method,且归属于OrderService,而非OrderController。
  4. 符合SOLID的依赖反转原则:controller并非直接相依于service,而是将service依赖注入进controller。
  5. 其他controller也可以重复使用此段商业逻辑。

Controller
牵涉到外部行为

public function store(Request $request)
{$this->emailService->send($request->all());
}
复制代码

使用PHP写的逻辑

public function store(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,调用其他class的职责了。

Conclusion
实务上会有很多service,须自行依照SOLID原则去判断是否该建立service。
Service使得商业逻辑从controller中解放,不仅更容易维护、更容易扩展、更容易重复使用,且更容易测试。

在这里推荐个大数据学习交流群:806025609,群里都是学大 数据开发的,如果你正在学习大数据 ,欢迎你加入,群内还 会不定期分享干货(只有大数据软件开发相关的),进群即 可获取一份免费大数据学习资料~

Laravel框架中如何使用Service模式?相关推荐

  1. Laravel框架中使用Service模式

    Laravel框架中使用 Presenter 模式 Laravel框架中使用 Repository 模式 Laravel的中大型项目构架和优雅的插件扩展l5-repository 若将商业逻辑都写在c ...

  2. laravel 框架中使用数据库迁移添加注释

    laravel 框架中数据库迁移添加注释 在使用laravel框架过程中,估计很多人都有用过数据库迁移文件.可能大家都会在建表时为字段添加注释.我在此要说明的是为表添加注释 首先我们需要引入larav ...

  3. php辅助框架,【PHP开发框架】Laravel框架中辅助函数:optional ()函数的介绍

    laravel框架中的辅助函数有很多,那么,在 Laravel 新版本中又有什么非常好用的辅助函数呢?接下来的这篇文章中,ki4网将给大家介绍一个非常有用的辅助方法:optional()函数,这个函数 ...

  4. 阿里物联网套件在laravel框架中的使用--第一弹

    最近一直在研究物联网套件,也算是有点心得.然后研究归研究,终归是要回归实践的.在网上大致百度下,发现专门写阿里物联网套件的文章很少,所以就大致总结下,大致说一下物联网提供的phpSDK在laravel ...

  5. Laravel框架中config配置文件的使用

    在进行程序开发时,为了后期维护的方便,我们习惯上将配置信息单独写在一个配置文件中.在laravel框架中为我们提供了config目录专门用来存放配置文件.如果我们需要在config目录中添加自定义配置 ...

  6. laravel services.php,「Laravel框架中使用Service模式」- 海风纷飞Blog

    若将商业逻辑都写在controller,会造成controller肥大而难以维护,基于SOLID原则,我们应该使用Service模式辅助controller,将相关的商业逻辑封装在不同的service ...

  7. Laravel框架中Guard的底层实现分析

    1. 什么是Guard 在Laravel/Lumen框架中,用户的登录/注册的认证基本都已经封装好了,开箱即用.而登录/注册认证的核心就是: 用户的注册信息存入数据库(登记) 从数据库中读取数据和用户 ...

  8. 关于laravel框架中and 和orWhere 的多条件嵌套

    最近的项目一直在使用laravel框架,在使用过程中突然发现自带的助手函数where()与orWhere()使用起来和自己预想中的不一样,特此记录学习防止以后忘记. 举个例子我们需要查找状态为1,名称 ...

  9. CI框架中的开启调试模式

    1,CI框架中开启调试模式: 在需要调试的方法名前面加上:     $this -> output -> enable_profiler(TRUE); 这样就可以看大打印出的s q l语句 ...

最新文章

  1. 科研文献|了解多个含水层中微生物砷的迁移:DNA 和 RNA 分析的见解
  2. HashMap HashTable HashSet区别剖析
  3. android中播放gif动画之二
  4. Git undo 操作
  5. js中的可变参数arguments与json
  6. Qt创建Android服务
  7. 网络编程(part11)--socket模块方法及socket套接字属性
  8. robots.txt文件详解
  9. MySQL buffer pool里的三种链表和三种page
  10. 【pytorch】torch.linspace==>返回一个一维的tensor(张量),这个张量包含了从start到end,分成steps个线段得到的向量
  11. 目标识别(object detection)中的 IoU(Intersection over Union)
  12. win10 安装oracle 11gR2_database出现universal Installer后闪退就没反应的解决方案
  13. java的数组排序和去重
  14. 十大高人气商城高口碑蓝牙耳机排行榜,颜值在线性能无敌
  15. 【MySQL】增大字符串长度不会锁表吗
  16. TSE无线通信(铺垫)
  17. Delphi中BeginUpdate和EndUpdate作用
  18. Android水平仪实训报告,水准仪测量实训报告
  19. 微信支付的软件架构究竟有多牛逼...
  20. iphone桌面横屏设置在哪里_苹果手机怎么设置横屏切换

热门文章

  1. sklearn预测pima糖尿病
  2. DDPush开源推送框架源码分析之APPServer到DDPush
  3. CiteSpace介绍与使用
  4. 《一》ODOO15搭建完整的财务核算体系(制造成本全核算)
  5. java计算机毕业设计vue开发一个简单音乐播放器MyBatis+系统+LW文档+源码+调试部署
  6. 电子科大电气工程导师介绍绍_电子科技大学导师介绍
  7. 一文图解单目相机标定算法
  8. 12 -- OpenCV学习—边缘检测
  9. 【Linux】raid管理工具-mdadm-raid0管理
  10. saas应用与传统应用开发_如何从SaaS应用程序查询和提取数据