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

Symfony 4 - How to add business logic to an Entity

我有一个 Article 实体,实际上我在 3 个树枝模板中使用了 path() 函数,提供了 slug 作为第二个参数文章的 url 来显示一篇文章。

我知道这不是最好的方法,因为例如,如果我想提供 id 而不是 slug,我应该更改多个模板中的代码。

我想在文章 class 中使用 getUrl() 方法,但我无法使用路由服务生成 url .

在 Symfony 4 中有更好的方法吗?

这是 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
        ]);
    }

}

然后在模板中我有类似这样的代码:

 {% for article in articles %}
    ...

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

我想做的是有这样的代码:

 {% for article in articles %}
    ...

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

其中 getUrl 执行 path() 方法的工作,因此如果我更改路线中的某些内容,它将反映在所有模板中,但我无法这样做,因为我无法获取路线服务在文章实体中。

那么是否有其他方法可以实现相同的目标?

您正在寻找的是达到预期效果的错误方法。为此,您必须将路由器注入到实体中,这实际上将其变成 "business logic"(例如控制器)而不是 "persistence" 并打破了单一责任原则。此外,技术上很难实现,因为您必须修改 Doctrine 的内部结构

有两种方法可以正确处理这个问题,它们都涉及自定义 Twig 扩展:

最简单的方法是定义一个自定义 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()]);
    }
}

然后在 twig 中,你只需像这样使用过滤器:

{% for article in articles %}
    ...

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

如果您使用带有 awtowiring/autoconfigure 的 Symfony 3.4+,只需创建 class 就足够了,否则您需要将其注册为容器中的 twig 扩展。 详情请参考Symfony's documentation

仅当您想在 view/templating 之外重用路由生成时,才需要提到的第二个选项。在这种情况下,必须将必要的逻辑(现在位于 Twig 扩展中)移动到单独的独立服务中。然后你必须将这个服务注入到扩展中并使用它而不是直接调用路由器。检查相关的 documentation entry 以获得关于 creating/registering 服务的详细遍历

我不明白为什么企业实体应该知道路由系统。

如果你想让它的 url 被实体知道,只需在你的实体中添加一个 setUrl()getUrl() 方法并将它存储在已经生成的 url