简体   繁体   English

Symfony 4 - 如何向实体添加业务逻辑

[英]Symfony 4 - How to add business logic to an Entity

I have an Article entity and actually I use the path() function in 3 twig templates, providing as second parameter the slug of the article, to get the url to show one article. 我有一个Article实体,实际上我在3个twig模板中使用path()函数,作为第二个参数提供文章的slug ,以获取显示一篇文章的url。

I know it's not the best way to do that, because, for example, if I want to provide the id instead of the slug I should change the code in more than one template. 我知道这不是最好的方法,因为,例如,如果我想提供id而不是slug我应该在多个模板中更改代码。

I thought to use a getUrl() method in the Article class but than I can't use the Route service to generate the url. 我想在Article类中使用getUrl()方法,但我不能使用Route服务来生成url。

Is there a better way to do that in Symfony 4? 在Symfony 4中有更好的方法吗?

This is the a portion of the ArticleController : 这是ArticleController的一部分:

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

use App\Entity\Article;
use App\Repository\ArticleRepository;

class ArticleController extends AbstractController
{
    ...

    /**
     * @Route("/article/{slug}", name="article_show")
     */
    public function show(Article $article) {
        return $this->render('article/show.html.twig', [
            'article' => $article
        ]);
    }

}

Then in the templates I have a similar code like this: 然后在模板中我有类似的代码:

 {% for article in articles %}
    ...

        <a href="{{ path('article_show',{ 'slug': article.slug } ) }}">
            {{ article.title }}
        </a>
    ...
 {% endfor %}

What I would like to do is to have a code like this: 我想做的是拥有这样的代码:

 {% for article in articles %}
    ...

        <a href="{{ article.getUrl() }}">
            {{ article.title }}
        </a>
    ...
 {% endfor %}

where getUrl does the work of the path() method, so if I change something in the route it will be reflected in all the templates, but I can't do this because I can't fetch the Route service in the Article entity. 其中getUrl执行path()方法的工作,所以如果我在路由中更改某些内容它将反映在所有模板中,但我不能这样做,因为我无法在Article实体中获取Route服务。

So is there an alternative way to accomplish the same goal? 那么有没有另一种方法来实现相同的目标?

What you are looking for is a wrong way of achieving the desired effect. 您正在寻找的是达到预期效果的错误方法。 For this to work you will have to inject the router into the entity which essentially turns it into "business logic" (eg controller) rather than "persistence" and breaks the single responsibility principle. 为此,您必须将路由器注入实体,实际上将其转换为“业务逻辑”(例如控制器)而不是“持久性”并打破单一责任原则。 Besides it's hard to achieve technically as you'll have to modify Doctrine's internals 除了技术上很难实现,因为你必须修改Doctrine的内部

There are two ways to handle this properly and both of them involve custom Twig extension: 有两种方法可以正确处理这两种方法,它们都涉及自定义的Twig扩展:

The simplest one is to define a custom Twig filter which takes care of generating the correct URL: 最简单的方法是定义一个自定义的Twig过滤器,它负责生成正确的URL:

<?php

namespace App\Twig\Extension;

use App\Entity\Article;

use Symfony\Component\Routing\RouterInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;

class ArticleExtension extends AbstractExtension
{
    private $router;

    public function __construct(RouterInterface $router)
    {
        $this->router = $router;
    }

    public function getFilters()
    {
        return [
            new TwigFilter('article_url', [$this, 'getArticleUrl']),
        ];
    }

    public function getArticleUrl(Article $article): string
    {
        return $this->router->generate('article_show', ['slug' => $article->getSlug()]);
    }
}

Then in twig you just use the filter like this: 然后在树枝上你只需使用这样的过滤器:

{% for article in articles %}
    ...

    <a href="{{ article|article_url }}">
        {{ article.title }}
    </a>
    ...
{% endfor %}

If you are using Symfony 3.4+ with awtowiring/autoconfigure just creating the class will be enough otherwise you need to register it as twig extension in the container. 如果你使用Symfony 3.4+和awtowiring / autoconfigure只是创建类就足够了,否则你需要在容器中将它注册为twig扩展。 Please refer to Symfony's documentation for more details 有关更多详细信息,请参阅Symfony的文档

The second option mentioned is only necessary if you want to reuse the route generation outside of the view/templating. 只有在想要在视图/模板之外重用路径生成时,才需要提到的第二个选项。 In this case the necessary logic (which is now in the Twig extension) must be moved to a separate standalone service. 在这种情况下,必须将必要的逻辑(现在在Twig扩展中)移动到单独的独立服务。 Then you'd have to inject this service into the extension and use it instead of directly invoking the router. 然后,您必须将此服务注入扩展并使用它而不是直接调用路由器。 Check the relevant documentation entry for a verbose walk-trough on creating/registering a service 有关创建/注册服务的详细步骤,请查看相关文档条目

I can't understand why a business entity should be aware of the routing system. 我无法理解为什么业务实体应该知道路由系统。

If you want its url know by the entity, just add a setUrl() and getUrl() method in your entity and store it the already generated url 如果您希望实体知道其URL,只需在您的实体中添加setUrl()getUrl()方法并将其存储为已生成的url

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

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