diff --git a/Classes/DataProcessing/CategoriesProcessor.php b/Classes/DataProcessing/CategoriesProcessor.php new file mode 100644 index 00000000..c6662e5f --- /dev/null +++ b/Classes/DataProcessing/CategoriesProcessor.php @@ -0,0 +1,136 @@ +checkIf($processorConfiguration['if.'])) { + return $processedData; + } + + $defaultQueryConfig = [ + 'pidInList' => (string)$cObj->stdWrapValue('pidInList', $processorConfiguration, 'root'), + 'recursive' => (string)$cObj->stdWrapValue('recursive', $processorConfiguration, '0'), + 'selectFields' => '{#sys_category}.{#uid} AS id, {#sys_category}.{#title}', + ]; + $queryConfig = []; + + $uidInList = (string)$cObj->stdWrapValue('uidInList', $processorConfiguration, ''); + if (!empty($uidInList)) { + $queryConfig = [ + 'uidInList' => $uidInList, + 'languageField' => 0, + ]; + } + + if (!empty($processorConfiguration['relation.'])) { + $referenceConfiguration = $processorConfiguration['relation.']; + $relationField = $cObj->stdWrapValue('fieldName', $referenceConfiguration ?? []); + if (!empty($relationField)) { + $relationTable = $cObj->stdWrapValue('table', $referenceConfiguration, $cObj->getCurrentTable()); + + if (!empty($relationTable)) { + $queryConfig = [ + 'join' => '{#sys_category_record_mm} on {#sys_category_record_mm}.{#uid_local} = {#sys_category}.{#uid}', + 'where' => '({#sys_category_record_mm}.{#tablenames} = \'' . $relationTable . '\' AND {#sys_category_record_mm}.{#fieldname} = \'' . $relationField . '\' AND {#sys_category_record_mm}.{#uid_foreign}=' . $cObj->data['uid'] . ')', + ]; + } + } + } + + if (empty($queryConfig) === true) { + return $processedData; + } + + ArrayUtility::mergeRecursiveWithOverrule( + $queryConfig, + $defaultQueryConfig + ); + + $targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration, 'categories'); + $categories = $cObj->getRecords('sys_category', $queryConfig); + + $processedRecordVariables = []; + foreach ($categories as $key => $category) { + $processedRecordVariables[$key] = ArrayUtility::filterRecursive($category, function ($key) { + return $key === 'id' || $key === 'title'; + }, ARRAY_FILTER_USE_KEY); + } + + $processedData[$targetVariableName] = $processedRecordVariables; + + return $processedData; + } +} diff --git a/Configuration/Services.php b/Configuration/Services.php index 5f0196e6..8f59c5d8 100644 --- a/Configuration/Services.php +++ b/Configuration/Services.php @@ -14,6 +14,7 @@ use FriendsOfTYPO3\Headless\ContentObject\IntegerContentObject; use FriendsOfTYPO3\Headless\ContentObject\JsonContentContentObject; use FriendsOfTYPO3\Headless\ContentObject\JsonContentObject; +use FriendsOfTYPO3\Headless\DataProcessing\CategoriesProcessor; use FriendsOfTYPO3\Headless\DataProcessing\DatabaseQueryProcessor; use FriendsOfTYPO3\Headless\DataProcessing\FilesProcessor; use FriendsOfTYPO3\Headless\DataProcessing\FlexFormProcessor; @@ -120,6 +121,7 @@ GalleryProcessor::class => ['identifier' => 'headless-gallery', 'share' => false, 'public' => false], DatabaseQueryProcessor::class => ['identifier' => 'headless-database-query', 'share' => false, 'public' => true], FlexFormProcessor::class => ['identifier' => 'headless-flex-form', 'share' => false, 'public' => false], + CategoriesProcessor::class => ['identifier' => 'headless-categories', 'share' => false, 'public' => false], ] as $class => $processorConfig ) { $service = $services->set($class) diff --git a/Configuration/TypoScript/Configuration/PageConfiguration.typoscript b/Configuration/TypoScript/Configuration/PageConfiguration.typoscript index 3a1c81f0..7fb945ff 100644 --- a/Configuration/TypoScript/Configuration/PageConfiguration.typoscript +++ b/Configuration/TypoScript/Configuration/PageConfiguration.typoscript @@ -51,7 +51,16 @@ page { } } meta =< lib.meta - categories =< lib.categories + categories = JSON + categories { + dataProcessing { + 10 = headless-categories + 10 { + relation.fieldName = categories + as = categories + } + } + } breadcrumbs =< lib.breadcrumbs appearance =< lib.pageAppearance content =< lib.content diff --git a/Configuration/TypoScript/ContentElement/ContentElement.typoscript b/Configuration/TypoScript/ContentElement/ContentElement.typoscript index 4154624f..0ca61eb1 100755 --- a/Configuration/TypoScript/ContentElement/ContentElement.typoscript +++ b/Configuration/TypoScript/ContentElement/ContentElement.typoscript @@ -14,40 +14,14 @@ lib.contentElement { colPos { field = colPos } - categories = COA + categories = JSON categories { - 10 = CONTENT - 10 { - table = sys_category - select { - pidInList = root - selectFields = sys_category.title - join = sys_category_record_mm on sys_category_record_mm.uid_local = sys_category.uid - where { - field = uid - wrap = AND sys_category_record_mm.tablenames = 'tt_content' AND sys_category_record_mm.uid_foreign=| - } - } - renderObj = TEXT - renderObj { - field = title - wrap = |###BREAK### - } - } - stdWrap.split { - token = ###BREAK### - cObjNum = 1 |*|2|*| 3 - 1 { - current = 1 - stdWrap.wrap = | - } - 2 { - current = 1 - stdWrap.wrap = ,| - } - 3 { - current = 1 - stdWrap.wrap = | + dataProcessing { + 10 = headless-categories + 10 { + relation.fieldName = categories + + as = categories } } } diff --git a/README.md b/README.md index c75f03f0..bfbaacb7 100644 --- a/README.md +++ b/README.md @@ -247,6 +247,9 @@ Used for processing flexforms. ### RootSitesProcessor Render your all headless sites configuration for your frontend application. +### CategoriesProcessor +This processor returns an array of categories (`id` and `title`) by either a +relation record or a comma separated list of category uids ## Contributing ![Alt](https://repobeats.axiom.co/api/embed/197db91cad9195bb15a06c91fda5a215bff26cba.svg) diff --git a/Tests/Functional/ContentTypes/BaseContentTypeTest.php b/Tests/Functional/ContentTypes/BaseContentTypeTest.php index aa5d025c..35a086a6 100644 --- a/Tests/Functional/ContentTypes/BaseContentTypeTest.php +++ b/Tests/Functional/ContentTypes/BaseContentTypeTest.php @@ -25,7 +25,7 @@ public function setUp(): void $this->importDataSet(__DIR__ . '/../Fixtures/content.xml'); } - protected function checkDefaultContentFields($contentElement, $id, $pid, $type, $colPos = 0, $categories = '') + protected function checkDefaultContentFields($contentElement, $id, $pid, $type, $colPos = 0, $categories = []) { self::assertEquals($id, $contentElement['id'], 'id mismatch'); self::assertEquals($type, $contentElement['type'], 'type mismatch'); diff --git a/Tests/Functional/ContentTypes/ShortcutElementTest.php b/Tests/Functional/ContentTypes/ShortcutElementTest.php index 1197f237..3c65d51c 100644 --- a/Tests/Functional/ContentTypes/ShortcutElementTest.php +++ b/Tests/Functional/ContentTypes/ShortcutElementTest.php @@ -42,7 +42,12 @@ public function testShortcutContentElement() self::assertFalse(isset($contentElement['content']['shortcut'][0]['bodytext'])); // element at pos 1 is our TextElement - $this->checkDefaultContentFields($contentElement['content']['shortcut'][1], 1, 1, 'text', 0, 'SysCategory1Title,SysCategory2Title'); + $categories = [ + ['id' => 1, 'title' => 'SysCategory1Title'], + ['id' => 2, 'title' => 'SysCategory2Title'], + ]; + + $this->checkDefaultContentFields($contentElement['content']['shortcut'][1], 1, 1, 'text', 0, $categories); $this->checkAppearanceFields($contentElement['content']['shortcut'][1], 'layout-1', 'Frame', 'SpaceBefore', 'SpaceAfter'); $this->checkHeaderFields($contentElement['content']['shortcut'][1], 'Header', 'SubHeader', 1, 2); $this->checkHeaderFieldsLink($contentElement['content']['shortcut'][1], 'Page 1', '/page1?parameter=999&cHash=', '_blank'); diff --git a/Tests/Functional/ContentTypes/TextElementTest.php b/Tests/Functional/ContentTypes/TextElementTest.php index 654072b2..87f5f5d9 100644 --- a/Tests/Functional/ContentTypes/TextElementTest.php +++ b/Tests/Functional/ContentTypes/TextElementTest.php @@ -26,8 +26,12 @@ public function testTextContentElement() $fullTree = json_decode((string)$response->getBody(), true); $contentElement = $fullTree['content']['colPos0'][0]; + $categories = [ + ['id' => 1, 'title' => 'SysCategory1Title'], + ['id' => 2, 'title' => 'SysCategory2Title'], + ]; - $this->checkDefaultContentFields($contentElement, 1, 1, 'text', 0, 'SysCategory1Title,SysCategory2Title'); + $this->checkDefaultContentFields($contentElement, 1, 1, 'text', 0, $categories); $this->checkAppearanceFields($contentElement, 'layout-1', 'Frame', 'SpaceBefore', 'SpaceAfter'); $this->checkHeaderFields($contentElement, 'Header', 'SubHeader', 1, 2); $this->checkHeaderFieldsLink($contentElement, 'Page 1', '/page1?parameter=999&cHash=', '_blank');