truffo / ez-content-decorator-bundle
您的包的简要描述
Requires
- php: >=5.4.0
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。