MVC(Laravel)在哪里添加逻辑

2022-08-30 06:37:38

假设每当我执行 CRUD 操作或以特定方式修改关系时,我也想执行其他操作。例如,每当有人发布帖子时,我也想将某些内容保存到表中以进行分析。也许不是最好的例子,但总的来说,有很多这种“分组”功能。

通常,我看到这种类型的逻辑放入控制器中。这一切都很好,直到你想在很多地方重现这个功能。当您开始进入部分,创建API并生成虚拟内容时,它就变成了保持干燥的问题。

我所看到的管理方法是事件,存储库,库和添加到模型中。以下是我对每一项的理解:

服务业:这是大多数人可能放置此代码的地方。我对服务的主要问题是,有时很难在其中找到特定的功能,我觉得当人们专注于使用Eloquent时,它们会被遗忘。当我可以这样做时,我怎么知道我需要调用库中的方法?publishPost()$post->is_published = 1

我看到的唯一条件是,如果您只使用服务(理想情况下,使Eloquent无法从控制器一起访问)。

最终,如果您的请求通常遵循您的模型结构,这似乎只会创建一堆额外的不必要的文件。

存储 库:据我所知,这基本上就像一个服务,但有一个接口,所以你可以在ORM之间切换,我不需要。

事件:从某种意义上说,我认为这是最优雅的系统,因为您知道您的模型事件总是会在Eloquent方法上调用,因此您可以像往常一样编写控制器。我可以看到这些变得混乱,如果有人有大型项目使用事件进行关键耦合的例子,我希望看到它。

模型:传统上,我会有执行CRUD和处理关键耦合的类。这实际上使事情变得容易,因为您知道CRUD周围的所有功能+必须完成的任何操作都在那里。

很简单,但在MVC架构中,这通常不是我看到的。从某种意义上说,虽然我更喜欢这个而不是服务,因为它更容易找到,而且要跟踪的文件更少。不过,它可能会变得有点杂乱无章。我想听听这种方法的缺点,以及为什么大多数人似乎没有这样做。

每种方法的优点/缺点是什么?我错过了什么吗?


答案 1

我认为您介绍的所有模式/架构都非常有用,只要您遵循SOLID原则。

对于在哪里添加逻辑,我认为参考单一责任原则很重要。另外,我的答案认为您正在从事中型/大型项目。如果这是一个在页面上抛出的东西项目,忘记这个答案,把它全部添加到控制器或模型中。

简短的回答是:对你有意义的地方(通过服务)。

长答案:

控制者:控制者的责任是什么?当然,您可以将所有逻辑放在控制器中,但这是控制器的责任吗?我不这么认为。

对我来说,控制器必须接收请求并返回数据,这不是放置验证,调用db方法等的地方。

模型:这是否是添加逻辑(如在用户注册时发送欢迎电子邮件或更新帖子的投票计数)的好地方?如果您需要从代码中的其他位置发送相同的电子邮件,该怎么办?是否创建静态方法?如果该电子邮件需要来自其他模型的信息,该怎么办?

我认为模型应该代表一个实体。使用Laravel,我只使用模型类来添加诸如,和关系之类的东西(这是因为我使用存储库模式,否则模型也将具有,,等方法)。fillableguardedtablesaveupdatefind

存储库(存储库模式):一开始我对此感到非常困惑。而且,像你一样,我想“好吧,我使用MySQL,仅此而已。

但是,我已经平衡了使用存储库模式的优缺点,现在我使用它。我认为现在,此时此刻,我只需要使用MySQL。但是,如果三年后我需要改用MongoDB之类的东西,大部分工作都完成了。所有这些都是以牺牲一个额外的接口和一个.$app->bind(«interface», «repository»)

事件(观察者模式):事件对于可以在任何给定时间在任何类上抛出的东西很有用。例如,考虑向用户发送通知。需要时,可以触发事件以在应用程序的任意类上发送通知。然后,您可以有一个类似的类来处理用户通知的所有触发事件。UserNotificationEvents

服务:到目前为止,您可以选择向控制器或模型添加逻辑。对我来说,在服务中添加逻辑是有意义的。让我们面对现实吧,服务是类的一个花哨的名字。而且,您可以在您的应用程序中拥有对您有意义的任意数量的类。

举个例子:不久前,我开发了类似Google Forms的东西。我从 a 开始,最后以 、 、 、 和 .为什么?因为它对我来说是有道理的。如果你与一个团队合作,你应该把你的逻辑放在对团队有意义的地方。CustomFormServiceCustomFormServiceCustomFormRenderCustomFieldServiceCustomFieldRenderCustomAnswerServiceCustomAnswerRender

使用服务与控制器/模型的优点是,您不受单个控制器或单个模型的约束。您可以根据需要创建任意数量的服务,具体取决于应用程序的设计和需求。除此之外,还可以在应用程序的任何类中调用服务。

这很长,但我想向您展示我如何构建我的应用程序:

app/
    controllers/
    MyCompany/
        Composers/
        Exceptions/
        Models/
        Observers/
        Sanitizers/
        ServiceProviders/
        Services/
        Validators/
    views
    (...)

我将每个文件夹用于特定功能。例如,该目录包含一个负责处理验证的类,该类基于特定验证程序的 和(通常每个模型一个)。我可以很容易地将此代码放在服务中,但是对我来说,即使仅在服务中使用(目前),为此设置一个特定的文件夹也是有意义的。ValidatorsBaseValidator$rules$messages

我建议您阅读以下文章,因为它们可能会更好地向您解释事情:

Dayle Rees(CodeBright的作者)的《Breaking the Mold》:这就是我把它们放在一起的地方,尽管我为了满足我的需求而改变了一些东西。

Chris Goosey在Laravel中使用Repositories and Services解耦你的代码:这篇文章很好地解释了什么是服务和存储库模式,以及它们如何组合在一起。

Laracasts还有简化的存储库单一责任,这些都是很好的资源,有实际的例子(即使你必须付钱)。


答案 2

我想发布对我自己的问题的回复。我可以谈论这个好几天,但我会尝试快速发布这个,以确保我得到它。

我最终利用了Laravel提供的现有结构,这意味着我主要将文件保留为模型,视图和控制器。我还有一个“库”文件夹,用于存放可重用的组件,这些组件不是真正的模型。

我没有将我的模型包装在服务/库中。提供的所有原因都不能100%说服我使用服务的好处。虽然我可能是错的,但据我所知,它们只会导致大量额外的几乎空的文件,我需要在使用模型时创建和切换,并且还确实降低了使用eloquent的好处(特别是在检索模型时,例如,使用分页,范围等)。

我将业务逻辑放在模型中,并直接从我的控制器访问雄辩的。我使用多种方法来确保业务逻辑不会被绕过:

  • 访问器和突变体:Laravel有很棒的访问器和突变体。如果我想在帖子从草稿移动到已发布时执行操作,我可以通过创建函数setIsPublishedAttribute并在其中包含逻辑来调用它。
  • 覆盖创建/更新等:您始终可以重写模型中的 Eloquent 方法以包含自定义功能。这样,您就可以在任何 CRUD 操作上调用功能。编辑:我认为在较新的Laravel版本中覆盖创建有一个错误(所以我使用现在在boot中注册的事件)
  • 验证:我以相同的方式挂接我的验证,例如,我将通过覆盖CRUD函数以及访问器/赋值器(如果需要)来运行验证。有关详细信息,请参阅 Esensi 或 dwightwatson/validating。
  • 魔法方法:我使用模型的__get和__set方法在适当的时候挂钩到功能中
  • 扩展雄辩:如果您想在所有更新/创建时都采取任何操作,您甚至可以扩展雄辩并将其应用于多个模型。
  • 事件:这也是一个直截了当且普遍同意的地方。我认为事件的最大缺点是很难追踪异常(可能不是Laravel的新事件系统的新案例)。我也喜欢根据我的事件所做的事情而不是何时调用来分组我的事件......例如,有一个MailSender订阅者,它侦听发送邮件的事件。
  • 添加数据透视/属于多个事件:我挣扎的时间最长的事情之一是如何将行为附加到对属于ToMany关系的修改上。例如,每当用户加入组时执行操作。我几乎完成了为此完善的自定义库。我还没有发布它,但它是功能性的!将尝试尽快发布链接。编辑我最终把我所有的枢轴都变成了正常的模型,我的生活变得容易多了......

通过使用模型解决人们的担忧:

  • 组织:是的,如果你在模型中包含更多的逻辑,它们可以更长,但总的来说,我发现我的模型中有75%仍然很小。如果我选择组织较大的文件,我可以使用特征来做到这一点(例如,根据需要为模型创建一个文件夹,其中包含更多文件,如PostScopes,PostAccessors,PostValidation等)。我知道这不一定是特征的用途,但这个系统可以毫无问题地工作。

附加说明:我觉得把你的模型包裹在服务中就像有一把瑞士军刀,里面有很多工具,然后围绕它建造另一把基本上做同样的事情的刀?是的,有时您可能希望用胶带将刀片拔掉或确保两个刀片一起使用...但通常还有其他方法可以做到这一点...

何时使用服务:本文很好地阐述了何时使用服务的好例子(提示:这并不常见)。他说,基本上,当你的对象在生命周期的奇怪部分使用多个模型或模型时,这是有道理的。http://www.justinweiss.com/articles/where-do-you-put-your-code/


推荐