Skip to content

Commit 834c053

Browse files
committed
LokiExtensions#14 Category selection input implementation
1 parent ef5775d commit 834c053

11 files changed

Lines changed: 1150 additions & 0 deletions

File tree

Data/CategoryTreeNode.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Loki\AdminComponents\Data;
6+
7+
class CategoryTreeNode
8+
{
9+
public function __construct(
10+
public readonly int $id,
11+
public readonly string $label,
12+
public readonly bool $isActive,
13+
public readonly int $level,
14+
public readonly ?int $parentId = null,
15+
public readonly array $parentIds = [],
16+
public readonly array $children = [],
17+
) {
18+
}
19+
20+
public function toUiArray(): array
21+
{
22+
return array_merge(
23+
get_object_vars($this),
24+
[
25+
'hasChildren' => $this->children !== [],
26+
'expanded' => $this->level === 0,
27+
'hovered' => false,
28+
'parentsPath' => null,
29+
]
30+
);
31+
}
32+
}
33+
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Loki\AdminComponents\Form\Field\FieldType;
6+
7+
use Loki\AdminComponents\Data\CategoryTreeNode;
8+
use Loki\AdminComponents\Service\CategorySelection\CategoryTreeBuilder;
9+
use Loki\AdminComponents\Service\CategorySelection\CategoryTreeRenderer;
10+
use Loki\AdminComponents\Form\Field\Field;
11+
use Loki\AdminComponents\Form\Field\FieldType\FieldTypeAbstract;
12+
use Magento\Framework\Serialize\SerializerInterface;
13+
14+
class CategorySelection extends FieldTypeAbstract
15+
{
16+
public function __construct(
17+
private readonly CategoryTreeBuilder $categoryTreeBuilder,
18+
private readonly CategoryTreeRenderer $categoryTreeRenderer,
19+
private readonly SerializerInterface $serializer,
20+
) {
21+
}
22+
23+
public function getTemplate(): string
24+
{
25+
return 'Loki_AdminComponents::form/field_type/category_selection.phtml';
26+
}
27+
28+
public function getCategoryNodeJson(CategoryTreeNode $treeNode): string
29+
{
30+
return $this->serializer->serialize($treeNode->toUiArray());
31+
}
32+
33+
/**
34+
* @param CategoryTreeNode[] $categoryTreeNodes
35+
*/
36+
public function getMaxCategoryLevel(array $categoryTreeNodes): int
37+
{
38+
return $this->categoryTreeBuilder->calculateMaxLevel($categoryTreeNodes);
39+
}
40+
41+
public function renderChildCategoryNode(CategoryTreeNode $categoryTreeNode, Field $field): string
42+
{
43+
return $this->categoryTreeRenderer->renderChildNode($categoryTreeNode, $field);
44+
}
45+
46+
public function renderCategoriesBreadcrumb(): string
47+
{
48+
return $this->categoryTreeRenderer->renderBreadcrumb();
49+
}
50+
51+
public function renderSearchInput(): string
52+
{
53+
return $this->categoryTreeRenderer->renderSearchInput();
54+
}
55+
56+
public function getHoverSequenceJson(): string
57+
{
58+
$categoryTree = $this->getCategoryTree();
59+
$hoverSequence = $this->categoryTreeBuilder->buildHoverSequence($categoryTree);
60+
61+
return $this->serializer->serialize($hoverSequence);
62+
}
63+
64+
/**
65+
* @return CategoryTreeNode[]
66+
*/
67+
public function getCategoryTree(int $storeId = 0, ?string $filter = null): array
68+
{
69+
return $this->categoryTreeBuilder->build($storeId, $filter);
70+
}
71+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Loki\AdminComponents\Model\CategorySelection;
6+
7+
use Magento\Backend\Model\Auth\Session;
8+
9+
class CategoryTreeCacheKeyGenerator
10+
{
11+
public const CACHE_TAG_PREFIX = 'loki_admin_components_form_category_tree';
12+
13+
public function __construct(
14+
private readonly Session $authSession,
15+
) {
16+
}
17+
18+
public function generate(int $storeId, ?string $filter): string
19+
{
20+
$parts = [
21+
self::CACHE_TAG_PREFIX,
22+
(string) $storeId,
23+
$this->getUserRole(),
24+
$filter ?? '',
25+
];
26+
27+
return implode('_', array_filter($parts));
28+
}
29+
30+
private function getUserRole(): string
31+
{
32+
$user = $this->authSession->getUser();
33+
34+
return (string)$user?->getAclRole();
35+
}
36+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Loki\AdminComponents\Service\CategorySelection;
6+
7+
use Magento\Catalog\Model\Category;
8+
use Magento\Catalog\Model\ResourceModel\Category\Collection;
9+
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;
10+
use Magento\Framework\DB\Helper as DbHelper;
11+
12+
class CategoryFilterService
13+
{
14+
public function __construct(
15+
private readonly CollectionFactory $categoryCollectionFactory,
16+
private readonly DbHelper $dbHelper,
17+
) {
18+
}
19+
20+
/**
21+
* @return int[]
22+
*/
23+
public function getVisibleCategoryIds(int $storeId, ?string $filter): array
24+
{
25+
$collection = $this->createFilteredCollection($storeId, $filter);
26+
27+
return $this->extractCategoryIdsWithAncestors($collection);
28+
}
29+
30+
private function createFilteredCollection(int $storeId, ?string $filter): Collection
31+
{
32+
$collection = $this->categoryCollectionFactory->create();
33+
$collection->addAttributeToSelect('path')
34+
->addAttributeToFilter('entity_id', ['neq' => Category::TREE_ROOT_ID])
35+
->setStoreId($storeId);
36+
37+
if (!empty($filter)) {
38+
$collection->addAttributeToFilter(
39+
'name',
40+
['like' => $this->dbHelper->addLikeEscape($filter, ['position' => 'any'])]
41+
);
42+
}
43+
44+
return $collection;
45+
}
46+
47+
/**
48+
* @return int[]
49+
*/
50+
private function extractCategoryIdsWithAncestors(Collection $collection): array
51+
{
52+
$categoryIds = [];
53+
54+
foreach ($collection as $category) {
55+
$path = $category->getPath() ?? '';
56+
foreach (explode('/', $path) as $ancestorId) {
57+
$categoryIds[(int) $ancestorId] = true;
58+
}
59+
}
60+
61+
return array_keys($categoryIds);
62+
}
63+
}

0 commit comments

Comments
 (0)