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

Version

Laravel 5.1.22

商业逻辑

商业逻辑中,常见的如:牵涉到外部行为:如发送Email,使用外部API…。

使用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'));});}

在中大型项目,会有几个问题:将牵涉到外部行为的商业逻辑写在controller,造成controller的肥大难以维护。

违反SOLID的单一职责原则:外部行为不应该写在controller。

controller直接相依于外部行为,使得我们无法对controller做单元测试。

比较好的方式是使用service:将外部行为注入到service。

在service使用外部行为。

将service注入到controller。

EmailService.phpapp/Services/EmailService.phpnamespace 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.phpapp/Http/Controllers/UserController.phpnamespace 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

改用这种写法,有几个优点将外部行为写在service,解决controller肥大问题。

符合SOLID的单一职责原则:外部行为写在service,没写在controller。

符合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);

}

在中大型项目,会有几个问题:将PHP写的商业逻辑直接写在controller,造成controller的肥大难以维护。

违反SOLID的单一职责原则:商业逻辑不应该写在controller。

违反SOLID的单一职责原则:若未来想要改变折扣与加总的算法,都需要改到此method,也就是说,此method同时包含了计算折扣与计算加总的职责,因此违反SOLID的单一职责原则。

直接写在controller的逻辑无法被其他controller使用。

比较好的方式是使用service。将相依物件注入到service。

在service写PHP逻辑使用相依物件。

将service注入到controller。

OrderService.phpapp/Services/OrderService.phpnamespace 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.phpapp/Http/Controllers/OrderController.phpnamespace 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的责任。

改用这种写法,有几个优点:将PHP写的商业逻辑写在service,解决controller肥大问题。

符合SOLID的单一职责原则:商业逻辑写在service,没写在controller。

符合SOLID的单一职责原则:计算折扣与计算加总分开在不同method,且归属于OrderService,而非OrderController。

符合SOLID的依赖反转原则:controller并非直接相依于service,而是将service依赖注入进controller。

其他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中解放,不仅更容易维护、更容易扩展、更容易重复使用,且更容易测试。

Sample Code

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

  1. mysql支持事务的储存引擎_「mysql事务与mysql储存引擎」- 海风纷飞Blog

    事务概念及存储引擎 1.0 为何要事务? 先来看一个场景,银行转账汇款: 李彦宏和周鸿祎天天打架,现在让李彦宏给周鸿祎转款1000 元 设计如下表 account表 编号(id)用户名(user)金额 ...

  2. Laravel框架中使用Service模式

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

  3. docker项目部署 php_「Docker部署PHP+Vue项目」- 海风纷飞Blog

    创建Docker映射目录-- vue_demo         # Demo项目 -- php_vue -- docker-compose.yaml -- nginx ---- apps        ...

  4. centos7查看当前端口_「Centos7开放及查看端口」- 海风纷飞Blog

    1.开放端口 firewall-cmd --zone=public --add-port=5432/tcp --permanent  # 开放5432端口 firewall-cmd --zone=pu ...

  5. 上海时间戳 php,「PHP的时间戳与具体时间转化」- 海风纷飞Blog

    三个内置函数:time()   //获取UNIX系统时间戳 mktime(hour,minute,second,month,day,year)  //将指定时间转化为时间戳 date(时间格式,时间戳 ...

  6. 聊聊 CSS 中的布局模式

    本文来自作者 大漠 在 GitChat 上分享 「聊聊 CSS 中的布局模式」,「阅读原文」查看交流实录. 「文末高能」 编辑 | 哈比 一.聊聊 CSS 中的布局模式 在大家的印象中,CSS 非常的 ...

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

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

  8. angular中的MVVM模式

    在开始介绍angular原理之前,我们有必要先了解下mvvm模式在angular中运用.虽然在angular社区一直将angular统称为前端MVC框架,同时angular团队也称它为MVW(What ...

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

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

最新文章

  1. TypeError: string argument without an encoding
  2. java9新特性 2017_Java 9 ← 2017,2019 → Java 13 ,来看看Java两年来的变化
  3. 作业帮口算批改怎么开 作业帮口算批改如何用
  4. 127.0.0.1 zxt.php,恭喜您!序列号购买成功!
  5. Kconfig中select与depends on原理
  6. 弹出框--用css实现div在页面居中(水平垂直居中效果)
  7. 利用云服务器搭建解锁网易云变灰歌曲的代理
  8. 我去扒了杜蕾斯的微博
  9. 这份赏金任务,人人都能做,只要……
  10. android分析内存工具,Android Studio内存泄漏分析工具汇总
  11. Android 9.0 PM机制系列(四) APK安装需要空间分析
  12. WPARAM 与 LPARAM 参数的解析 [C#、WinAPI]
  13. 为何NB-LOT 覆盖比较广
  14. DSPE-PEG-MMPs; PEG-MMPs-DSPE ;聚乙二醇-基质金属蛋白酶-磷脂 ;磷脂-聚乙二醇-基质金属蛋白酶
  15. P5727 【深基5.例3】冰雹猜想
  16. 二、牵引降压变电所综合自动化系统
  17. 计算机常用的英语术语(不定时更新)
  18. aix 进程占用内存_AIX 5L上的共享库内存占用量
  19. 工行银企互联接入详解(2)--下载证书
  20. 比星链和6G还牛,中国在通信领域研究的新方向将奠定全球领先地位

热门文章

  1. 电磁场中场点和源点及▽(R)▽(1/R)▽.▽(1/R)
  2. shiro执行多个过滤器_shiro教程5(整合SSM项目-认证)
  3. 01_Flume基本架构及原理
  4. window自动备件软件
  5. 使用Masonry让cell高度自适
  6. webpack模块定义和使用的模式
  7. Laravel 5.x 启动过程分析 [转]
  8. 在你的网站中使用 AdSense广告
  9. [置顶] Android之Handler用法总结
  10. [恢]hdu 2138