Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IBX-5905: Implemented LoadContent events #250

Open
wants to merge 8 commits into
base: 4.6
Choose a base branch
from
7 changes: 7 additions & 0 deletions src/contracts/Persistence/Content/ContentInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class ContentInfo extends ValueObject
*/
public $contentTypeId;

public string $contentTypeIdentifier;

/**
* Section id the content is assigned to.
*
Expand Down Expand Up @@ -134,6 +136,11 @@ class ContentInfo extends ValueObject
* @var bool
*/
public $isHidden = false;

public function getContentTypeIdentifier(): string
{
return $this->contentTypeIdentifier;
}
}

class_alias(ContentInfo::class, 'eZ\Publish\SPI\Persistence\Content\ContentInfo');
90 changes: 90 additions & 0 deletions src/contracts/Repository/Events/Content/BeforeLoadContentEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Contracts\Core\Repository\Events\Content;

use Ibexa\Contracts\Core\Repository\Event\BeforeEvent;
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
use UnexpectedValueException;

final class BeforeLoadContentEvent extends BeforeEvent
{
private int $contentId;

/** @var string[]|null */
private ?array $languages;

private ?int $versionNo;

private bool $useAlwaysAvailable;

private ?Content $content = null;

/**
* @param string[] $languages
*/
public function __construct(
int $contentId,
array $languages = null,
?int $versionNo = null,
bool $useAlwaysAvailable = true
) {
$this->contentId = $contentId;
$this->languages = $languages;
$this->versionNo = $versionNo;
$this->useAlwaysAvailable = $useAlwaysAvailable;
}

public function getContentId(): int
{
return $this->contentId;
}

/**
* @return string[]|null
*/
public function getLanguages(): ?array
{
return $this->languages;
}

public function getVersionNo(): ?int
{
return $this->versionNo;
}

public function getUseAlwaysAvailable(): bool
{
return $this->useAlwaysAvailable;
}

public function getContent(): Content
{
if (!$this->hasContent()) {
throw new UnexpectedValueException(
sprintf(
'Return value is not set or not of type %s. Check hasContent() or set it using setContent() before you call the getter.',
Content::class
)
);
}

return $this->content;
}

public function setContent(?Content $content): void
{
$this->content = $content;
}

/** @phpstan-assert-if-true !null $this->content */
public function hasContent(): bool
{
return $this->content instanceof Content;
}
}
26 changes: 26 additions & 0 deletions src/lib/Event/ContentService.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Ibexa\Contracts\Core\Repository\Events\Content\BeforeDeleteTranslationEvent;
use Ibexa\Contracts\Core\Repository\Events\Content\BeforeDeleteVersionEvent;
use Ibexa\Contracts\Core\Repository\Events\Content\BeforeHideContentEvent;
use Ibexa\Contracts\Core\Repository\Events\Content\BeforeLoadContentEvent;
use Ibexa\Contracts\Core\Repository\Events\Content\BeforePublishVersionEvent;
use Ibexa\Contracts\Core\Repository\Events\Content\BeforeRevealContentEvent;
use Ibexa\Contracts\Core\Repository\Events\Content\BeforeUpdateContentEvent;
Expand Down Expand Up @@ -380,6 +381,31 @@ public function revealContent(ContentInfo $contentInfo): void
new RevealContentEvent(...$eventData)
);
}

public function loadContent(
int $contentId,
array $languages = null,
?int $versionNo = null,
bool $useAlwaysAvailable = true
): Content {
$eventData = [
$contentId,
$languages,
$versionNo,
$useAlwaysAvailable,
];

$beforeEvent = new BeforeLoadContentEvent(...$eventData);

$this->eventDispatcher->dispatch($beforeEvent);
if ($beforeEvent->isPropagationStopped()) {
return $beforeEvent->getContent();
}

return $beforeEvent->hasContent()
? $beforeEvent->getContent()
: $this->innerService->loadContent($contentId, $languages, $versionNo, $useAlwaysAvailable);
}
}

class_alias(ContentService::class, 'eZ\Publish\Core\Event\ContentService');
19 changes: 15 additions & 4 deletions src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler;
use Ibexa\Contracts\Core\Persistence\Content\MetadataUpdateStruct;
use Ibexa\Contracts\Core\Persistence\Content\Relation\CreateStruct as RelationCreateStruct;
use Ibexa\Contracts\Core\Persistence\Content\Type;
use Ibexa\Contracts\Core\Persistence\Content\UpdateStruct;
use Ibexa\Contracts\Core\Persistence\Content\VersionInfo;
use Ibexa\Contracts\Core\Repository\Values\Content\Relation;
Expand Down Expand Up @@ -769,14 +770,15 @@ private function internalLoadContent(
'a.data_text AS ezcontentobject_attribute_data_text',
'a.sort_key_int AS ezcontentobject_attribute_sort_key_int',
'a.sort_key_string AS ezcontentobject_attribute_sort_key_string',
't.main_node_id AS ezcontentobject_tree_main_node_id'
't.main_node_id AS ezcontentobject_tree_main_node_id',
'ct.identifier AS content_type_identifier',
)
->from('ezcontentobject', 'c')
->innerJoin(
'c',
'ezcontentobject_version',
'v',
$expr->andX(
$expr->and(
$expr->eq('c.id', 'v.contentobject_id'),
$expr->eq('v.version', $version ?? 'c.current_version')
)
Expand All @@ -785,7 +787,7 @@ private function internalLoadContent(
'v',
'ezcontentobject_attribute',
'a',
$expr->andX(
$expr->and(
$expr->eq('v.contentobject_id', 'a.contentobject_id'),
$expr->eq('v.version', 'a.version')
)
Expand All @@ -794,10 +796,19 @@ private function internalLoadContent(
'c',
'ezcontentobject_tree',
't',
$expr->andX(
$expr->and(
$expr->eq('c.id', 't.contentobject_id'),
$expr->eq('t.node_id', 't.main_node_id')
)
)
->innerJoin(
'c',
'ezcontentclass',
'ct',
$expr->and(
$expr->eq('c.contentclass_id', 'ct.id'),
$expr->eq('ct.version', Type::STATUS_DEFINED)
)
);

$queryBuilder->where(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Query\QueryBuilder as DoctrineQueryBuilder;
use Ibexa\Contracts\Core\Persistence\Content\Type;
use Ibexa\Core\Persistence\Legacy\Content\Gateway;
use function time;

Expand Down Expand Up @@ -113,13 +114,26 @@ public function createLoadContentInfoQueryBuilder(
}

$queryBuilder
->select('c.*', 't.main_node_id AS ezcontentobject_tree_main_node_id')
->select(
'c.*',
't.main_node_id AS ezcontentobject_tree_main_node_id',
'ct.identifier AS content_type_identifier'
)
->from(Gateway::CONTENT_ITEM_TABLE, 'c')
->leftJoin(
'c',
'ezcontentobject_tree',
't',
$joinCondition
)
->innerJoin(
'c',
'ezcontentclass',
'ct',
$expr->and(
$expr->eq('c.contentclass_id', 'ct.id'),
$expr->eq('ct.version', Type::STATUS_DEFINED)
)
);

return $queryBuilder;
Expand Down Expand Up @@ -163,7 +177,8 @@ public function createVersionInfoFindQueryBuilder(): DoctrineQueryBuilder
'c.status AS ezcontentobject_status',
'c.name AS ezcontentobject_name',
'c.language_mask AS ezcontentobject_language_mask',
'c.is_hidden AS ezcontentobject_is_hidden'
'c.is_hidden AS ezcontentobject_is_hidden',
'ct.identifier AS content_type_identifier'
)
->from(Gateway::CONTENT_VERSION_TABLE, 'v')
->innerJoin(
Expand All @@ -180,6 +195,15 @@ public function createVersionInfoFindQueryBuilder(): DoctrineQueryBuilder
$expr->eq('t.contentobject_id', 'v.contentobject_id'),
$expr->eq('t.main_node_id', 't.node_id')
)
)
->innerJoin(
'c',
'ezcontentclass',
'ct',
$expr->and(
$expr->eq('c.contentclass_id', 'ct.id'),
$expr->eq('ct.version', Type::STATUS_DEFINED)
)
);

return $query;
Expand Down
8 changes: 6 additions & 2 deletions src/lib/Persistence/Legacy/Content/Mapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -370,12 +370,16 @@ private function loadCachedVersionFieldDefinitionsPerLanguage(
*
* @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo
*/
public function extractContentInfoFromRow(array $row, $prefix = '', $treePrefix = 'ezcontentobject_tree_')
{
public function extractContentInfoFromRow(
array $row,
$prefix = '',
$treePrefix = 'ezcontentobject_tree_'
) {
$contentInfo = new ContentInfo();
$contentInfo->id = (int)$row["{$prefix}id"];
$contentInfo->name = (string)$row["{$prefix}name"];
$contentInfo->contentTypeId = (int)$row["{$prefix}contentclass_id"];
$contentInfo->contentTypeIdentifier = (string)$row['content_type_identifier'];
$contentInfo->sectionId = (int)$row["{$prefix}section_id"];
$contentInfo->currentVersionNo = (int)$row["{$prefix}current_version"];
$contentInfo->ownerId = (int)$row["{$prefix}owner_id"];
Expand Down
15 changes: 13 additions & 2 deletions src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Doctrine\DBAL\Query\QueryBuilder;
use Ibexa\Contracts\Core\Persistence\Content\ContentInfo;
use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler;
use Ibexa\Contracts\Core\Persistence\Content\Type;
use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion;
use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo;
use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway;
Expand Down Expand Up @@ -216,8 +217,9 @@ private function getContentInfoList(
array $languageFilter
): array {
$query = $this->connection->createQueryBuilder();
$expr = $query->expr();
$query->select(
'DISTINCT c.*, main_tree.main_node_id AS main_tree_main_node_id',
'DISTINCT c.*, main_tree.main_node_id AS main_tree_main_node_id, ct.identifier AS content_type_identifier',
);

if ($sort !== null) {
Expand All @@ -236,10 +238,19 @@ private function getContentInfoList(
'c',
LocationGateway::CONTENT_TREE_TABLE,
'main_tree',
$query->expr()->andX(
$expr->and(
'main_tree.contentobject_id = c.id',
'main_tree.main_node_id = main_tree.node_id'
)
)
->innerJoin(
'c',
'ezcontentclass',
'ct',
$expr->and(
$expr->eq('c.contentclass_id', 'ct.id'),
$expr->eq('ct.version', Type::STATUS_DEFINED)
)
);

if ($sort !== null) {
Expand Down
7 changes: 5 additions & 2 deletions tests/lib/Event/AbstractServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@

abstract class AbstractServiceTest extends TestCase
{
public function getEventDispatcher(string $beforeEventName, string $eventName): TraceableEventDispatcher
public function getEventDispatcher(string $beforeEventName, ?string $eventName): TraceableEventDispatcher
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a feeling this is here on purpose so you won't forget to implement a pair of events - before event and event.
Any POV @Nattfarinn or @ibexa/php-dev?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't add the after event for the loadContent function here, but I will add it if necessary :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say event layer is complete only if it dispatches Before and After events. Before for pipeline interception and input refinement, After as a reaction and postprocessing.

I would add After event. Thanks @alongosz for catching this 👍 .

{
$eventDispatcher = new EventDispatcher();
$eventDispatcher->addListener($beforeEventName, static function (BeforeEvent $event) {});
$eventDispatcher->addListener($eventName, static function (AfterEvent $event) {});
if ($eventName !== null) {
$eventDispatcher->addListener($eventName, static function (AfterEvent $event) {
});
}

return new TraceableEventDispatcher(
$eventDispatcher,
Expand Down
Loading
Loading