From 834c053f2729acfe7ed466a5c7386da9a7462f85 Mon Sep 17 00:00:00 2001 From: Rajeev K Tomy Date: Mon, 9 Feb 2026 13:06:18 +0100 Subject: [PATCH 1/6] #14 Category selection input implementation --- Data/CategoryTreeNode.php | 33 ++ Form/Field/FieldType/CategorySelection.php | 71 ++++ .../CategoryTreeCacheKeyGenerator.php | 36 ++ .../CategoryFilterService.php | 63 +++ .../CategorySelection/CategoryTreeBuilder.php | 240 ++++++++++++ .../CategoryTreeRenderer.php | 61 +++ etc/di.xml | 1 + .../form/field_type/category_selection.phtml | 370 ++++++++++++++++++ .../category_selection/crumbs.phtml | 57 +++ .../category_selection/search_input.phtml | 163 ++++++++ .../select_inner_item.phtml | 55 +++ 11 files changed, 1150 insertions(+) create mode 100644 Data/CategoryTreeNode.php create mode 100644 Form/Field/FieldType/CategorySelection.php create mode 100644 Model/CategorySelection/CategoryTreeCacheKeyGenerator.php create mode 100644 Service/CategorySelection/CategoryFilterService.php create mode 100644 Service/CategorySelection/CategoryTreeBuilder.php create mode 100644 Service/CategorySelection/CategoryTreeRenderer.php create mode 100644 view/adminhtml/templates/form/field_type/category_selection.phtml create mode 100644 view/adminhtml/templates/form/field_type/category_selection/crumbs.phtml create mode 100644 view/adminhtml/templates/form/field_type/category_selection/search_input.phtml create mode 100644 view/adminhtml/templates/form/field_type/category_selection/select_inner_item.phtml diff --git a/Data/CategoryTreeNode.php b/Data/CategoryTreeNode.php new file mode 100644 index 0000000..376a485 --- /dev/null +++ b/Data/CategoryTreeNode.php @@ -0,0 +1,33 @@ + $this->children !== [], + 'expanded' => $this->level === 0, + 'hovered' => false, + 'parentsPath' => null, + ] + ); + } +} + diff --git a/Form/Field/FieldType/CategorySelection.php b/Form/Field/FieldType/CategorySelection.php new file mode 100644 index 0000000..0b99efc --- /dev/null +++ b/Form/Field/FieldType/CategorySelection.php @@ -0,0 +1,71 @@ +serializer->serialize($treeNode->toUiArray()); + } + + /** + * @param CategoryTreeNode[] $categoryTreeNodes + */ + public function getMaxCategoryLevel(array $categoryTreeNodes): int + { + return $this->categoryTreeBuilder->calculateMaxLevel($categoryTreeNodes); + } + + public function renderChildCategoryNode(CategoryTreeNode $categoryTreeNode, Field $field): string + { + return $this->categoryTreeRenderer->renderChildNode($categoryTreeNode, $field); + } + + public function renderCategoriesBreadcrumb(): string + { + return $this->categoryTreeRenderer->renderBreadcrumb(); + } + + public function renderSearchInput(): string + { + return $this->categoryTreeRenderer->renderSearchInput(); + } + + public function getHoverSequenceJson(): string + { + $categoryTree = $this->getCategoryTree(); + $hoverSequence = $this->categoryTreeBuilder->buildHoverSequence($categoryTree); + + return $this->serializer->serialize($hoverSequence); + } + + /** + * @return CategoryTreeNode[] + */ + public function getCategoryTree(int $storeId = 0, ?string $filter = null): array + { + return $this->categoryTreeBuilder->build($storeId, $filter); + } +} diff --git a/Model/CategorySelection/CategoryTreeCacheKeyGenerator.php b/Model/CategorySelection/CategoryTreeCacheKeyGenerator.php new file mode 100644 index 0000000..593fa0d --- /dev/null +++ b/Model/CategorySelection/CategoryTreeCacheKeyGenerator.php @@ -0,0 +1,36 @@ +getUserRole(), + $filter ?? '', + ]; + + return implode('_', array_filter($parts)); + } + + private function getUserRole(): string + { + $user = $this->authSession->getUser(); + + return (string)$user?->getAclRole(); + } +} diff --git a/Service/CategorySelection/CategoryFilterService.php b/Service/CategorySelection/CategoryFilterService.php new file mode 100644 index 0000000..60920ed --- /dev/null +++ b/Service/CategorySelection/CategoryFilterService.php @@ -0,0 +1,63 @@ +createFilteredCollection($storeId, $filter); + + return $this->extractCategoryIdsWithAncestors($collection); + } + + private function createFilteredCollection(int $storeId, ?string $filter): Collection + { + $collection = $this->categoryCollectionFactory->create(); + $collection->addAttributeToSelect('path') + ->addAttributeToFilter('entity_id', ['neq' => Category::TREE_ROOT_ID]) + ->setStoreId($storeId); + + if (!empty($filter)) { + $collection->addAttributeToFilter( + 'name', + ['like' => $this->dbHelper->addLikeEscape($filter, ['position' => 'any'])] + ); + } + + return $collection; + } + + /** + * @return int[] + */ + private function extractCategoryIdsWithAncestors(Collection $collection): array + { + $categoryIds = []; + + foreach ($collection as $category) { + $path = $category->getPath() ?? ''; + foreach (explode('/', $path) as $ancestorId) { + $categoryIds[(int) $ancestorId] = true; + } + } + + return array_keys($categoryIds); + } +} diff --git a/Service/CategorySelection/CategoryTreeBuilder.php b/Service/CategorySelection/CategoryTreeBuilder.php new file mode 100644 index 0000000..d7b2898 --- /dev/null +++ b/Service/CategorySelection/CategoryTreeBuilder.php @@ -0,0 +1,240 @@ +cacheKeyGenerator->generate($storeId, $filter); + $cachedData = $this->cache->load($cacheKey); + + if ($cachedData !== false) { + return $this->deserializeCategoryTree($cachedData); + } + + $tree = $this->buildFreshTree($storeId, $filter); + + $this->cacheTree($cacheKey, $tree); + + return $tree; + } + + /** + * @param CategoryTreeNode[] $treeNodes + */ + public function calculateMaxLevel(array $treeNodes): int + { + $maxLevel = 0; + + foreach ($treeNodes as $node) { + $maxLevel = max($maxLevel, $node->level); + + if ($node->children !== []) { + $maxLevel = max($maxLevel, $this->calculateMaxLevel($node->children)); + } + } + + return $maxLevel; + } + + /** + * @param CategoryTreeNode[] $treeNodes + * @return int[] + */ + public function buildHoverSequence(array $treeNodes): array + { + $sequence = []; + + foreach ($treeNodes as $node) { + $sequence[] = $node->id; + $sequence = [ + ...$sequence, + ...$this->buildHoverSequence($node->children) + ]; + } + + return $sequence; + } + + /** + * @return CategoryTreeNode[] + */ + private function buildFreshTree(int $storeId, ?string $filter): array + { + $visibleCategoryIds = $this->categoryFilterService->getVisibleCategoryIds($storeId, $filter); + + if ($visibleCategoryIds === []) { + return []; + } + + $flatCategories = $this->loadFlatCategories($storeId, $visibleCategoryIds); + + return $this->buildTreeStructure($flatCategories); + } + + /** + * @param int[] $categoryIds + * @return array + */ + private function loadFlatCategories(int $storeId, array $categoryIds): array + { + $collection = $this->categoryCollectionFactory->create(); + $collection->addAttributeToSelect(['name', 'is_active', 'parent_id']) + ->addAttributeToFilter('entity_id', ['in' => $categoryIds]) + ->setStoreId($storeId); + + return $this->convertCollectionToFlatArray($collection); + } + + /** + * @return array + */ + private function convertCollectionToFlatArray(Collection $collection): array + { + $flat = []; + + foreach ($collection as $category) { + $flat[(int) $category->getId()] = [ + 'id' => (int) $category->getId(), + 'label' => (string) $category->getName(), + 'is_active' => (bool) $category->getIsActive(), + 'parent_id' => (int) $category->getParentId(), + ]; + } + + return $flat; + } + + /** + * @param array $flatCategories + * @return CategoryTreeNode[] + */ + private function buildTreeStructure(array $flatCategories): array + { + return $this->buildTreeRecursive( + $flatCategories, + Category::TREE_ROOT_ID, + 0, + [] + ); + } + + /** + * @param array $flatCategories + * @param int[] $ancestorIds + * @return CategoryTreeNode[] + */ + private function buildTreeRecursive( + array $flatCategories, + int $parentId, + int $level, + array $ancestorIds + ): array { + $nodes = []; + + foreach ($flatCategories as $category) { + if ($category['parent_id'] !== $parentId) { + continue; + } + + $currentAncestorIds = $this->buildAncestorIds($ancestorIds, $parentId); + + $children = $this->buildTreeRecursive( + $flatCategories, + $category['id'], + $level + 1, + $currentAncestorIds + ); + + $nodes[] = new CategoryTreeNode( + id: $category['id'], + label: $category['label'], + isActive: $category['is_active'], + level: $level, + parentId: $parentId, + parentIds: $currentAncestorIds, + children: $children + ); + } + + return $nodes; + } + + /** + * @param int[] $ancestorIds + * @return int[] + */ + private function buildAncestorIds(array $ancestorIds, int $parentId): array + { + if ($parentId === Category::TREE_ROOT_ID) { + return $ancestorIds; + } + + return [...$ancestorIds, $parentId]; + } + + /** + * @param CategoryTreeNode[] $tree + */ + private function cacheTree(string $cacheKey, array $tree): void + { + $this->cache->save( + $this->serializer->serialize($tree), + $cacheKey, + [Category::CACHE_TAG, Block::CACHE_TAG] + ); + } + + /** + * @return CategoryTreeNode[] + */ + private function deserializeCategoryTree(string $cachedData): array + { + $treeData = $this->serializer->unserialize($cachedData); + + return $this->convertArrayToTreeNodes($treeData); + } + + /** + * @return CategoryTreeNode[] + */ + private function convertArrayToTreeNodes(array $treeData): array + { + return array_map(fn (array $nodeData) => + new CategoryTreeNode( + id: $nodeData['id'], + label: $nodeData['label'], + isActive: $nodeData['isActive'], + level: $nodeData['level'], + parentId: $nodeData['parentId'], + parentIds: $nodeData['parentIds'], + children: $this->convertArrayToTreeNodes($nodeData['children'] ?? []) + ), + $treeData + ); + } +} diff --git a/Service/CategorySelection/CategoryTreeRenderer.php b/Service/CategorySelection/CategoryTreeRenderer.php new file mode 100644 index 0000000..8b71f53 --- /dev/null +++ b/Service/CategorySelection/CategoryTreeRenderer.php @@ -0,0 +1,61 @@ +layout->createBlock(Template::class, 'category_selection_inner_item_' . $categoryTreeNode->id) + ->setData('category_tree_node', $categoryTreeNode) + ->setData('field', $field) + ->setTemplate($this->getChildItemTemplate()) + ->toHtml(); + } + + public function renderBreadcrumb(): string + { + return $this->layout->createBlock(Template::class, 'categories_selection_crumbs') + ->setTemplate($this->getBreadcrumbsTemplate()) + ->toHtml(); + } + + public function renderSearchInput(): string + { + return $this->layout->createBlock(Template::class, 'category_selection_search_input') + ->setTemplate($this->getSearchInputTemplate()) + ->toHtml(); + } + + private function getChildItemTemplate(): string + { + return self::TEMPLATE_CHILD_ITEM; + } + + private function getBreadcrumbsTemplate(): string + { + return self::TEMPLATE_BREADCRUMB; + } + + private function getSearchInputTemplate(): string + { + return self::TEMPLATE_SEARCH; + } +} diff --git a/etc/di.xml b/etc/di.xml index 32c8ca1..095a9c4 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -44,6 +44,7 @@ Loki\AdminComponents\Form\Field\FieldType\ProductSelect Loki\AdminComponents\Form\Field\FieldType\CustomerSelect Loki\AdminComponents\Form\Field\FieldType\EntitySelect + Loki\AdminComponents\Form\Field\FieldType\CategorySelection diff --git a/view/adminhtml/templates/form/field_type/category_selection.phtml b/view/adminhtml/templates/form/field_type/category_selection.phtml new file mode 100644 index 0000000..d20306e --- /dev/null +++ b/view/adminhtml/templates/form/field_type/category_selection.phtml @@ -0,0 +1,370 @@ +getData('view_model'); +$field = $block->getField(); +$fieldType = $field->getFieldType(); +$categoryTreeNodes = $fieldType->getCategoryTree(); +?> +
+
+
+
+ +
+
+
+
+ renderCategoriesBreadcrumb() ?> +
+
+ renderSearchInput() ?> + +
    +
  • +
    + + + +
    + children as $childCategory): ?> + renderChildCategoryNode($childCategory, $field)?> + +
  • +
+ + +
+ +
+
+
+
+
+
+
+ diff --git a/view/adminhtml/templates/form/field_type/category_selection/crumbs.phtml b/view/adminhtml/templates/form/field_type/category_selection/crumbs.phtml new file mode 100644 index 0000000..0b5f7fc --- /dev/null +++ b/view/adminhtml/templates/form/field_type/category_selection/crumbs.phtml @@ -0,0 +1,57 @@ + + + diff --git a/view/adminhtml/templates/form/field_type/category_selection/search_input.phtml b/view/adminhtml/templates/form/field_type/category_selection/search_input.phtml new file mode 100644 index 0000000..7d519ec --- /dev/null +++ b/view/adminhtml/templates/form/field_type/category_selection/search_input.phtml @@ -0,0 +1,163 @@ + +
+ + + +
+ + + + diff --git a/view/adminhtml/templates/form/field_type/category_selection/select_inner_item.phtml b/view/adminhtml/templates/form/field_type/category_selection/select_inner_item.phtml new file mode 100644 index 0000000..d086132 --- /dev/null +++ b/view/adminhtml/templates/form/field_type/category_selection/select_inner_item.phtml @@ -0,0 +1,55 @@ +getData('field'); +$categoryTreeNode = $block->getData('category_tree_node'); +$fieldType = $field->getFieldType(); +?> +
    +
  • +
    + + + +
    + children as $childCategory): ?> + renderChildCategoryNode($childCategory, $field)?> + +
  • +
From 7d2cad63f3a46e132e878a3f518e98c47649c472 Mon Sep 17 00:00:00 2001 From: Rajeev K Tomy Date: Mon, 9 Feb 2026 15:43:13 +0100 Subject: [PATCH 2/6] separate main script from the main template of category selection --- Data/CategoryTreeNode.php | 1 - Form/Field/FieldType/CategorySelection.php | 5 + .../CategorySelection/CategoryTreeBuilder.php | 14 +- .../CategoryTreeRenderer.php | 33 ++- .../form/field_type/category_selection.phtml | 270 +---------------- .../category_selection/main_js.phtml | 275 ++++++++++++++++++ .../category_selection/search_input.phtml | 2 +- .../select_inner_item.phtml | 4 +- 8 files changed, 326 insertions(+), 278 deletions(-) create mode 100644 view/adminhtml/templates/form/field_type/category_selection/main_js.phtml diff --git a/Data/CategoryTreeNode.php b/Data/CategoryTreeNode.php index 376a485..7e993f8 100644 --- a/Data/CategoryTreeNode.php +++ b/Data/CategoryTreeNode.php @@ -30,4 +30,3 @@ public function toUiArray(): array ); } } - diff --git a/Form/Field/FieldType/CategorySelection.php b/Form/Field/FieldType/CategorySelection.php index 0b99efc..21d7861 100644 --- a/Form/Field/FieldType/CategorySelection.php +++ b/Form/Field/FieldType/CategorySelection.php @@ -38,6 +38,11 @@ public function getMaxCategoryLevel(array $categoryTreeNodes): int return $this->categoryTreeBuilder->calculateMaxLevel($categoryTreeNodes); } + public function renderMainScript(Field $field): string + { + return $this->categoryTreeRenderer->renderMainScript($field); + } + public function renderChildCategoryNode(CategoryTreeNode $categoryTreeNode, Field $field): string { return $this->categoryTreeRenderer->renderChildNode($categoryTreeNode, $field); diff --git a/Service/CategorySelection/CategoryTreeBuilder.php b/Service/CategorySelection/CategoryTreeBuilder.php index d7b2898..7eb7f94 100644 --- a/Service/CategorySelection/CategoryTreeBuilder.php +++ b/Service/CategorySelection/CategoryTreeBuilder.php @@ -226,13 +226,13 @@ private function convertArrayToTreeNodes(array $treeData): array { return array_map(fn (array $nodeData) => new CategoryTreeNode( - id: $nodeData['id'], - label: $nodeData['label'], - isActive: $nodeData['isActive'], - level: $nodeData['level'], - parentId: $nodeData['parentId'], - parentIds: $nodeData['parentIds'], - children: $this->convertArrayToTreeNodes($nodeData['children'] ?? []) + id: $nodeData['id'], + label: $nodeData['label'], + isActive: $nodeData['isActive'], + level: $nodeData['level'], + parentId: $nodeData['parentId'], + parentIds: $nodeData['parentIds'], + children: $this->convertArrayToTreeNodes($nodeData['children'] ?? []) ), $treeData ); diff --git a/Service/CategorySelection/CategoryTreeRenderer.php b/Service/CategorySelection/CategoryTreeRenderer.php index 8b71f53..2eeccfd 100644 --- a/Service/CategorySelection/CategoryTreeRenderer.php +++ b/Service/CategorySelection/CategoryTreeRenderer.php @@ -13,6 +13,8 @@ class CategoryTreeRenderer { private const TEMPLATE_CHILD_ITEM = 'Loki_AdminComponents::form/field_type/category_selection/select_inner_item.phtml'; + private const MAIN_SCRIPT = + 'Loki_AdminComponents::form/field_type/category_selection/main_js.phtml'; private const TEMPLATE_BREADCRUMB = 'Loki_AdminComponents::form/field_type/category_selection/crumbs.phtml'; private const TEMPLATE_SEARCH = 'Loki_AdminComponents::form/field_type/category_selection/search_input.phtml'; @@ -21,6 +23,14 @@ public function __construct( ) { } + public function renderMainScript(Field $field): string + { + return $this->layout->createBlock(Template::class, 'category_selection_main_script') + ->setData('field', $field) + ->setTemplate($this->getMainScript()) + ->toHtml(); + } + public function renderChildNode(CategoryTreeNode $categoryTreeNode, Field $field): string { return $this->layout->createBlock(Template::class, 'category_selection_inner_item_' . $categoryTreeNode->id) @@ -44,18 +54,35 @@ public function renderSearchInput(): string ->toHtml(); } - private function getChildItemTemplate(): string + /** + * Make it public to provide facility to overwrite template. + */ + public function getChildItemTemplate(): string { return self::TEMPLATE_CHILD_ITEM; } - private function getBreadcrumbsTemplate(): string + /** + * Make it public to provide facility to overwrite template. + */ + public function getBreadcrumbsTemplate(): string { return self::TEMPLATE_BREADCRUMB; } - private function getSearchInputTemplate(): string + /** + * Make it public to provide facility to overwrite template. + */ + public function getSearchInputTemplate(): string { return self::TEMPLATE_SEARCH; } + + /** + * Make it public to provide facility to overwrite template. + */ + public function getMainScript(): string + { + return self::MAIN_SCRIPT; + } } diff --git a/view/adminhtml/templates/form/field_type/category_selection.phtml b/view/adminhtml/templates/form/field_type/category_selection.phtml index d20306e..49537aa 100644 --- a/view/adminhtml/templates/form/field_type/category_selection.phtml +++ b/view/adminhtml/templates/form/field_type/category_selection.phtml @@ -1,6 +1,6 @@ getCategoryTree();
@@ -45,12 +45,12 @@ $categoryTreeNodes = $fieldType->getCategoryTree(); :class="classMultiselect" @click="toggleListVisible" > - renderCategoriesBreadcrumb() ?> + renderCategoriesBreadcrumb() ?>
- renderSearchInput() ?> + renderSearchInput() ?>
    getCategoryTree();
children as $childCategory): ?> - renderChildCategoryNode($childCategory, $field)?> + renderChildCategoryNode($childCategory, $field)?> @@ -109,262 +109,4 @@ $categoryTreeNodes = $fieldType->getCategoryTree(); - +renderMainScript($field) ?> diff --git a/view/adminhtml/templates/form/field_type/category_selection/main_js.phtml b/view/adminhtml/templates/form/field_type/category_selection/main_js.phtml new file mode 100644 index 0000000..ae62514 --- /dev/null +++ b/view/adminhtml/templates/form/field_type/category_selection/main_js.phtml @@ -0,0 +1,275 @@ +getData('field'); +$fieldType = $field->getFieldType(); +$maxTreeLevel = $fieldType->getMaxCategoryLevel($fieldType->getCategoryTree()); +?> + diff --git a/view/adminhtml/templates/form/field_type/category_selection/search_input.phtml b/view/adminhtml/templates/form/field_type/category_selection/search_input.phtml index 7d519ec..1b059b0 100644 --- a/view/adminhtml/templates/form/field_type/category_selection/search_input.phtml +++ b/view/adminhtml/templates/form/field_type/category_selection/search_input.phtml @@ -63,7 +63,7 @@ use Magento\Framework\View\Element\Template; diff --git a/view/adminhtml/templates/form/field_type/category_selection/select_inner_item.phtml b/view/adminhtml/templates/form/field_type/category_selection/select_inner_item.phtml index d086132..9f310c6 100644 --- a/view/adminhtml/templates/form/field_type/category_selection/select_inner_item.phtml +++ b/view/adminhtml/templates/form/field_type/category_selection/select_inner_item.phtml @@ -1,7 +1,7 @@ getFieldType(); children as $childCategory): ?> - renderChildCategoryNode($childCategory, $field)?> + renderChildCategoryNode($childCategory, $field)?> From ae660c340ef9ed84fd07821a35ee5b87cad6e9d3 Mon Sep 17 00:00:00 2001 From: Rajeev K Tomy Date: Mon, 9 Feb 2026 16:28:21 +0100 Subject: [PATCH 3/6] remove unnecessary classes from category_selection/select_inner_item.phtml --- .../form/field_type/category_selection/select_inner_item.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/adminhtml/templates/form/field_type/category_selection/select_inner_item.phtml b/view/adminhtml/templates/form/field_type/category_selection/select_inner_item.phtml index 9f310c6..e8dd51c 100644 --- a/view/adminhtml/templates/form/field_type/category_selection/select_inner_item.phtml +++ b/view/adminhtml/templates/form/field_type/category_selection/select_inner_item.phtml @@ -26,7 +26,7 @@ $fieldType = $field->getFieldType();
  • -
    Date: Mon, 9 Feb 2026 16:38:30 +0100 Subject: [PATCH 4/6] Change category_selection input alpine component name to CategorySelection --- .../templates/form/field_type/category_selection.phtml | 2 +- .../form/field_type/category_selection/main_js.phtml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/view/adminhtml/templates/form/field_type/category_selection.phtml b/view/adminhtml/templates/form/field_type/category_selection.phtml index 49537aa..fd3745d 100644 --- a/view/adminhtml/templates/form/field_type/category_selection.phtml +++ b/view/adminhtml/templates/form/field_type/category_selection.phtml @@ -18,7 +18,7 @@ $categoryTreeNodes = $fieldType->getCategoryTree();
    diff --git a/view/adminhtml/templates/form/field_type/category_selection/main_js.phtml b/view/adminhtml/templates/form/field_type/category_selection/main_js.phtml index ae62514..9c1f732 100644 --- a/view/adminhtml/templates/form/field_type/category_selection/main_js.phtml +++ b/view/adminhtml/templates/form/field_type/category_selection/main_js.phtml @@ -120,7 +120,7 @@ $maxTreeLevel = $fieldType->getMaxCategoryLevel($fieldType->getCategoryTree()); }, }; - function CategoriesSelection() { + function CategorySelection() { return { ...CategorySelectionSearch, ...CategorySelectionHover, @@ -269,7 +269,7 @@ $maxTreeLevel = $fieldType->getMaxCategoryLevel($fieldType->getCategoryTree()); } document.addEventListener('alpine:init', () => { - Alpine.data('CategoriesSelection', CategoriesSelection); + Alpine.data('CategorySelection', CategorySelection); Alpine.data('CategorySelectItem', CategorySelectItem); }); From cdbd253be190a10bf8c17a6944d2e521af6e0f5d Mon Sep 17 00:00:00 2001 From: Rajeev K Tomy Date: Mon, 9 Feb 2026 16:56:55 +0100 Subject: [PATCH 5/6] avoid sending children infor as part of UI data from CategoryTreeNode DTO --- Data/CategoryTreeNode.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Data/CategoryTreeNode.php b/Data/CategoryTreeNode.php index 7e993f8..87a903f 100644 --- a/Data/CategoryTreeNode.php +++ b/Data/CategoryTreeNode.php @@ -19,7 +19,7 @@ public function __construct( public function toUiArray(): array { - return array_merge( + $uiData = array_merge( get_object_vars($this), [ 'hasChildren' => $this->children !== [], @@ -28,5 +28,9 @@ public function toUiArray(): array 'parentsPath' => null, ] ); + + unset($uiData['children']); + + return $uiData; } } From 852c69b721229f790322b5da7b58a636e2b5b2b6 Mon Sep 17 00:00:00 2001 From: Rajeev K Tomy Date: Mon, 9 Feb 2026 17:09:30 +0100 Subject: [PATCH 6/6] fix intermittent issues where this.data.id is not avaiable with category_selection alpine components --- .../templates/form/field_type/category_selection/main_js.phtml | 2 +- .../form/field_type/category_selection/search_input.phtml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/view/adminhtml/templates/form/field_type/category_selection/main_js.phtml b/view/adminhtml/templates/form/field_type/category_selection/main_js.phtml index 9c1f732..0715400 100644 --- a/view/adminhtml/templates/form/field_type/category_selection/main_js.phtml +++ b/view/adminhtml/templates/form/field_type/category_selection/main_js.phtml @@ -237,7 +237,7 @@ $maxTreeLevel = $fieldType->getMaxCategoryLevel($fieldType->getCategoryTree()); } }, get itemId() { - return this.data.id; + return this.data?.id; }, classInnerItem() { return { _parent: this.data.hasChildren }; diff --git a/view/adminhtml/templates/form/field_type/category_selection/search_input.phtml b/view/adminhtml/templates/form/field_type/category_selection/search_input.phtml index 1b059b0..ac20d03 100644 --- a/view/adminhtml/templates/form/field_type/category_selection/search_input.phtml +++ b/view/adminhtml/templates/form/field_type/category_selection/search_input.phtml @@ -122,7 +122,7 @@ use Magento\Framework\View\Element\Template; return { ...CategorySelectItemCommon(), get itemId() { - return this.data.id; + return this.data?.id; }, init() { this.$nextTick(() => {