Symfony 4 - 如何将业务逻辑添加到实体中

3
我有一个“Article(文章)”实体,并且我在三个twig模板中使用path()函数,提供作为第二个参数的文章“slug”来获取显示一篇文章的URL。但是我知道这不是最好的方法,因为如果我想要提供ID而不是slug,我需要在多个模板中更改代码。我考虑在Article类中使用getUrl()方法,但我无法使用Route服务生成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()方法的工作,因此如果我在路由中进行更改,它将反映在所有模板中,但我不能这样做,因为我无法在Article实体中获取Route服务。

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


如果您添加一些示例代码,那么理解您的问题将会更容易。 - sr9yar
1
有时候我会创建像ArticleView这样的对象,实际上它们是服务,因此您可以注入您的路由服务。然后,您将传递一个Article对象来创建各种与视图相关的内容。第二种方法是制作Twig扩展函数,因此您可以执行href ="{{ article_get_url(article)}}"。不太喜欢Twig扩展,因为它使在非Twig相关情况下使用功能变得有点困难。 - Cerad
你可以使用 DynamicRouter 并定义一个 RouteProvider,为你的 Entity 提供路由。 - Somrlik
你需要制作Twig扩展。 - malcolm
@Cerad 您如何在没有Twig扩展的情况下直接使用ArticleView服务?您是否将其作为变量传递到控制器中的模板中? - Marco P.
你只需要像处理其他变量一样将它传递到Twig模板中。然后使用类似于{{ articleView.getUrl(article) }}的语法即可。 - Cerad
2个回答

2
你所寻找的方法是实现所需效果的错误方式。为了使其工作,您将不得不将路由器注入实体中,从而将其转化为“业务逻辑”(例如控制器),而非“持久性”,并破坏单一责任原则。此外,从技术上实现它很困难,因为您将不得不修改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 %}

如果您正在使用带有autowiring/autoconfigure的Symfony 3.4+,仅创建类就足够了,否则您需要在容器中注册它作为Twig扩展。有关更多详细信息,请参考Symfony文档
第二种提到的选项仅在您想要在视图/模板之外重用路由生成时才是必需的。在这种情况下,必须将所需的逻辑(现在在Twig扩展中)移动到单独的独立服务中。然后,您必须将此服务注入到扩展中并使用它来代替直接调用路由器。请查看相关的文档条目,以获得有关创建/注册服务的详细说明。

0

我不明白为什么一个商业实体需要了解路由系统。

如果你想让实体知道它的URL,只需在实体中添加setUrl()getUrl()方法,并将已生成的URL存储起来即可。


你是在建议直接将URL存储在数据库中吗? - Marco P.
1
是的,如果它是他们身份的一部分。 - goto

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接