truffo/ez-content-decorator-bundle

1.0.0 2015-08-03 20:50 UTC

This package is not auto-updated.

Last update: 2024-09-28 17:17:58 UTC


README

介绍

当我们在模板上工作时,eZ Publish 通常提供两个对象

  • 位置:表示在树状结构中的位置
  • 内容:表示实际的内容

大多数情况下,这两个元素足够了,但很快,我们就会感到需要扩展这两个对象。

一个典型的例子是在控制器中通过 LocationQuery 获取位置的子列表,我们将对其进行分页并将其传递给视图。

视图将显示一个带有标题的列表,标题将由标题字段或内容主体字段组成,并截断到一定数量的字符。

简而言之,这是Web的一个大经典。

通常,我们会重复这些操作成百上千次。由于项目中有多个开发者,每个人都会有一些小差异

  • 有些人喜欢排除不合适的内容类型,而有些人则只包含合适的内容类型
  • 有些人喜欢在控制器中计算最大值,而有些人则将任务委托给子查询

我们发现缺乏一致性,以及内容类型之间的复制/粘贴,每次更改都会导致无休止的反馈循环,总的来说,我们有一个技术债务的温床。

## 想法:装饰器

此包提供了一个非常简单的机制来具体解决此问题,它允许我们非常简单、非常快速地创建封装我们的内容类型的装饰器。

想法很简单,首先将构成内容的元素组合在一个类中

  • 位置
  • 内容

这是 ContentDecorator 类。好吧,这没什么用。

这个类将作为我们的基类,用于定义具体实例的装饰器。

class Article extends ContentDecorator {
     public function getPropertyArticle()
}
class Blog extends ContentDecorator {
     public function getPropertyBlog()
}

每个类都可以定义特定于每种内容类型的特定方法。

最后,我们利用语义配置来在类和内容类型之间建立联系。

ezcontentdecorator.global.class_mapping:
  article: \<vendor\<mon_site_bundle>\ContentType\Article
  blog: \<vendor\<mon_site_bundle>\ContentType\Blog

请注意,我们可以通过语义配置在不同的 siteaccess 之间使用不同的映射。这在多站点平台上可能非常实用。

ContentDecoratorFactory 服务允许根据内容类型实例化正确的类。

$article = $contentDecoratorFactory->getContentDecorator($articleLocation);
echo $article->getPropertyArticle();

$blog = $contentDecoratorFactory->getContentDecorator($blogLocation);
echo $blog->getPropertyBlog();

自动加载

到目前为止,为了利用此机制,必须在控制器中的每次调用中

  • 加载装饰器
  • 通过自定义 Twig 操作符直接在视图中加载装饰器

这不是一个好的解决方案,尤其是因为 eZ Publish 提供了一个 PreContentView 事件监听器,它允许我们自动加载装饰器并将变量注入到模板中。

/**
 * @param PreContentViewEvent $event
 */
public function onPreContentView( PreContentViewEvent $event )
{
    $contentView = $event->getContentView();

    /** @var \Truffo\eZContentDecoratorBundle\Decorator\ContentDecoratorFactory $contentDecoratorFactory */
    $contentDecoratorFactory = $this->container->get('ezcontentdecorator.services.factory');

    if ($contentView->hasParameter('location')) {
        $location = $contentView->getParameter('location');
        /** @var \Truffo\eZContentDecoratorBundle\Decorator\ContentDecorator $contentDecorator */
        $contentDecorator = $contentDecoratorFactory->getContentDecorator($location);
        $contentView->addParameters([
            $contentDecorator->getContentTypeIdentifier() => $contentDecorator,
            'decorator' => $contentDecorator
        ]);
    }
}

因此,我们有两个变量,它们在所有具有位置参数的模板中自动可用

  • <内容类型名称>
  • 装饰器

它们是相同的,只是语法上的糖。

因此,在一个文章的完整视图中,我们可能会看到以下代码

{{ ez_render_field(article.content, "title") }}
{{ article.propertyArticle }}

或其等效代码

{{ ez_render_field(decorator.content, "title") }}
{{ decorator.propertyArticle }}

在列表中的自动加载

找到了一种在包含租赁视图的视图中自动加载的解决方案。现在,对于搜索结果,这几乎是同样简单的。

一个良好的实践是始终使用分页器(PagerFanta)来通过SearchService获取查询结果。这个组件的优点是允许注入适配器。

LocationDecoratorSearchAdapter会自动将我们的装饰器注入到返回的每个租赁实例中。

在我们的存储库中,我们可能有一些像这样的东西

public function getArticleList($location, $limit = 10, $page = 1)
{
    $query = new LocationQuery();
    $query->criterion =
    new Criterion\LogicalAnd(array(
         new Criterion\ContentTypeIdentifier(['article']),
         new Criterion\Subtree($location->pathString),
         new Criterion\Visibility(Criterion\Visibility::VISIBLE)
    ));
    $query->sortClauses = [  new SortClause\Field('article', 'publication_date', Query::SORT_DESC, 'fre-FR') ];
    return LocationDecoratorSearchAdapter::buildPager($query, $searchService, $contentDecoratorFactory
    , $limit, $page)
}

在我们的控制器中

$params += ['items' => $helper->getArticleList($location)];  
return $this->get('ez_content')->viewLocation($locationId, $viewType, $layout, $params);

因此,在我们的视图中,我们直接有装饰器

{% for item in items %}
    {{ ez_render_field(item.content, "title") }}
    {{ decorator.propertyArticle }} 
{% endfor %}

使用特性实现横向可重用性

如果同时使用PHP特性,上述机制会更加强大。以我们章节示例为例,我们将能够编写类似以下内容的东西

interface Chapoable
{
     public function chapo();
}

traits Chapo {
     public function chapo()
     {
         // Notre logique pour construire un chapo ...
     }
}

class Article implements Chapoable {
     use Chapo;
}

class Page implements Chapoable
{
     use Chapo;
}
{{ article.chapo }}

关于在内容的不同类型中使用字段命名的良好约定,与该包和特性一起。我们可以最大限度地利用代码的可重用性和简洁性,CQFD。