Laravel框架中使用 Presenter 模式

Laravel框架中使用 Repository 模式

Laravel的中大型项目构架和优雅的插件扩展l5-repository

若将商业逻辑都写在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中解放,不仅更容易维护、更容易扩展、更容易重复使用,且更容易测试。
Sample Code
完整的示例可以在我的GitHub上找到
External API
If…else

转载自:http://www.sangeng.org/blog/detail_519.html

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

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

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

  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框架中如何使用Service模式?

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

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

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

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

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

  9. Laravel框架中数据库分表时Model使用方法

    前言: 0.最近在使用laravel框架做MySQL分表的时候经过实践和踩坑,总结了以下3种可行的分表方法,亲测可用. 1.本人公司做的是SaaS系统,以店铺为维度.店铺id(shop_id) 命名规 ...

最新文章

  1. A - Wireless Network POJ - 2236
  2. iframe中请求页面而session失效时页面跳转问题
  3. ubuntu新建python代码文件_[Vim]新建python文件自动添加python header
  4. maven和docker_与Maven和Docker的集成测试
  5. IDEA运行VUE npm install报错:chromedriver@2.27.2 install: node install.js
  6. #if 0 #elif 1 #else #endif 用法
  7. LimeSurvey问卷调查管理系统
  8. SQL Server之 (四) ADO增删查改 登录demo 带参数的sql语句 插入自动返回行号
  9. 深度学习笔记_基本概念_逆卷积的详细解释ConvTranspose2d(fractionally-strided convolutions)
  10. DevOps技术学习路线图 初阶+中阶+高阶
  11. Flash cs3教程-传统数字动画
  12. xy坐标转换为极坐标_xy坐标怎么换算(色坐标xy换算uv计算式)
  13. 12f的接线 esp8266_ESP8266 系列最小系统版接线
  14. 电脑连上了WIFI,但是却没有网(前提WIFI没有问题)
  15. win10 u盘 修复计算机,u盘启动修复Win10引导文件丢失的方法
  16. c语言rgb数值颜色渐变算法,颜色渐变算法
  17. Shell替换数组元素之间的间隔符号
  18. mv或者cp带小括号文件名解析问题总结
  19. vue3+ts实现视频根据时间轴截取,并可以通过传入截取起止时间进行当前剪辑的回显
  20. xcode连接新的iPhone进行app调试教程

热门文章

  1. Java——通过Java代码启动批处理文件(二)
  2. OpenCV copyMakeBorder
  3. 浏览器主页被篡改(IE浏览器恢复默认页)
  4. COleSafeArray
  5. 出现socket:(10107)系统调用失败
  6. 如何测试一个网页登录界面
  7. scipy.spatial.distance 与 sklearn cosine_similarity;计算cosine相似度距离,scipy pdist 结果转化成array方阵
  8. 计算机的语言——指令
  9. 温州无证程序员被拘留--程序员伤不起呀。。。。
  10. YTUoj_鸣人和佐助