kalio / ezobjectwrapperbundle
为您的 eZPlatform/eZPublish5 内容/位置对象提供简易包装
Requires
- php: >=5.4
- ezsystems/ezpublish-kernel: >=5.3|>=2014.03
Requires (Dev)
README
为 eZPlatform / eZPublish 5 开发(≥5.3)提供的 Symfony 扩展包。
由 Kaliop 团队开发。
描述
此扩展包提供了一种简单的模型来组织所有处理 eZPublish 位置和内容对象的“业务逻辑”代码,将其与控制器、命令和其他与框架相关联的应用层分离。
这样,如果您更改了您的内容类型定义之一,您无需在整个代码库中寻找修复程序,而只需在理想情况下更改一个地方即可。
它还试图采用对之前未使用过 eZPublish 的 Symfony 开发者熟悉的模式。注意:如果您是 Symfony 开发者,请阅读下面的《这不是您的爷爷的 ORM》章节,以帮助避免常见错误。
工作原理
该扩展包提供了一个 实体管理器 服务,用于检索存储库服务。对于每种特定的内容类型,使用不同的存储库服务。
存储库 服务用于存储获取实体的逻辑。一个典型的存储库方法可以是例如 getLastTenModified()
。
实体 实例为内容和位置提供延迟加载的包装。它们应该包含从内容字段解码数据的逻辑,以及获取相关实体(而不会过度加载视图控制器并创建新的内核请求)。它们通常 不 配置为 Symfony 服务(例如,它们不是单例)。
开发人员应该为网站上使用的每个内容类型创建一个新的存储库和实体类;最简单的方法是继承现有的类,并添加自定义业务逻辑。
twig 辅助函数
此扩展包还提供了一个 Twig 函数 renderLocation
,它使用视图控制器作为服务,并且不会重新启动 Symfony 内核,以实现更快的执行。
安装
推荐通过 Composer 安装此扩展包。
- 将
kaliop/ezobjectwrapperbundle
包添加到您的 composer.json 文件中
{ "require": { "kaliop/ezobjectwrapperbundle": "~3.0" } }
- 将 eZObjectWrapperBundle 添加到 EzPublishKernel.php
new \Kaliop\eZObjectWrapperBundle\KaliopeZObjectWrapperBundle(),
使用方法
检索实体
$entityManager = $this->container->get('ezobject_wrapper.entity_manager'); $repository = $entityManager->getRepository('article'); $articleEntity = $repository->loadEntityFromLocationId(2); echo 'Article entity 2 is named: ' . $articleEntity->content()->contentInfo->name; echo 'Article entity 2 has parent Id: ' . $articleEntity->location()->parentLocationId;
存储库中还有许多其他方法可用,您可以通过 id、内容、位置、位置 id 和远程 id 检索实体。
在 Twig 中调用实体的方法或属性
{{ ez_field_value(articleEntity.content, 'title') }}
Twig 函数 render_location
此函数渲染位置,而无需使用单独的控制器和重新加载内核
{{ render_location(locationId, 'view_type', {}) }}
创建您自己的实体和存储库
让我们假设您想处理 'newsletter' 内容类型。
-
创建一个实体类
namespace Acme\AcmeBundle\Entity; use Kaliop\eZObjectWrapperBundle\Core\Entity as BaseEntity; class Newsletter extends BaseEntity { }
-
创建一个与之一致的存储库类
namespace Acme\AcmeBundle\Repository; use Kaliop\eZObjectWrapperBundle\Core\Repository as BaseRepository; class Newsletter extends BaseRepository { protected $entityClass = '\Acme\AcmeBundle\Entity\Newsletter'; }
-
将存储库注册到实体管理器
ezobject_wrapper: class_map: newsletter: \Acme\AcmeBundle\Repository\Newsletter
(注意,这是“纯”配置,不必在
parameters
中) -
测试它是否工作
$entityManager = $this->container->get('ezobject_wrapper.entity_manager'); $repository = $entityManager->getRepository('newsletter');
-
向实体和存储库类添加新方法
namespace Acme\AcmeBundle\Repository; use ...; class Newsletter extends BaseRepository { protected $entityClass = '\Acme\AcmeBundle\Entity\Newsletter'; /** * @return \Acme\AcmeBundle\Entity\Newsletter[] */ public function getAllNewsletters() { $query = new Query(); $query->filter = new Criterion\LogicalAnd(array( new Criterion\ContentTypeIdentifier($this->contentTypeIdentifier), new Criterion\Subtree('/1/2/212') // root node where all newsletters are located )); $query->performCount = false; $query->limit = PHP_INT_MAX-1; $query->offset = 0; // A helper method made available from the base repository class return $this->loaddEntitiesFromSearchResults( $this->getSearchService()->findContent($query) ); } } namespace Acme\AcmeBundle\Entity; use ...; class Newsletter extends BaseEntity { protected $issueTypeIdentifier = 'newsletter_issue'; /** * @return \DateTime */ public function getLatestIssueDate() { $query = new Query(); $query->filter = new Criterion\LogicalAnd(array( new Criterion\ContentTypeIdentifier($this->issueTypeIdentifier), new Criterion\Subtree($this->location()->pathString) )); $query->performCount = false; $query->limit = 1; $query->offset = 0; $query->sortClauses = array(new DatePublished(Query::SORT_DESC)); $result = $this->repository->getSearchService()->findContent($query); if (count($result->searchHits) > 0) { $latest = $result->searchHits[0]; return $latest->valueObject->contentInfo->publishedDate; } return new \DateTime("@0"); } }
高级使用
将新的存储库注册为服务
如上所示,我们已经介绍了如何将 PHP 类注册为 Repository。将类注册为仓库的另一种方法是使用带有标签的 Symfony 服务。为了换取更多配置,你获得的主要优势是现在可以将配置设置注入到仓库中。
示例
services: ezobject_wrapper.repository.newsletter: class: \Acme\AcmeBundle\Repository\Newsletter parent: ezobject_wrapper.repository.abstract arguments: - @ezpublish.api.repository - @ezobject_wrapper.entity_manager calls: # Injecting some settings to our custom repository class. E.g. the root path of newsletter contents - [ setSettings, [ { newsletter_location_path: %newsletter_location_path% } ] ] tags: # Tagging the service will make it register with the Entity Manager for the given contentType - { name: ezobject_wrapper.repository, content_type: newsletter } parameters: # Using a parameter allows to easily set different values for different environments for things like location Ids newsletter_location_path: /1/2/212/
然后,在 Acme\AcmeBundle\Repository 类内部,你可以使用 settings 成员
... $query->filter = new Criterion\LogicalAnd(array( new Criterion\ContentTypeIdentifier($this->contentTypeIdentifier), new Criterion\Subtree($this->settings['newsletter_location_path']) // root node where all newsletters are located )); ...
注意:如果你想确保注入到自定义 Repository 中的设置始终有效,你只需实现 validateSettings
方法即可
将配置设置传递到实体中
由于实体类未注册为 Symfony 服务,因此一开始可能难以将设置注入到实体实例中。Repository 类中提供了 enrichEntityAtLoad 方法用于此目的。
namespace Acme\AcmeBundle\Repository; use ...; class Newsletter extends BaseRepository { protected $entityClass = '\Acme\AcmeBundle\Entity\Newsletter'; /** * @return \Acme\AcmeBundle\Entity\Newsletter[] */ protected function enrichEntityAtLoad($entity) { $entity = parent::enrichEntityAtLoad($entity); return $entity->setIssueTypeIdentifier('newsletter_issue'); } } namespace Acme\AcmeBundle\Entity; use ...; class Newsletter extends BaseEntity { protected $issueTypeIdentifier; /** * @return $this */ public function setIssueTypeIdentifier($issueTypeIdentifier) { $this->issueTypeIdentifier = $issueTypeIdentifier; return $this; } }
允许实体生成其位置视图的 URL
对于这种常见场景,提供了特质,可以添加到 Repository 和实体类中
namespace Acme\AcmeBundle\Repository; use ...; class Newsletter extends BaseRepository { use Kaliop\eZObjectWrapperBundle\Core\Traits\RouterInjectingRepository; } namespace Acme\AcmeBundle\Entity; use ...; class Newsletter extends BaseEntity { use Kaliop\eZObjectWrapperBundle\Core\Traits\UrlGeneratingEntity; /** * To be used when absolute urls to this Location have to be generated, and there is no twig template or routing service available * @return string */ public function absoluteUrl() { return $this->getUrlAlias(true); } }
services: ezobject_wrapper.repository.newsletter: class: \Acme\AcmeBundle\Repository\Newsletter parent: ezobject_wrapper.repository.abstract arguments: - @ezpublish.api.repository - @ezobject_wrapper.entity_manager calls: - [ setRouter, [ '@router' ] ] tags: - { name: ezobject_wrapper.repository, content_type: newsletter }
允许实体将富文本字段渲染为 HTML
富文本字段需要 Symfony 服务的帮助将它们的 xml 内容转换为 html。同样,提供了特质,可以添加到 Repository 和实体类中。
namespace Acme\AcmeBundle\Repository; use ...; class Newsletter extends BaseRepository { use \Kaliop\eZObjectWrapperBundle\Core\Traits\RichTextConverterInjectingRepository; } namespace Acme\AcmeBundle\Entity; use ...; class Newsletter extends BaseEntity { use \Kaliop\eZObjectWrapperBundle\Core\Traits\RichTextConvertingEntity; /** * @return string */ public function bodyAsHtml() { return $this->getHtml($this->content()->getField('body')->xml); } }
services: ezobject_wrapper.repository.newsletter: class: \Acme\AcmeBundle\Repository\Newsletter parent: ezobject_wrapper.repository.abstract arguments: - @ezpublish.api.repository - @ezobject_wrapper.entity_manager calls: - [ setRichTextConverter, [ '@ezpublish.fieldType.ezxmltext.converter.html5' ] ] tags: - { name: ezobject_wrapper.repository, content_type: newsletter }
允许实体获取相关实体
一个常见用例是,给定一个实体实例,你想要获取其相关对象(s)作为实体。也有适用于此情况的特质。
Kaliop\eZObjectWrapperBundle\Core\Traits\RelationTraversingEntity
只需将其添加到你的实体类中,你就可以使用 2 个新方法来检索其对象关系字段的内容
$relatedEntity = $this->getRelation('fieldName');
$relatedEntitiesArray = $this->getRelations('anotherFieldName');
对缓存的影响(即不要自己伤害自己)
当使用 render_location twig 辅助函数或使用在辅助方法中获取其他内容对象的实体子类时,请注意你正在引入缓存依赖关系。
eZPublish 默认会竭尽全力确保在对象被编辑时,保持内容对象“HTML 版本”的缓存会自动过期。可以说,视图缓存过期是使用 Symfony 子请求显示内容对象列表的主要原因。
每次你显示的不是当前的位置时,请记得将它的 Id 添加到主控制器响应中的 X-Location-Id 标头,这样 eZPublish 就会知道何时清除其缓存。
有关更多详细信息,请参阅:https://doc.ez.no/display/EZP/HttpCache
这不是你爷爷的 ORM
eZObjectWrapper 中的 EntityManager、Repository 和实体类与 Doctrine ORM 中的对应类只有模糊的相似之处。请不要假设你可以以相同的方式使用它们。
例如,目前
- 实体无法持久化,除非使用 eZPublish 仓库 API
- 没有创建未附加到实体管理器的实体的概念
- 没有 DQL 或其他等效语言
- 唯一的查询构建器是来自 eZPublish 仓库 API 的