我想区分不同类型的块:
- 呈现同一控制器的其他操作的块。
- 可以在应用程序的其他部分使用的块。
有什么区别?
仅呈现与父模板相同的控制器的其他操作的块大部分时间都在那里模块化一个页面并使部分可缓存。此块在整个应用程序中仅使用一次。
呈现诸如评级星或评论之类的部分的块是一种提供特定功能的独立小部件。当前控制器对这个小部件一无所知。这种块主要在应用程序中多次使用。
这对软件设计意味着什么?
这意味着我们可能希望在未来改变评论和评分的工作方式。将来可能不再由 ESI 呈现,因为我们已经将功能外包给了第三方服务并且只需要在这个地方包含某种 JavaScript?还是我们直接渲染它们?
这必须由小部件决定,而不是由包含小部件的部分决定。
那么我能做些什么来改进我的设计呢?
您可以继续使用 ESI(因为它对您的用例有意义),但您应该更改模块包含在 Twig 文件中的方式。您应该将此逻辑从模板中移出到 AcmeDemoCommunityBundle 中的一个单独的 Twig 扩展中。
namespace Acme\DemoCommunityBundle\Twig;
use Symfony\Component\HttpKernel\Fragment\FragmentHandler;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
use Acme\DemoCommunityBundle\Rating\RateableInterface;
class CommunityExtension extends \Twig_Extension
{
/**
* @var string
*/
const RATING_ACTION = 'AcmeDemoCommunityBundle:Rating:ratingStars';
/**
* @var FragmentHandler
*/
protected $handler;
public function __construct(FragmentHandler $handler)
{
$this->handler = $handler;
}
public function getFunctions()
{
return array(
'community_rating' => new \Twig_Function_Method($this, 'communityRating', array('is_safe' => array('html'))),
);
}
public function communityRating(RateableInterface $object, $readOnly = false)
{
return $this->handler->render(new ControllerReference(self::RATING_ACTION, array(
'objectType' => $object->getRatingType(),
'objectId' => $object->getId(),
'readOnly' => $readOnly
)), 'esi', $options);
}
public function getName()
{
return 'community';
}
}
services:
acme_community.twig.community:
class: Acme\DemoCommunityBundle\Twig\CommunityExtension
arguments: [ @fragment.handler ]
tags:
- { name: twig.extension }
现在您的模板应如下所示:
{% for products as product %}
<div class="product">
<h4>{{ product.name }}</h4>
<div class="ratings">
{{ community_rating(product, true) }}
</div>
</div>
{% endfor %}
通过这种设计,可以很容易地在我们的应用程序中使用评级星,但我们也可以灵活地更改未来评级工作的实现方式,而无需触及使用评级的模板。