maymeow / cakephp-service-layer
CakePHP 的服务层和领域/业务模型实现
Requires
- php: >=7.2
- cakephp/cakephp: ^4.0
Requires (Dev)
- cakephp/cakephp-codesniffer: ^3.0
- dereuromark/cakephp-ide-helper: ^1.0
- phpstan/phpstan: ^0.12.14
- phpunit/phpunit: ^8.5.0
This package is auto-updated.
Last update: 2024-09-12 19:40:47 UTC
README
这更多的是一种设计模式和概念性想法,而不是大量代码,这将提高您的代码库的可维护性。此插件仅提供一些类,帮助您在遵循约定优于配置的框架方式下将此概念应用于 CakePHP 框架。
支持的 CakePHP 版本
- 对于 CakePHP 3.x,使用 1.x 分支和版本
- 对于 CakePHP 4.x,使用 master 分支或 2.x 版本
简介
任何 MVC 框架的黄金法则基本上是“胖模型,瘦控制器”。
虽然这效果相当不错,但通过分离,例如数据库操作和实际的业务逻辑,抽象可以做得更好。大多数 Cake 开发者可能将表对象用作一切事物的容器。从严格意义上讲,这是不正确的。业务逻辑不属于数据库表的上下文,并且应该与任何持久层分离。CakePHP 喜欢将持久性与业务逻辑混合在一起。非常出色的业务逻辑应该是无框架的。您只需使用框架来持久化业务逻辑的结果。
表对象应仅封装该表直接相关的任何内容。与该表相关的查询、自定义查找器等。我们想要遵循的一些原则是关注点分离和单一职责原则。CakePHP 中的 Model
文件夹代表数据模型,不应将其用于添加此关注点之外的任何内容。服务层有助于解决这个问题。
服务类,一个自定义的类,不是 CakePHP 框架的一部分,将实现真实业务逻辑并执行任何类型的计算或其他逻辑操作,然后将结果传递回控制器,控制器再将该结果传递给视图。
这确保了代码的每一部分都易于测试和替换。例如,服务在 shell 应用程序中也可以使用,因为它不依赖于控制器。如果很好地分离,理论上可以在两个应用程序之间共享一个包含所有表对象的插件,因为特定于每个应用程序的应用程序逻辑将实现在服务层中,而不是在表对象中。
回答起来更容易的问题可能是,当不需要使用它时。如果您应用程序的业务逻辑将只有一种类型的客户端,比如用户界面,并且它的用例响应不涉及多个事务资源,那么可能不需要服务层 [...]。
但是,一旦您设想了第二种类型的客户端或第二种事务资源在用例响应中使用,那么从一开始就在设计中考虑服务层就很有价值。
它是有观点的
本页面上有一段简单的段落对此进行了很好的解释,说明了为什么领域驱动设计(DDD)在MVC中是一个相当抽象且具有很强主观性的话题。
根据埃里克·埃文斯的说法,领域驱动设计(DDD)不是一种技术或方法论。它是一种关于如何组织应用程序和结构代码的思维方式。这种思维方式与流行的MVC架构非常互补。领域模型提供了系统的结构视图。大多数时候,应用程序不会改变,改变的是领域。然而,MVC并没有真正告诉你模型应该如何结构化。这就是为什么一些框架不会强制你使用特定的模型结构,而是让你根据知识和技能的增长来发展你的模型。
由于这个原因,CakePHP没有提供DDD或服务层架构的模板结构。基本上,这取决于你自己。
此插件提供了一个可能的实现。这不是一成不变的,你也不必同意它。将此插件视为一个实现建议或模板,以及一个指导,供那些关心可维护代码但不知道如何进一步改进代码库的开发者参考。
如何使用它
CakePHP默认使用定位器而不是依赖注入容器。此插件提供了一个带有特质的 CakePHP 风格的服务定位器,使您可以在应用程序的任何位置使用该特质简单地加载服务。
还有一个ServicePaginatorTrait,允许您使用类似表格对象的存储库对象在服务中使用分页。
以下示例使用SomeServiceNameService
类
use Burzum\CakeServiceLayer\Service\ServiceAwareTrait; class AppController extends Controller { use ServiceAwareTrait; } class FooController extends AppController { public function initialize() { parent::initialize(); $this->loadService('Articles'); } /** * Get a list of articles for the current logged in user */ public function index() { $this->set('results', $this->Articles->getListingForUser( $this->Auth->user('id') $this->getRequest()->getQueryParams() )); } }
如果控制器中已存在使用该服务名称的属性,则会抛出警告。在理想情况下,当使用服务时,您的控制器不必使用任何表实例。表不是控制器关心的内容。
上述代码的优点是,传递给服务的参数可以来自shell输入或其他来源。逻辑与控制器或模型无关。使用适当的抽象,底层数据源,即服务使用的存储库,应该可以透明地替换为任何匹配所需实现的接口。
您还可以加载命名空间服务
// Loads BarService from MyPlugin and src/Service/Foo/ $this->loadService('MyPlugin.Foo/Bar');
确保使用文档化的 IdeHelper 扩展来获取IDE支持。
有关详细信息,请参阅文档。
为什么没有DI容器?
您可以使用您选择的DI容器实现相同的效果,但之前从未真正需要这样做,定位器本身也工作得很好,并且它们比添加完整的DI容器库要小得多。在过去大约10年的任何 CakePHP 应用程序中,包括500多张表的重大项目,我从未需要添加DI容器。 CakePHP 的一个核心概念是遵循约定,而不是在巨大的DI配置中编织事物,或者在一个容器中四处使用容器,这在大多数情况下只是许多开发者使用的超级全局桶。
当然,这是一个非常主观的话题,所以如果你不同意并想使用DI容器,请随时这样做!有选择权真是太好了!
CakePHP的DI插件
您可以在Awesome CakePHP插件列表中找到更多DI插件。
演示
沙盒展示了实时演示。请查看公开可用的代码以获取详细信息。
许可证
版权所有:弗洛里安·克雷默
根据MIT许可证授权。文件的分发必须保留上述版权声明。