昨天,Jeffrey Way 发布了一条推文,他问大家更愿意将其控制器命名为单数还是复数。 我回答我两种方案都不选,我使用单动作控制器。随后发生的是,有的人同意,有的不同意,有的甚至做出了最奇怪的事情。

由于十分强烈的反映,我想写一篇文章来解释为什么我爱单行为控制器、还有我为什么觉得它们很美妙。

首先在开始文章之前,我想要说这个东西并不是只有单一的真相。与往常一样,我想指出的是,一切都归结于你的个人喜好。我只能教、建议和指出一些事情,由你来决定是否同意、不同意、接受、学习和 / 或调整。或者都不是。从这篇博客中获得你想要的,随心所欲地做让自己感到舒适的事情吧。

对比 CRUD 和 Domain Modelling

开始前,我们先来想想我们倾向于写 resourceful 的 CRUD 控制器。我相信很多人会坚持使用这种做法,因为这是 Laravel 中的一个标准做法,文档中的大多数示例也是使用这种方法。另外,这或许也是你在各类博客或 app 代码中经常看到的。

但是,如果你停下来思考一下,这是编写它们的最佳方法吗?是软件行业的一般性做法吗?最近几年,我在 “领域驱动设计”(Domain Driven Design) 等领域投入了大量时间,并且思考软件如何应用于你工作的领域(Domian)以及它转化的过程。当您开始考虑模仿您领域中无处不在的语言的术语和措辞时,您会发现您的代码将变得更加清晰明了,更加戳到点子上。(最后这一句话仍值得斟酌、改进)

最后,我相信编写软件的本质是尽可能地应用 domain processes 来让你的代码更加易读和更加可维护。

Resourceful 控制器在这两个方面做得并不好。首先,它们不易读,因为您倾向于根据数据来构建它们,而不是根据领域来构建它们。这样的话,你就会丢失上下文对照。你表现了数据的处理方式,但却没有说明到底发生什么了,也没有说明你使用哪个过程进行处理。

第二,你没有针对可维护性进行优化。由于你是根据数据结构来构建的,因此你也会跟着耦合进去。实际上,您的领域模型在不断发展,数据结构也在不断发展。如果你的数据结构处理着多个过程或领域的多个部分,那你将很难进行调整。

一个实际的例子

因为理论很无聊,上代码更加容易解释,所以我们来看一个实际的例子。

假设您正在构建一个应用,它允许用户去组织事件。您想提供一种创建,更新和删除这些事件的方法。这是一种非常典型的例子,你会用 CRUD 的方式来考虑实现它。那么,让我们看看就这样一个 resourceful 控制器是如何被转换的。

首先我们来看看路由:

Route::get('events', [EventController::class, 'index']);
Route::get('events/create', [EventController::class, 'create']);
Route::post('events', [EventController::class, 'store']);
Route::get('event/{event}', [EventController::class, 'show']);
Route::get('events/{event}/edit', [EventController::class, 'edit']);
Route::put('events/{event}', [EventController::class, 'update']);
Route::destroy('events/{event}', [EventController::class, 'destroy']); 

现在对应的控制器:

<?php
namespace AppHttpControllers;
use AppModelsEvent;
final class EventController
{public function index(){// ...}public function create(){// ...}public function store(){// ...}public function show(Event $event){// ...}public function edit(Event $event){// ...}public function update(Event $event){// ...}public function destroy(Event $event){// ...}
}

这个 EventController 处理所有的 CRUD 请求,展示事件列表,展示指定的事件,创建一个事件,更新一个现存的事件和删除一个事件。

来看看 index 方法的细节:

public function index()
{$events = Event::paginate(10);return view('events.index', compact('events'));
}

在这个方法中,我们检索出事件们,然后交给视图让它去展示到一个分页列表中。 到目前为止都还好。但是你现在想实现一个方法,用不同的页面去查看过去和即将来的事件。让我们看看如何在 index 方法中实现它:

public function index(Request $request)
{if ($request->boolean('past')) {$events = Event::past()->paginate(10);} elseif ($request->boolean('upcoming')) {$events = Event::upcoming()->paginate(10);} else {$events = Event::paginate(10);}return view('events.index', compact('events'));
}

呃啊!看起来好乱啊。尽管我们已经用 Eloquent scopes 来隐藏查询逻辑,但是还是有很丑的链式语句。我们来看看如何用单行为控制器来代替它。

每个单行为控制器只执行一件事情,仅仅一件事情。

首先,我们不使用查询参数去获得不同的事件列表,而是使用专用路由去实现它。

Route::get('events', ShowAllEventsController::class);
Route::get('events/past', ShowPastEventsController::class);
Route::get('events/upcoming', ShowUpcomingEventsController::class);

这个路由比之前的要长一些,但是这个比之前的要更有表达力。你可以一下子辨识出哪一个控制器处理哪一个特定的逻辑。如果你对比一下 URL,你会看到在可读性上改进了一些:

# Before
/events
/events?past=true
/events?upcoming=true
# After
/events
/events/past
/events/upcoming

现在来看其中一个控制器。就看 ShowUpcomingEventsController 这个控制器:

<?php
namespace AppHttpControllers;
use AppModelsEvent;
final class ShowUpcomingEventsController
{public function __invoke(){$events = Event::upcoming()->paginate(10);return view('events.index', compact('events'));}
} 

丑陋的 if 语句没了, and has made way for the same readable three liner we had from our first CRUD controller example. But instead of having all of the other CRUD operations we now have a dedicated controller for a dedicated action.

简单,易读,便于维护。

你可能会问自己,这样做值么,毕竟之前的 if 语句也没那么坏吧?但是我想向你展示的是你正在为未来的改进做优化,并改进维护性。下次你想要对这三个页面做任何指定改变的时候,你会知道在哪里改,并且不需要艰难地更新一个 if 语句。

当然,上面的例子很简单,我们来看一个更复杂一点的。我们试试重构 create 和 store 方法:

public function create()
{return view('events.create');
}
public function store(Request $request)
{$data = $request->validate(['name' => 'required','start' => 'required','end' => 'required|after:start',])$event = Event::create($data);return redirect()->route('event.show', $event);
}

我们要做的就是把这两个方法移到专用的控制器,这样更好地解释了这些方法做了啥。这些方法更好地服务于你,比起把它们放在一个叫做 ScheduleNewEventController 的控制器中。我们接着更新这个控制器的路由:

Route::get('events/schedule', [ScheduleNewEventController::class, 'showForm']);
Route::post('events/schedule', [ScheduleNewEventController::class, 'schedule']);

我不会向你展示一个确切的控制器,因为它们有和上面的例子一样,有两个方法,只不过把 showForm 和 schedule 重新命名为更能表达它们干了啥的名字。即使这个不是单行为控制器,但是方法论是一样的:把你应用中的专用行为(方法)和它对应的控制器拆分到一起。

好了,现在你已经看了单行为控制器的例子了。你可能会想,这会导致越来越多的文件。但事实上,这个根本就不是问题。文件多又没啥。有更多、更小、更容易维护的文件比有更大、更难分析的要好。你可以打开一个单行为控制器的文件,然后快速扫描代码,马上就能知道这是干嘛的。

我经常把他们分组到不同的目录,这些目录负责领域的各个部分。这让你从文件结构的角度看控制器时,更加容易。

拆分控制器也让你跟容易找到特定的一个控制器。想象一下,你要寻找那个可以安排事件的控制器时。现在你只需要按照文件名搜索编辑器,而不是一个通用的 EventController。

其他情况

我也被问到是否要对所有控制器执行此操作。不总是。在命名控制器时,我倾向于严谨且简洁,但我也会像你一样适应各种情况。

当然,有时候你还是想用 resourceful 控制器。比如在你构建 RESTful API 时。这样做是很有意义,因为你经常直接与数据本身交互,而没有经常与领域或任何进程进行交互。CMS(内容管理系统)或 Laravel Nova 等应用程序就是最好的例子。

但是在需要的时候,您最好问问自己的方案是否更接近领域和处理过程。在需要根据领域执行操作的时候,比如 GraphQL 之类的或 API 之类的 RPC ,这样做可能更适合。

结论

我希望这有一点见地,你现在能更理解我为什么如此喜欢单行为控制器了吧。我相信,结合小的 classes,再使用无处不在的语言、显式地命名,会带来更可维护的代码,甚至是控制器,不仅仅是领域对象。但是正如我开头所说,选择能帮助你的部分,好好分辨哪些适用于你,哪些不行。

更多学习内容请访问:

八重樱:腾讯T3-T4标准精品PHP架构师教程目录大全,只要你看完保证薪资上升一个台阶(持续更新)​zhuanlan.zhihu.com

以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的可以加入我的官方群点击此处。

laravel created_at 时间戳_Laravel 单行为控制器设计的魅力相关推荐

  1. laravel created_at 时间戳_使用 HTTP 测试测试 Laravel 中间件

    文章转发自专业的Laravel开发者社区,原始链接:https://learnku.com/laravel/t/34002 在本文中,我将展示一个使用 HTTP 测试中间件的实例.HTTP 级测试更能 ...

  2. 基于FPGA的SPI FLASH控制器设计

    1.SPI FLASH的基本特征 本文实现用FPGA来设计SPI FLASH,FLASH型号为W25Q128BV.支持3种通信方式,SPI.Dual SPI和Quad SPI.FLASH的存储单元无法 ...

  3. php找不到控制器里面的方法,php – 在Laravel 4中找不到控制器类

    如果你没有从原始位置移动控制器目录(这是«project_root»/ app / controllers /,你必须保证: > Laravel的自动加载具有控制器目录.导航到«project_ ...

  4. 【Paper】2013_基于一致性理论的无人机编队控制器设计_郭伟强

    原文地址: [1]郭伟强. 基于一致性理论的无人机编队控制器设计[D].哈尔滨工业大学,2013. 2013_基于一致性理论的无人机编队控制器设计_郭伟强 3.3 一致性理论 3.4 控制方案设计 3 ...

  5. 六自由度高超声速飞行器的建模与控制器设计

    高超声速飞行器是指飞行马赫数大于5的飞行器,它是一种近空间飞行器."近空间"可简单理解为:现有飞机飞行的最高高度(约20Km)和卫星运行轨道的最低高度(约100Km)间的空域.近空 ...

  6. 基于单片机的调光控制器设计

    1 调光控制器设计 在日常生活中,我们常常需要对灯光的亮度进行调节.本调光控制器通过单片机控制双向可控硅的导通来实现白炽灯(纯阻负载)亮度的调整.双向可控硅的特点是导通后即使触发信号去掉,它仍将保持导 ...

  7. AGV控制器设计与融合

    提 要 AGV(Automated Guided Vehicle)是自动化物流运输系统.柔性生产组织系统的关键设备.目前,柔性生产的重要性已经逐渐得到国内很多企业的重视,计划购进有关的自动车和自动物流 ...

  8. 最小拍有纹波系统仿真实验matlab代码,最小拍控制器设计.doc

    最小拍控制器设计matlab实验 [实验目的] 了解和掌握有纹波和无纹波最小拍控制器的原理和设计方法. 利用Matlab仿真,观察系统的输入输出曲线. [实验内容] 系统如图所示, G(s)?-R( ...

  9. SDRAM控制器设计

    SDRAM控制器设计 SDRAM 器件引脚示意图和功能框图如下: SDRAM 器件有如下的特性 通常情况下, SDRAM 存储器工作在 3.3V 的电压下(需要注意的是 DDR DRAM工作电压是 2 ...

最新文章

  1. UVA11427玩纸牌(全概率+递推)
  2. Web API Test Client 1.2.0
  3. 合辑 | 面试必备!18篇Java面试疑难点详解
  4. 微信小程序INC自增自减MUL自乘问题
  5. 信息学奥赛一本通_长乐一中老师演绎“奥赛传奇”
  6. python学生类出不来中文_Python 这类看起来学习门槛低的语言,是否真的适合入门编程学习?...
  7. 元类被称为 Python 中的“深奥的巫术“
  8. c++如何显示图片_Vue+laravel后端添加商品后图片如何显示?
  9. Floyd算法的理解
  10. 基于java 企业进销存管理系统设计(含源文件)
  11. 为什么机器学习很难学习因果关系?
  12. [转载] Python数据分析:python与numpy效率对比
  13. python 实现两个excel表格数据的对比--代码
  14. MySQL常用语句、连接MySQL格式:mysql-h主机地址-u用户名-p用户密 (详细篇 修改版)
  15. 标准C程序设计七---03
  16. EPUB文档格式简单总结
  17. 2022年全国职业院校技能大赛网络安全竞赛试题 A-1样题
  18. Linux系统磁盘分区及挂载 - fdisk
  19. 在8051上的人工智能
  20. windows10操作系统任务栏点击无反应怎么解决?

热门文章

  1. spring boot项目怎么记录用户操作行为和登录时间_6 个 Github 项目拿下 Spring Boot
  2. java定时器每一分钟执行一次_2行代码搞定一个定时器
  3. kali linux u盘自启,如何实现Kali linux系统下的U盘启动(小白指导)
  4. python 预测 位置_Python:核岭回归预测,KRR
  5. PML之平均 、中值 、众数、标准偏差、方差
  6. matplotlib之subplot和动态作图(笔记六)
  7. Maven 仓库使用与私有仓库搭建
  8. Java的守护线程Daemon
  9. 安徽大学2014年c语言平时作业,安徽大学2014年c语言平时作业
  10. cn域名保护隐私_为什么域名隐私保护如此重要