From 3e143a0eb6ecee0aba610728c7a9dfa79b637556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Tue, 19 Sep 2023 12:39:03 +0200 Subject: [PATCH] IBX-6282: Added support for custom name schema attributes (#257) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For more details see https://issues.ibexa.co/browse/IBX-6282 and https://github.com/ibexa/core/pull/257 Added support for custom attributes when generating content name schema. Key changes: * Added two new events: * `\Ibexa\Contracts\Core\Event\NameSchema\ResolveNameSchemaEvent` * `\Ibexa\Contracts\Core\Event\NameSchema\ResolveContentNameSchemaEvent` * Changed new `NameSchemaService` method names: * `resolve` => `resolveNameSchema` * `resolveNameSchema` => `resolveContentNameSchema` * Kept BC behavior on legacy `NameSchemaService` helper class --------- Co-Authored-By: Paweł Niedzielski Co-Authored-By: Andrew Longosz --- phpstan-baseline.neon | 126 +------------ .../NameSchema/AbstractNameSchemaEvent.php | 56 ++++++ .../Event/NameSchema/AbstractSchemaEvent.php | 52 ++++++ .../NameSchema/ContentAwareEventInterface.php | 16 ++ .../ResolveContentNameSchemaEvent.php | 38 ++++ .../NameSchema/ResolveNameSchemaEvent.php | 13 ++ .../NameSchema/ResolveUrlAliasSchemaEvent.php | 32 ++++ .../Event/ResolveUrlAliasSchemaEvent.php | 53 ------ .../NameSchema/NameSchemaServiceInterface.php | 22 ++- .../SchemaIdentifierExtractorInterface.php | 2 +- src/lib/Repository/ContentService.php | 6 +- .../EventSubscriber/NameSchemaSubscriber.php | 161 ++++++++++++++--- .../Repository/Helper/NameSchemaService.php | 168 +++++++++++++++++- .../NameSchema/NameSchemaService.php | 168 ++++++------------ .../NameSchema/SchemaIdentifierExtractor.php | 2 +- .../UnresolvedTokenNamesException.php | 15 ++ .../repository/inner/name_schema.yaml | 4 + .../NameSchema/NameSchemaServiceTest.php | 143 ++++++++++----- .../SchemaIdentifierExtractorTest.php | 9 + .../Repository/Service/Mock/ContentTest.php | 4 +- 20 files changed, 710 insertions(+), 380 deletions(-) create mode 100644 src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php create mode 100644 src/contracts/Event/NameSchema/AbstractSchemaEvent.php create mode 100644 src/contracts/Event/NameSchema/ContentAwareEventInterface.php create mode 100644 src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php create mode 100644 src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php create mode 100644 src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php delete mode 100644 src/contracts/Event/ResolveUrlAliasSchemaEvent.php create mode 100644 src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 09579e6f87..b0e87ef612 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -5035,31 +5035,6 @@ parameters: count: 1 path: src/contracts/Container/Encore/ConfigurationDumper.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Event\\\\ResolveUrlAliasSchemaEvent\\:\\:__construct\\(\\) has parameter \\$schemaIdentifiers with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Event/ResolveUrlAliasSchemaEvent.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Event\\\\ResolveUrlAliasSchemaEvent\\:\\:getSchemaIdentifiers\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Event/ResolveUrlAliasSchemaEvent.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Event\\\\ResolveUrlAliasSchemaEvent\\:\\:getTokenValues\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Event/ResolveUrlAliasSchemaEvent.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Event\\\\ResolveUrlAliasSchemaEvent\\:\\:setTokenValues\\(\\) has parameter \\$names with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Event/ResolveUrlAliasSchemaEvent.php - - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Event\\\\ResolveUrlAliasSchemaEvent\\:\\:\\$schemaIdentifiers type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Event/ResolveUrlAliasSchemaEvent.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\BinaryBase\\\\PathGenerator\\:\\:getStoragePathForField\\(\\) has no return type specified\\.$#" count: 1 @@ -7015,11 +6990,6 @@ parameters: count: 1 path: src/contracts/Repository/LocationService.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceInterface\\:\\:resolveNameSchema\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php - - message: "#^PHPDoc tag @param for parameter \\$objectStateGroupId with type mixed is not subtype of native type int\\.$#" count: 1 @@ -20245,16 +20215,6 @@ parameters: count: 1 path: src/lib/Repository/ContentService.php - - - message: "#^Parameter \\#2 \\$fieldMap of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceInterface\\:\\:resolveNameSchema\\(\\) expects array\\\\>, array\\\\> given\\.$#" - count: 1 - path: src/lib/Repository/ContentService.php - - - - message: "#^Parameter \\#3 \\$fieldMap of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceInterface\\:\\:resolve\\(\\) expects array\\\\>, array\\\\> given\\.$#" - count: 1 - path: src/lib/Repository/ContentService.php - - message: "#^Parameter \\#3 \\$prioritizedLanguages of method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildContentDomainObjectFromPersistence\\(\\) expects array\\, array\\\\|null given\\.$#" count: 1 @@ -20336,12 +20296,12 @@ parameters: path: src/lib/Repository/ContentTypeService.php - - message: "#^Parameter \\#1 \\$value of method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:getName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value, Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null given\\.$#" + message: "#^Cannot access property \\$fieldTypeIdentifier on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" count: 1 - path: src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php + path: src/lib/Repository/Helper/NameSchemaService.php - - message: "#^Parameter \\#3 \\$fieldMap of method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:resolve\\(\\) expects array\\\\>, array\\ given\\.$#" + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:getName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" count: 1 path: src/lib/Repository/Helper/NameSchemaService.php @@ -20645,16 +20605,6 @@ parameters: count: 1 path: src/lib/Repository/Mapper/RoleDomainMapper.php - - - message: "#^Cannot access offset mixed on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - - - message: "#^Cannot access property \\$fieldTypeIdentifier on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:extractTokens\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -20665,26 +20615,6 @@ parameters: count: 1 path: src/lib/Repository/NameSchema/NameSchemaService.php - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:mergeFieldMap\\(\\) has parameter \\$fieldMap with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:mergeFieldMap\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:mergeFieldMap\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:resolveNameSchema\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:resolveToken\\(\\) has parameter \\$groupLookupTable with no value type specified in iterable type array\\.$#" count: 1 @@ -20705,26 +20635,11 @@ parameters: count: 1 path: src/lib/Repository/NameSchema/NameSchemaService.php - - - message: "#^Parameter \\#1 \\$value of method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:getName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value, string given\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - - - message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:getName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - message: "#^Property Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" count: 1 path: src/lib/Repository/NameSchema/NameSchemaService.php - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\SchemaIdentifierExtractor\\:\\:extract\\(\\) should return array\\\\> but returns array\\\\>\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/SchemaIdentifierExtractor.php - - message: "#^Parameter \\#1 \\$module of class Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\UnauthorizedException constructor expects string, int\\\\|int\\<1, max\\> given\\.$#" count: 1 @@ -58210,41 +58125,6 @@ parameters: count: 2 path: tests/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationServiceTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceTest\\:\\:getDataForTestResolve\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Repository/NameSchema/NameSchemaServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceTest\\:\\:getDataForTestResolveNameSchema\\(\\) return type has no value type specified in iterable type iterable\\.$#" - count: 1 - path: tests/lib/Repository/NameSchema/NameSchemaServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceTest\\:\\:getEventDispatcherMock\\(\\) has parameter \\$tokenValues with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Repository/NameSchema/NameSchemaServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceTest\\:\\:testResolve\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Repository/NameSchema/NameSchemaServiceTest.php - - - - message: "#^PHPDoc tag @return contains unresolvable type\\.$#" - count: 1 - path: tests/lib/Repository/NameSchema/NameSchemaServiceTest.php - - - - message: "#^Parameter \\#2 \\$content of method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceTest\\:\\:buildNameSchemaService\\(\\) expects Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content given\\.$#" - count: 4 - path: tests/lib/Repository/NameSchema/NameSchemaServiceTest.php - - - - message: "#^Parameter \\#3 \\$fieldMap of method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:resolve\\(\\) expects array\\\\>, array\\ given\\.$#" - count: 1 - path: tests/lib/Repository/NameSchema/NameSchemaServiceTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\PHPUnitConstraint\\\\AllValidationErrorsOccur\\:\\:extractTranslatable\\(\\) has parameter \\$fieldErrors with no value type specified in iterable type array\\.$#" count: 1 diff --git a/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php new file mode 100644 index 0000000000..35e59cb927 --- /dev/null +++ b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php @@ -0,0 +1,56 @@ +> */ + private array $fieldMap; + + /** @var array */ + private array $languageCodes; + + /** + * @param array> $schemaIdentifiers + * @param array> $fieldMap + * @param array $languageCodes + */ + public function __construct( + array $schemaIdentifiers, + ContentType $contentType, + array $fieldMap, + array $languageCodes + ) { + parent::__construct($schemaIdentifiers); + $this->contentType = $contentType; + $this->fieldMap = $fieldMap; + $this->languageCodes = $languageCodes; + } + + public function getContentType(): ContentType + { + return $this->contentType; + } + + /** @return array> */ + public function getFieldMap(): array + { + return $this->fieldMap; + } + + /** @return array */ + public function getLanguageCodes(): array + { + return $this->languageCodes; + } +} diff --git a/src/contracts/Event/NameSchema/AbstractSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractSchemaEvent.php new file mode 100644 index 0000000000..869ecc0d90 --- /dev/null +++ b/src/contracts/Event/NameSchema/AbstractSchemaEvent.php @@ -0,0 +1,52 @@ +> */ + private array $schemaIdentifiers; + + /** @var array> */ + private array $tokenValues = []; + + /** + * @param array> $schemaIdentifiers + */ + public function __construct(array $schemaIdentifiers) + { + $this->schemaIdentifiers = $schemaIdentifiers; + } + + /** + * @return array> + */ + final public function getTokenValues(): array + { + return $this->tokenValues; + } + + /** + * @param array> $names + */ + final public function setTokenValues(array $names): void + { + $this->tokenValues = $names; + } + + /** + * @return array> + */ + public function getSchemaIdentifiers(): array + { + return $this->schemaIdentifiers; + } +} diff --git a/src/contracts/Event/NameSchema/ContentAwareEventInterface.php b/src/contracts/Event/NameSchema/ContentAwareEventInterface.php new file mode 100644 index 0000000000..18072e6854 --- /dev/null +++ b/src/contracts/Event/NameSchema/ContentAwareEventInterface.php @@ -0,0 +1,16 @@ +> $schemaIdentifiers + * @param array> $fieldMap + * @param array $languageCodes + */ + public function __construct( + Content $content, + array $schemaIdentifiers, + ContentType $contentType, + array $fieldMap, + array $languageCodes + ) { + parent::__construct($schemaIdentifiers, $contentType, $fieldMap, $languageCodes); + $this->content = $content; + } + + public function getContent(): Content + { + return $this->content; + } +} diff --git a/src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php new file mode 100644 index 0000000000..d74df806ca --- /dev/null +++ b/src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php @@ -0,0 +1,13 @@ +> $schemaIdentifiers + */ + public function __construct( + array $schemaIdentifiers, + Content $content + ) { + parent::__construct($schemaIdentifiers); + $this->content = $content; + } + + public function getContent(): Content + { + return $this->content; + } +} diff --git a/src/contracts/Event/ResolveUrlAliasSchemaEvent.php b/src/contracts/Event/ResolveUrlAliasSchemaEvent.php deleted file mode 100644 index 3f75eb3463..0000000000 --- a/src/contracts/Event/ResolveUrlAliasSchemaEvent.php +++ /dev/null @@ -1,53 +0,0 @@ - */ - private array $schemaIdentifiers; - - private Content $content; - - /** - * @var array> - */ - private array $names = []; - - public function __construct( - array $schemaIdentifiers, - Content $content - ) { - $this->schemaIdentifiers = $schemaIdentifiers; - $this->content = $content; - } - - public function getSchemaIdentifiers(): array - { - return $this->schemaIdentifiers; - } - - public function getContent(): Content - { - return $this->content; - } - - public function getTokenValues(): array - { - return $this->names; - } - - public function setTokenValues(array $names): void - { - $this->names = $names; - } -} diff --git a/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php b/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php index 1d27bb149e..05920c32d7 100644 --- a/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php +++ b/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php @@ -21,15 +21,18 @@ interface NameSchemaServiceInterface /** * @return array key value map of names for a language code */ - public function resolveUrlAliasSchema(Content $content, ContentType $contentType = null): array; + public function resolveUrlAliasSchema( + Content $content, + ContentType $contentType = null + ): array; /** - * @param array> $fieldMap + * @param array> $fieldMap * @param array $languageCodes * - * @return array + * @return array */ - public function resolveNameSchema( + public function resolveContentNameSchema( Content $content, array $fieldMap = [], array $languageCodes = [], @@ -39,12 +42,17 @@ public function resolveNameSchema( /** * Returns the real name for a content name pattern. * - * @param array> $fieldMap + * @param array> $fieldMap * @param array $languageCodes * - * @return array + * @return array * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ - public function resolve(string $nameSchema, ContentType $contentType, array $fieldMap, array $languageCodes): array; + public function resolveNameSchema( + string $nameSchema, + ContentType $contentType, + array $fieldMap, + array $languageCodes + ): array; } diff --git a/src/contracts/Repository/NameSchema/SchemaIdentifierExtractorInterface.php b/src/contracts/Repository/NameSchema/SchemaIdentifierExtractorInterface.php index e86980e817..51cc0d4a46 100644 --- a/src/contracts/Repository/NameSchema/SchemaIdentifierExtractorInterface.php +++ b/src/contracts/Repository/NameSchema/SchemaIdentifierExtractorInterface.php @@ -11,7 +11,7 @@ interface SchemaIdentifierExtractorInterface { /** - * @return array> + * @return array> */ public function extract(string $schemaString): array; } diff --git a/src/lib/Repository/ContentService.php b/src/lib/Repository/ContentService.php index 0f88850e33..b02bc75a8f 100644 --- a/src/lib/Repository/ContentService.php +++ b/src/lib/Repository/ContentService.php @@ -739,7 +739,7 @@ public function createContent(APIContentCreateStruct $contentCreateStruct, array $spiContentCreateStruct = new SPIContentCreateStruct( [ - 'name' => $this->nameSchemaService->resolve( + 'name' => $this->nameSchemaService->resolveNameSchema( $contentCreateStruct->contentType->nameSchema, $contentCreateStruct->contentType, $fieldValues, @@ -1406,7 +1406,7 @@ protected function internalUpdateContent( $spiContentUpdateStruct = new SPIContentUpdateStruct( [ - 'name' => $this->nameSchemaService->resolveNameSchema( + 'name' => $this->nameSchemaService->resolveContentNameSchema( $content, $fieldValues, $allLanguageCodes, @@ -1565,7 +1565,7 @@ protected function copyNonTranslatableFieldsFromPublishedVersion(APIContent $cur } $updateStruct = new SPIContentUpdateStruct(); - $updateStruct->name = $this->nameSchemaService->resolveNameSchema( + $updateStruct->name = $this->nameSchemaService->resolveContentNameSchema( $currentVersionContent, $fieldValues, $versionInfo->languageCodes, diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 89bbfc8615..63adea9b31 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -8,7 +8,13 @@ namespace Ibexa\Core\Repository\EventSubscriber; -use Ibexa\Contracts\Core\Event\ResolveUrlAliasSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\AbstractSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveContentNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveUrlAliasSchemaEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; use Ibexa\Core\FieldType\FieldTypeRegistry; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -27,49 +33,150 @@ public function __construct(FieldTypeRegistry $fieldTypeRegistry) public static function getSubscribedEvents(): array { return [ + ResolveNameSchemaEvent::class => [ + ['onResolveNameSchema', -100], + ], + ResolveContentNameSchemaEvent::class => [ + ['onResolveContentNameSchema', -100], + ], ResolveUrlAliasSchemaEvent::class => [ ['onResolveUrlAliasSchema', -100], ], ]; } - /** - * Resolves the URL alias schema by setting token values for specified field identifiers and languages. - * - * @param \Ibexa\Contracts\Core\Event\ResolveUrlAliasSchemaEvent $event - */ + public function onResolveNameSchema(ResolveNameSchemaEvent $event): void + { + if (!$this->isValid($event)) { + return; + } + + $tokenValues = $this->processEvent( + $event->getLanguageCodes(), + $event->getSchemaIdentifiers()['field'], + $event->getContentType(), + null, + $event->getTokenValues(), + $event->getFieldMap() + ); + + $event->setTokenValues($tokenValues); + } + + public function onResolveContentNameSchema(ResolveContentNameSchemaEvent $event): void + { + if (!$this->isValid($event)) { + return; + } + + $tokenValues = $this->processEvent( + $event->getLanguageCodes(), + $event->getSchemaIdentifiers()['field'], + $event->getContentType(), + $event->getContent(), + $event->getTokenValues(), + $event->getFieldMap() + ); + + $event->setTokenValues($tokenValues); + } + public function onResolveUrlAliasSchema(ResolveUrlAliasSchemaEvent $event): void { - if (!array_key_exists('field', $event->getSchemaIdentifiers())) { + if (!$this->isValid($event)) { return; } - $content = $event->getContent(); - $identifiers = $event->getSchemaIdentifiers()['field']; - $languages = $event->getContent()->getVersionInfo()->getLanguages(); - $tokenValues = $event->getTokenValues(); + $languageList = array_map( + static fn (Language $language): string => $language->getLanguageCode(), + (array)$event->getContent()->getVersionInfo()->getLanguages() + ); + $content = $event->getContent(); $contentType = $content->getContentType(); - foreach ($languages as $language) { - $languageCode = $language->getLanguageCode(); - foreach ($identifiers as $identifier) { - $fieldDefinition = $contentType->getFieldDefinition($identifier); - if (null === $fieldDefinition) { - continue; - } - $persistenceFieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldTypeIdentifier); - - $fieldValue = $content->getFieldValue($identifier, $languageCode); - $fieldValue = $persistenceFieldType->getName( - $fieldValue, - $fieldDefinition, + $tokenValues = $this->processEvent( + $languageList, + $event->getSchemaIdentifiers()['field'], + $contentType, + $content, + $event->getTokenValues() + ); + + $event->setTokenValues($tokenValues); + } + + public function isValid(AbstractSchemaEvent $event): bool + { + return array_key_exists('field', $event->getSchemaIdentifiers()); + } + + /** + * @param array $languages + * @param array $identifiers + * @param array> $tokenValues + * @param array> $fieldMap + * + * @return array> + */ + private function processEvent( + array $languages, + array $identifiers, + ContentType $contentType, + ?Content $content, + array $tokenValues, + array $fieldMap = [] + ): array { + foreach ($languages as $languageCode) { + $values = $content !== null || !empty($fieldMap) + ? $this->getValues( + $identifiers, + $contentType, + $content, + $fieldMap, $languageCode - ); + ) + : []; + $tokenValues[$languageCode] = array_merge($tokenValues[$languageCode] ?? [], $values); + } + + return $tokenValues; + } - $tokenValues[$languageCode][$identifier] = $fieldValue; + /** + * @param array $identifiers + * @param array> $fieldMap + * + * @return array + */ + private function getValues( + array $identifiers, + ContentType $contentType, + ?Content $content, + array $fieldMap, + string $languageCode + ): array { + $tokenValues = []; + foreach ($identifiers as $identifier) { + $fieldDefinition = $contentType->getFieldDefinition($identifier); + if (null === $fieldDefinition) { + continue; } + + $persistenceFieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldTypeIdentifier); + + if (!empty($fieldMap)) { + $fieldValue = $fieldMap[$identifier][$languageCode] ?? null; + } else { + $fieldValue = $content !== null ? $content->getFieldValue($identifier, $languageCode) : null; + } + + $tokenValues[$identifier] = $fieldValue !== null ? $persistenceFieldType->getName( + $fieldValue, + $fieldDefinition, + $languageCode + ) : ''; } - $event->setTokenValues($tokenValues); + return $tokenValues; } } diff --git a/src/lib/Repository/Helper/NameSchemaService.php b/src/lib/Repository/Helper/NameSchemaService.php index 47682ef83c..20990ffdc8 100644 --- a/src/lib/Repository/Helper/NameSchemaService.php +++ b/src/lib/Repository/Helper/NameSchemaService.php @@ -6,9 +6,16 @@ */ namespace Ibexa\Core\Repository\Helper; +use Ibexa\Contracts\Core\Persistence\Content\Type as SPIContentType; +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as ContentTypeHandler; use Ibexa\Contracts\Core\Repository\Values\Content\Content; use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\FieldTypeRegistry; +use Ibexa\Core\Repository\Mapper\ContentTypeDomainMapper; use Ibexa\Core\Repository\NameSchema\NameSchemaService as NativeNameSchemaService; +use Ibexa\Core\Repository\NameSchema\SchemaIdentifierExtractor; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * @deprecated inject \Ibexa\Contracts\Core\Repository\NameSchema\NameSchemaServiceInterface instead. @@ -16,17 +23,170 @@ */ class NameSchemaService extends NativeNameSchemaService { + protected ContentTypeHandler $contentTypeHandler; + + protected ContentTypeDomainMapper $contentTypeDomainMapper; + + public function __construct( + ContentTypeHandler $contentTypeHandler, + ContentTypeDomainMapper $contentTypeDomainMapper, + FieldTypeRegistry $fieldTypeRegistry, + EventDispatcherInterface $eventDispatcher, + array $settings = [] + ) { + $this->settings = $settings + [ + 'limit' => 150, + 'sequence' => '...', + ]; + + parent::__construct( + $fieldTypeRegistry, + new SchemaIdentifierExtractor(), + $eventDispatcher, + $settings + ); + $this->contentTypeHandler = $contentTypeHandler; + $this->contentTypeDomainMapper = $contentTypeDomainMapper; + } + public function resolveUrlAliasSchema(Content $content, ContentType $contentType = null): array { $contentType = $contentType ?? $content->getContentType(); - return $this->resolve( - empty($contentType->urlAliasSchema) ? $contentType->nameSchema : $contentType->urlAliasSchema, + return $this->resolveUrlAliasSchema( + $content, + $contentType + ); + } + + public function resolveContentNameSchema( + Content $content, + array $fieldMap = [], + array $languageCodes = [], + ContentType $contentType = null + ): array { + $contentType ??= $content->getContentType(); + + $languageCodes = $languageCodes ?: $content->versionInfo->languageCodes; + + return $this->resolveNameSchema( + $contentType->nameSchema, $contentType, - $content->fields, - $content->versionInfo->languageCodes + $this->mergeFieldMap( + $content, + $fieldMap, + $languageCodes + ), + $languageCodes ); } + + public function resolveNameSchema( + string $nameSchema, + ContentType $contentType, + array $fieldMap, + array $languageCodes + ): array { + [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($nameSchema); + $tokens = $this->extractTokens($filteredNameSchema); + $schemaIdentifiers = $this->getIdentifiers($nameSchema); + + $names = []; + + foreach ($languageCodes as $languageCode) { + // Fetch titles for language code + $titles = $this->getFieldTitles($schemaIdentifiers, $contentType, $fieldMap, $languageCode); + $name = $filteredNameSchema; + + // Replace tokens with real values + foreach ($tokens as $token) { + $string = $this->resolveToken($token, $titles, $groupLookupTable); + $name = str_replace($token, $string, $name); + } + $name = $this->validateNameLength($name); + + $names[$languageCode] = $name; + } + + return $names; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param array> $fieldMap + * @param array $languageCodes + * + * @return array> + */ + protected function mergeFieldMap(Content $content, array $fieldMap, array $languageCodes): array + { + $mergedFieldMap = []; + + foreach ($content->fields as $fieldIdentifier => $fieldLanguageMap) { + foreach ($languageCodes as $languageCode) { + $mergedFieldMap[$fieldIdentifier][$languageCode] = $fieldMap[$fieldIdentifier][$languageCode]; + } + } + + return $mergedFieldMap; + } + + /** + * Fetches the list of available Field identifiers in the token and returns + * an array of their current title value. + * + * @param array $schemaIdentifiers + * @param \Ibexa\Contracts\Core\Persistence\Content\Type|\Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * @param array> $fieldMap + * @param string $languageCode + * + * @return array Key is the field identifier, value is the title value + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType + * + * @see \Ibexa\Core\Repository\Values\ContentType\FieldType::getName() + */ + protected function getFieldTitles(array $schemaIdentifiers, $contentType, array $fieldMap, $languageCode): array + { + $fieldTitles = []; + + foreach ($schemaIdentifiers as $fieldDefinitionIdentifier) { + if (!isset($fieldMap[$fieldDefinitionIdentifier][$languageCode])) { + continue; + } + if ($contentType instanceof SPIContentType) { + $fieldDefinition = null; + foreach ($contentType->fieldDefinitions as $spiFieldDefinition) { + if ($spiFieldDefinition->identifier === $fieldDefinitionIdentifier) { + $fieldDefinition = $this->contentTypeDomainMapper->buildFieldDefinitionDomainObject( + $spiFieldDefinition, + // This is probably not main language, but as we don't expose it, it's ok for now. + $languageCode + ); + break; + } + } + if ($fieldDefinition === null) { + $fieldTitles[$fieldDefinitionIdentifier] = ''; + continue; + } + } elseif ($contentType instanceof ContentType) { + $fieldDefinition = $contentType->getFieldDefinition($fieldDefinitionIdentifier); + } else { + throw new InvalidArgumentType('$contentType', 'API or SPI variant of a Content Type'); + } + $fieldTypeService = $this->fieldTypeRegistry->getFieldType( + $fieldDefinition->fieldTypeIdentifier + ); + $fieldTitles[$fieldDefinitionIdentifier] = $fieldTypeService->getName( + $fieldMap[$fieldDefinitionIdentifier][$languageCode], + $fieldDefinition, + $languageCode + ); + } + + return $fieldTitles; + } } class_alias(NameSchemaService::class, 'eZ\Publish\Core\Repository\Helper\NameSchemaService'); diff --git a/src/lib/Repository/NameSchema/NameSchemaService.php b/src/lib/Repository/NameSchema/NameSchemaService.php index 591642135d..782a154191 100644 --- a/src/lib/Repository/NameSchema/NameSchemaService.php +++ b/src/lib/Repository/NameSchema/NameSchemaService.php @@ -8,7 +8,9 @@ namespace Ibexa\Core\Repository\NameSchema; -use Ibexa\Contracts\Core\Event\ResolveUrlAliasSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveContentNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveUrlAliasSchemaEvent; use Ibexa\Contracts\Core\Repository\NameSchema\NameSchemaServiceInterface; use Ibexa\Contracts\Core\Repository\NameSchema\SchemaIdentifierExtractorInterface; use Ibexa\Contracts\Core\Repository\Values\Content\Content; @@ -83,145 +85,58 @@ public function resolveUrlAliasSchema(Content $content, ContentType $contentType { $contentType ??= $content->getContentType(); $schemaName = $contentType->urlAliasSchema ?: $contentType->nameSchema; - [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($schemaName); $schemaIdentifiers = $this->schemaIdentifierExtractor->extract($schemaName); - $tokens = $this->extractTokens($filteredNameSchema); - /** @var \Ibexa\Contracts\Core\Event\ResolveUrlAliasSchemaEvent $event */ $event = $this->eventDispatcher->dispatch( new ResolveUrlAliasSchemaEvent( $schemaIdentifiers, $content ) ); - $names = []; - $tokenValues = $event->getTokenValues(); - foreach ($tokenValues as $languageCode => $tokenValue) { - $name = $filteredNameSchema; - foreach ($tokens as $token) { - $string = $this->resolveToken($token, $tokenValue, $groupLookupTable); - $name = str_replace($token, $string, $name); - } - $name = $this->validateNameLength($name); - - $names[$languageCode] = $name; - } - return $names; + return $this->buildNames($event->getTokenValues(), $schemaName); } - public function resolveNameSchema( + public function resolveContentNameSchema( Content $content, array $fieldMap = [], array $languageCodes = [], ContentType $contentType = null ): array { $contentType ??= $content->getContentType(); + $schemaName = $contentType->nameSchema; + $schemaIdentifiers = $this->schemaIdentifierExtractor->extract($schemaName); - $languageCodes = $languageCodes ?: $content->versionInfo->languageCodes; - - return $this->resolve( - $contentType->nameSchema, - $contentType, - $this->mergeFieldMap( + $event = $this->eventDispatcher->dispatch( + new ResolveContentNameSchemaEvent( $content, + $schemaIdentifiers, + $contentType, $fieldMap, $languageCodes - ), - $languageCodes + ) ); - } - - /** - * Convenience method for resolving name schema. - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content - * @param array $fieldMap - * @param array $languageCodes - * - * @return array - */ - protected function mergeFieldMap(Content $content, array $fieldMap, array $languageCodes): array - { - if (empty($fieldMap)) { - return $content->fields; - } - - $mergedFieldMap = []; - - foreach ($content->fields as $fieldIdentifier => $fieldLanguageMap) { - foreach ($languageCodes as $languageCode) { - $mergedFieldMap[$fieldIdentifier][$languageCode] - = $fieldMap[$fieldIdentifier][$languageCode] ?? $fieldLanguageMap[$languageCode]; - } - } - - return $mergedFieldMap; - } - - public function resolve(string $nameSchema, ContentType $contentType, array $fieldMap, array $languageCodes): array - { - [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($nameSchema); - $tokens = $this->extractTokens($filteredNameSchema); - $schemaIdentifiers = $this->getIdentifiers($nameSchema); - - $names = []; - - foreach ($languageCodes as $languageCode) { - // Fetch titles for language code - $titles = $this->getFieldTitles($schemaIdentifiers, $contentType, $fieldMap, $languageCode); - $name = $filteredNameSchema; - - // Replace tokens with real values - foreach ($tokens as $token) { - $string = $this->resolveToken($token, $titles, $groupLookupTable); - $name = str_replace($token, $string, $name); - } - $name = $this->validateNameLength($name); - - $names[$languageCode] = $name; - } - return $names; + return $this->buildNames($event->getTokenValues(), $schemaName); } - /** - * Fetches the list of available Field identifiers in the token and returns - * an array of their current title value. - * - * @param array $schemaIdentifiers - * @param array> $fieldMap - * - * @return string[] Key is the field identifier, value is the title value - * - * @see \Ibexa\Core\Repository\Values\ContentType\FieldType::getName() - */ - protected function getFieldTitles( - array $schemaIdentifiers, + public function resolveNameSchema( + string $nameSchema, ContentType $contentType, array $fieldMap, - string $languageCode + array $languageCodes ): array { - $fieldTitles = []; - - foreach ($schemaIdentifiers as $fieldDefinitionIdentifier) { - if (!isset($fieldMap[$fieldDefinitionIdentifier][$languageCode])) { - continue; - } - - $fieldDefinition = $contentType->getFieldDefinition($fieldDefinitionIdentifier); - $persistenceFieldType = $this->fieldTypeRegistry->getFieldType( - $fieldDefinition->fieldTypeIdentifier - ); - - $fieldTitles[$fieldDefinitionIdentifier] = $persistenceFieldType->getName( - $fieldMap[$fieldDefinitionIdentifier][$languageCode], - $fieldDefinition, - $languageCode - ); - } + $schemaIdentifiers = $this->schemaIdentifierExtractor->extract($nameSchema); + $event = $this->eventDispatcher->dispatch( + new ResolveNameSchemaEvent( + $schemaIdentifiers, + $contentType, + $fieldMap, + $languageCodes + ) + ); - return $fieldTitles; + return $this->buildNames($event->getTokenValues(), $nameSchema); } /** @@ -355,6 +270,37 @@ protected function filterNameSchema(string $nameSchema): array return [$nameSchema, $groupLookupTable]; } + /** + * @param array> $tokenValues + * + * @return array + */ + public function buildNames(array $tokenValues, string $nameSchema): array + { + if (empty($tokenValues)) { + throw new UnresolvedTokenNamesException('$tokenValues', 'is Empty'); + } + + [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($nameSchema); + $tokens = $this->extractTokens($filteredNameSchema); + + $names = []; + foreach ($tokenValues as $languageCode => $tokenValue) { + $names[$languageCode] = $this->validateNameLength( + str_replace( + $tokens, + array_map( + fn (string $token): string => $this->resolveToken($token, $tokenValue, $groupLookupTable), + $tokens + ), + $filteredNameSchema + ) + ); + } + + return $names; + } + /** * @return array */ diff --git a/src/lib/Repository/NameSchema/SchemaIdentifierExtractor.php b/src/lib/Repository/NameSchema/SchemaIdentifierExtractor.php index d61478fada..032e8ef279 100644 --- a/src/lib/Repository/NameSchema/SchemaIdentifierExtractor.php +++ b/src/lib/Repository/NameSchema/SchemaIdentifierExtractor.php @@ -13,7 +13,7 @@ final class SchemaIdentifierExtractor implements SchemaIdentifierExtractorInterface { /** - * @return array> + * @return array> * * @example * $extractor = new SchemaIdentifierExtractor(); diff --git a/src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php b/src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php new file mode 100644 index 0000000000..090664857e --- /dev/null +++ b/src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php @@ -0,0 +1,15 @@ +buildTestContentObject(); $contentType = $this->buildTestContentTypeStub(); - $nameSchemaService = $this->buildNameSchemaService( - ['field' => ['']], - $content, - ['eng-GB' => ['url_alias_schema' => 'foo']] - ); + $event = new ResolveUrlAliasSchemaEvent(['field' => ['']], $content); + $event->setTokenValues(['eng-GB' => ['url_alias_schema' => 'foo']]); + + $nameSchemaService = $this->buildNameSchemaService($event); $result = $nameSchemaService->resolveUrlAliasSchema($content, $contentType); @@ -52,24 +54,31 @@ public function testResolveUrlAliasSchemaFallbackToNameSchema(): void $content = $this->buildTestContentObject(); $contentType = $this->buildTestContentTypeStub(self::NAME_SCHEMA, ''); - $nameSchemaService = $this->buildNameSchemaService( - ['field' => [self::NAME_SCHEMA]], - $content, - ['eng-GB' => ['name_schema' => 'bar']] - ); + $event = new ResolveUrlAliasSchemaEvent(['field' => [self::NAME_SCHEMA]], $content); + $event->setTokenValues(['eng-GB' => ['name_schema' => 'bar']]); + $nameSchemaService = $this->buildNameSchemaService($event); $result = $nameSchemaService->resolveUrlAliasSchema($content, $contentType); self::assertEquals(['eng-GB' => 'bar'], $result); } /** - * @return iterable>, array, array> + * @return iterable>, + * 1: array>, + * 2: array, + * 3: array + * }> */ public static function getDataForTestResolveNameSchema(): iterable { yield 'Default: Field Map and Languages taken from Content Version' => [ [], + [ + 'eng-GB' => ['text2' => 'two'], + 'cro-HR' => ['text2' => 'dva'], + ], [], [ 'eng-GB' => 'two', @@ -83,6 +92,10 @@ public static function getDataForTestResolveNameSchema(): iterable 'text2' => ['cro-HR' => new TextLineValue('Dva'), 'eng-GB' => new TextLineValue('two')], 'text3' => ['eng-GB' => new TextLineValue('three')], ], + [ + 'eng-GB' => ['text2' => 'two', 'text3' => 'three'], + 'cro-HR' => ['text2' => 'Dva'], + ], ['eng-GB', 'cro-HR'], [ 'eng-GB' => 'three', @@ -94,22 +107,34 @@ public static function getDataForTestResolveNameSchema(): iterable /** * @dataProvider getDataForTestResolveNameSchema * - * @param array> $fieldMap A map of Field Definition Identifier and Language Code to Field Value + * @param array> $fieldMap + * @param array> $tokenValues * @param array $languageCodes * @param array $expectedNames */ - public function testResolveNameSchema(array $fieldMap, array $languageCodes, array $expectedNames): void - { + public function testResolveNameSchema( + array $fieldMap, + array $tokenValues, + array $languageCodes, + array $expectedNames + ): void { $content = $this->buildTestContentObject(); $nameSchema = ''; - $nameSchemaService = $this->buildNameSchemaService( - ['field' => [$nameSchema]], + $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); + $event = new ResolveContentNameSchemaEvent( $content, - [] + ['field' => ['text3', 'text2']], + $contentType, + $fieldMap, + $languageCodes + ); + $event->setTokenValues($tokenValues); + + $nameSchemaService = $this->buildNameSchemaService( + $event ); - $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); - $result = $nameSchemaService->resolveNameSchema($content, $fieldMap, $languageCodes, $contentType); + $result = $nameSchemaService->resolveContentNameSchema($content, $fieldMap, $languageCodes, $contentType); self::assertEquals( $expectedNames, @@ -120,14 +145,28 @@ public function testResolveNameSchema(array $fieldMap, array $languageCodes, arr /** * Data provider for the testResolve method. * + * @return array>, + * 1: string, + * 2: array>, + * 3: array, + * 4: array>, + * 5?: array{limit?: int, sequence?: string} + * }> + * * @see testResolve */ public static function getDataForTestResolve(): array { return [ [ - ['text1'], + ['field' => ['text1']], '', + [ + 'text1' => ['cro-HR' => new TextLineValue('jedan'), 'eng-GB' => new TextLineValue('one')], + 'text2' => ['cro-HR' => new TextLineValue('Dva'), 'eng-GB' => new TextLineValue('two')], + 'text3' => ['eng-GB' => new TextLineValue('three')], +], [ 'eng-GB' => 'one', 'cro-HR' => 'jedan', @@ -138,8 +177,13 @@ public static function getDataForTestResolve(): array ], ], [ - ['text2'], + ['field' => ['text2']], '', + [ + 'text1' => ['cro-HR' => new TextLineValue('jedan'), 'eng-GB' => new TextLineValue('one')], + 'text2' => ['cro-HR' => new TextLineValue('Dva'), 'eng-GB' => new TextLineValue('two')], + 'text3' => ['eng-GB' => new TextLineValue('three')], + ], [ 'eng-GB' => 'two', 'cro-HR' => 'dva', @@ -150,8 +194,13 @@ public static function getDataForTestResolve(): array ], ], [ - ['text1', 'text2'], + ['field' => ['text2', 'text2']], 'Hello, and and then goodbye and hello again', + [ + 'text1' => ['cro-HR' => new TextLineValue('jedan'), 'eng-GB' => new TextLineValue('one')], + 'text2' => ['cro-HR' => new TextLineValue('Dva'), 'eng-GB' => new TextLineValue('two')], + 'text3' => ['eng-GB' => new TextLineValue('three')], + ], [ 'eng-GB' => 'Hello, one and two and then goodbye...', 'cro-HR' => 'Hello, jedan and dva and then goodb...', @@ -171,31 +220,41 @@ public static function getDataForTestResolve(): array /** * @dataProvider getDataForTestResolve * - * @param string[] $schemaIdentifiers - * @param string[] $languageFieldValues field value translations - * @param string[] $fieldTitles [language => [field_identifier => title]] - * @param array $settings NameSchemaService settings + * @param array> $schemaIdentifiers + * @param array $languageFieldValues field value translations + * @param array> $fieldMap + * @param array> $fieldTitles [language => [field_identifier => title]] + * @param array{limit?: int, sequence?: string} $settings NameSchemaService settings */ public function testResolve( array $schemaIdentifiers, string $nameSchema, + array $fieldMap, array $languageFieldValues, array $fieldTitles, array $settings = [] ): void { $content = $this->buildTestContentObject(); + $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); + + $event = new ResolveNameSchemaEvent( + $schemaIdentifiers, + $contentType, + $fieldMap, + $content->versionInfo->languageCodes + ); + + $event->setTokenValues($fieldTitles); + $nameSchemaService = $this->buildNameSchemaService( - ['field' => [$nameSchema]], - $content, - [], + $event, $settings ); - $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); - $result = $nameSchemaService->resolve( + $result = $nameSchemaService->resolveNameSchema( $nameSchema, $contentType, - $content->fields, + $fieldMap, $content->versionInfo->languageCodes ); @@ -295,17 +354,9 @@ protected function buildTestContentTypeStub( ); } - /** - * @param array> $schemaIdentifiers - */ protected function getEventDispatcherMock( - array $schemaIdentifiers, - Content $content, - array $tokenValues + AbstractSchemaEvent $event ): EventDispatcherInterface { - $event = new ResolveUrlAliasSchemaEvent($schemaIdentifiers, $content); - $event->setTokenValues($tokenValues); - $eventDispatcherMock = $this->getEventDispatcher(); $eventDispatcherMock->method('dispatch') ->willReturn($event); @@ -314,14 +365,10 @@ protected function getEventDispatcherMock( } /** - * @param array> $schemaIdentifiers - * @param array> $tokenValues * @param array{limit?: integer, sequence?: string} $settings */ private function buildNameSchemaService( - array $schemaIdentifiers, - Content $content, - array $tokenValues, + AbstractSchemaEvent $event, array $settings = [] ): NameSchemaService { $fieldTypeRegistryMock = $this->getFieldTypeRegistryMock(); @@ -333,7 +380,7 @@ private function buildNameSchemaService( return new NameSchemaService( $fieldTypeRegistryMock, new SchemaIdentifierExtractor(), - $this->getEventDispatcherMock($schemaIdentifiers, $content, $tokenValues), + $this->getEventDispatcherMock($event), $settings ); } diff --git a/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php b/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php index a5bb17e369..29517d557c 100644 --- a/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php +++ b/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php @@ -94,6 +94,15 @@ public function getDataForTestExtract(): iterable 'attribute' => ['mouse_type', 'mouse_weight'], ], ]; + + $schemaString = ' )> )>'; + yield $schemaString => [ + $schemaString, + [ + 'field' => ['abc', 'xyz', 'name'], + 'attribute' => ['color'], + ], + ]; } protected function setUp(): void diff --git a/tests/lib/Repository/Service/Mock/ContentTest.php b/tests/lib/Repository/Service/Mock/ContentTest.php index 5f6a4558c7..bea81d54d1 100644 --- a/tests/lib/Repository/Service/Mock/ContentTest.php +++ b/tests/lib/Repository/Service/Mock/ContentTest.php @@ -1406,7 +1406,7 @@ static function (ValueStub $value) { $languageCodes ); $nameSchemaServiceMock->expects(self::once()) - ->method('resolve') + ->method('resolveNameSchema') ->with( self::equalTo($contentType->nameSchema), self::equalTo($contentType), @@ -3462,7 +3462,7 @@ static function (SPIValue $value) use ($emptyValue) { $languageCodes ); $nameSchemaServiceMock->expects($this->once()) - ->method('resolveNameSchema') + ->method('resolveContentNameSchema') ->with( $this->equalTo($content), $this->equalTo($values),