简体   繁体   English

Symfony - 在哪里放置多个/所有控制器的通用代码?

[英]Symfony - Where to place common code for multiple/all Controllers?

Let's say my app has multiple pages like假设我的应用程序有多个页面,例如

  • /home /home
  • /settings /设置
  • /blog /博客

For all these I need some common code.对于所有这些,我需要一些通用代码。 Example: Fetching the E-Mail-Address from a logged in user to show it on the top-right of each page, next to a logout button.示例:从登录用户那里获取电子邮件地址以将其显示在每个页面的右上角,在注销按钮旁边。

So on each and every controller (HomeController, SettingsController, BlogController) I would have to register a twig variable:因此,在每个控制器(HomeController、SettingsController、BlogController)上,我必须注册一个 twig 变量:

eg: AppBundle/Controller/HomeController.php例如:AppBundle/Controller/HomeController.php

public function indexAction()
{
    $user_email = Something::getUserEmail();
    ...
    return $this->render('home/index.html.twig', array('user_email' => $user_email));
}

So the getUserEmail() (and probably a lot more business logic) will be the same on all front end controller methods of my app.所以getUserEmail() (可能还有更多的业务逻辑)在我的应用程序的所有前端控制器方法上都是相同的。 It would be very redundant to copy the code into each controller method that renders a page.将代码复制到呈现页面的每个控制器方法中将是非常多余的。 I could register a service but I would have to call it on every controller too.我可以注册一个服务,但我也必须在每个控制器上调用它。 I could also write a BaseController with - let's say BaseController->init() - but again, I would have to call this method on every controller method over and over.我也可以用BaseController->init()编写一个 BaseController - 但同样,我必须一遍又一遍地在每个控制器方法上调用这个方法。

So what is the best practice to place common business logic for multiple/all controllers?那么为多个/所有控制器放置通用业务逻辑的最佳实践是什么?

Bonus: can I "pre-register" twig variables like in smarty or do I have to submit them via render ?奖励:我可以像 smarty 一样“预注册”树枝变量,还是必须通过render提交它们? It would be nice to register $user_email only once and not on each controller method as this would again lead to a lot of duplicate code.最好只注册 $user_email 一次而不是在每个控制器方法上注册,因为这会再次导致大量重复代码。

Asking for Symfony 3+, 4.要求 Symfony 3+、4。

TL;DR: don't make your controller 'fat' and use Dependency Injection instead. TL;DR:不要让你的控制器变得“胖”,而是使用依赖注入。

I'm using Symfony for years and I had the same question for a long time.我已经使用 Symfony 多年了,我也有同样的问题很长一段时间。 This article is worth reading. 这篇文章值得一读。 By using many third party libraries and bundles, I saw a common way where to place business logic.通过使用许多第三方库和包,我看到了放置业务逻辑的常见方式。

Here's an example of the directory structure of one of my bundles:这是我的一个包的目录结构示例:

Appbundle\
    Command
    Controller
    DataFixtures
    Datatables
    Entity
    EventListener
    Exception
    Extension
    Form
    Menu
    Resources\
         views
    Security\
         Authorization\
             Voter
    Template
    Util
    Validator

(not all directories are mentioned) (未提及所有目录)

An important part of the business logic is in Entity , while the security rules (mainly SecurityVoters) is in Security\\Authorization\\Voter .业务逻辑的一个重要部分在Entity ,而安全规则(主要是 SecurityVoters)在Security\\Authorization\\Voter

For business logic that is not tightly coupled to Symfony Components or Doctrine (like Forms, SecurityVoters and my entity model) I create classes for a specific goal (keeping Single responsibility principle in mind) and use Services/ Dependency Injection to use the business logic in my controller:对于与 Symfony 组件或 Doctrine 不紧密耦合的业务逻辑(如 Forms、SecurityVoters 和我的实体模型),我为特定目标创建类(牢记单一责任原则)并使用服务/ 依赖注入来使用业务逻辑我的控制器:

use AppBundle\Mailer\Something;

// ...
public function indexAction()
{
    $mailer = $this->get(Something::class);

    $user_email = $mailer->getUserEmail();

    ...
    return $this->render('home/index.html.twig', array('user_email' => $user_email));
}

One important rule: keep your controller slim .一条重要规则: 保持您的控制器纤薄 It has only one purpose: getting a Request and send back a Response.它只有一个目的:获取请求并发回响应。 Keeping your controllers as slim as possible has a lot of advantages:使您的控制器尽可能纤薄有很多优点:

  • It makes unit tests easier.它使单元测试更容易。 Testing services is a lot easier than testing controllers.测试服务比测试控制器容易得多。
  • You can use the same business logic in your Commands too.您也可以在命令中使用相同的业务逻辑。 After my application got more complicated, I learned that not all things my application did was necessarily initiated by the front controller.在我的应用程序变得更加复杂之后,我了解到并非我的应用程序所做的所有事情都必须由前端控制器启动。 For example background tasks: they are initiated by a cron job.例如后台任务:它们由 cron 作业启动。
  • If you want to have a second front controller (a RESTfull API for your native app/third party access/etc.) you can reuse the services.如果您想拥有第二个前端控制器(用于本机应用程序/第三方访问等的 RESTfull API),您可以重用这些服务。 If you want to switch to another framework you can easily switch and use the Symfony Components too.如果您想切换到另一个框架,您也可以轻松切换和使用 Symfony 组件。

For you question about Twig variables, check How to Inject Variables into all Templates (ie global Variables) .对于有关 Twig 变量的问题,请检查如何将变量注入所有模板(即全局变量)

Have you try the Traits ?你试过Traits吗? according to the docs根据文档

A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes Trait 旨在通过使开发人员能够在多个独立类中自由重用方法集来减少单继承的一些限制

A Trait is similar to a class, but only intended to group functionality in a fine-grained and consistent way Trait 类似于类,但仅用于以细粒度和一致的方式对功能进行分组

If you does not want Injection or Services this would be a good idea.如果您不想要注射或服务,这将是一个好主意。 Symfony is using this approach to. Symfony 正在使用这种方法。

abstract class Controller implements ContainerAwareInterface
{
    use ContainerAwareTrait;
    use ControllerTrait;
....
}

You could check both traits and give it a try.您可以检查这两个特征并尝试一下。

Hope it helps希望能帮助到你

If I understood you correctly, you can use standard controller and twig possibilities to achieve this goal.如果我理解正确,您可以使用标准控制器和树枝来实现这一目标。 You can create a controller for common actions like rendering an email of logged-in user, and then - in "base.html.twig" - render action's result.您可以为常见操作创建一个控制器,例如呈现登录用户的电子邮件,然后 - 在“base.html.twig”中 - 呈现操作的结果。

// CommonController.php // 通用控制器.php

/**
 * @Route("/common/get-email", name="get_email")
 */
public function getEmail()
{
    // ... fetch email in some way ...
    return $this->render('home/index.html.twig', array('user_email' => $user_email));
}

// base.html.twig // base.html.twig

{{ render(url('get_email')) }}

Please, consider the Embedding Controllers section.请考虑嵌入控制器部分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM