Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
1.75% |
1 / 57 |
|
33.33% |
1 / 3 |
CRAP | |
0.00% |
0 / 1 |
| MenuService | |
1.75% |
1 / 57 |
|
33.33% |
1 / 3 |
104.83 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getMenuTree | |
0.00% |
0 / 49 |
|
0.00% |
0 / 1 |
42 | |||
| buildSubTree | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace App\Domain\Menu\Service; |
| 6 | |
| 7 | use App\Domain\Menu\Entity\MenuRecord; |
| 8 | use App\Domain\Menu\Repository\MenuRecordRepository; |
| 9 | use App\Domain\Menu\Repository\MenuSortRepository; |
| 10 | |
| 11 | class MenuService |
| 12 | { |
| 13 | public function __construct( |
| 14 | private MenuRecordRepository $recordRepository, |
| 15 | private MenuSortRepository $sortRepository |
| 16 | ) {} |
| 17 | |
| 18 | /** |
| 19 | * Build nested menu tree for sidebar rendering and summary view builder. |
| 20 | * |
| 21 | * @return array<int, array<string, mixed>> |
| 22 | */ |
| 23 | public function getMenuTree(string $recordsTable = 'srv_menu_records', string $sortTable = 'srv_menu_sort'): array |
| 24 | { |
| 25 | $records = $this->recordRepository->findAll($recordsTable); |
| 26 | $sorts = $this->sortRepository->findAll($sortTable); |
| 27 | |
| 28 | /** @var array<string, MenuRecord> $recordsMap */ |
| 29 | $recordsMap = []; |
| 30 | foreach ($records as $record) { |
| 31 | $recordsMap[$record->getId()] = $record; |
| 32 | } |
| 33 | |
| 34 | // If no sort records exist yet, fall back to records order |
| 35 | if (empty($sorts)) { |
| 36 | $fallback = []; |
| 37 | foreach ($records as $index => $rec) { |
| 38 | $fallback[] = [ |
| 39 | 'sort_id' => 'sort-' . $rec->getId(), |
| 40 | 'id' => $rec->getId(), |
| 41 | 'type' => $rec->getType(), |
| 42 | 'title' => $rec->getTitle(), |
| 43 | 'icon' => $rec->getIcon() ?? 'bi-circle', |
| 44 | 'url' => $rec->getUrl() ?? '#', |
| 45 | 'badge_text' => $rec->getBadgeText(), |
| 46 | 'badge_color' => $rec->getBadgeColor() ?? 'primary', |
| 47 | 'target' => $rec->getTarget() ?? '_self', |
| 48 | 'status' => $rec->getStatus(), |
| 49 | 'parent_id' => null, |
| 50 | 'sort_order' => $index + 1, |
| 51 | 'children' => [], |
| 52 | ]; |
| 53 | } |
| 54 | return $fallback; |
| 55 | } |
| 56 | |
| 57 | /** @var array<string, array<string, mixed>> $nodesByRecordId */ |
| 58 | $nodesByRecordId = []; |
| 59 | /** @var array<string, array<int, array<string, mixed>>> $childrenByParentId */ |
| 60 | $childrenByParentId = []; |
| 61 | |
| 62 | foreach ($sorts as $sort) { |
| 63 | $record = $recordsMap[$sort->getMenuRecordId()] ?? null; |
| 64 | if ($record === null) { |
| 65 | continue; |
| 66 | } |
| 67 | |
| 68 | $node = [ |
| 69 | 'sort_id' => $sort->getId(), |
| 70 | 'id' => $record->getId(), |
| 71 | 'type' => $record->getType(), |
| 72 | 'title' => $record->getTitle(), |
| 73 | 'icon' => $record->getIcon() ?? 'bi-circle', |
| 74 | 'url' => $record->getUrl() ?? '#', |
| 75 | 'badge_text' => $record->getBadgeText(), |
| 76 | 'badge_color' => $record->getBadgeColor() ?? 'primary', |
| 77 | 'target' => $record->getTarget() ?? '_self', |
| 78 | 'status' => $record->getStatus(), |
| 79 | 'parent_id' => $sort->getParentId(), |
| 80 | 'sort_order' => $sort->getSortOrder(), |
| 81 | 'children' => [], |
| 82 | ]; |
| 83 | |
| 84 | $nodesByRecordId[$record->getId()] = $node; |
| 85 | $pId = $sort->getParentId() ?? 'root'; |
| 86 | $childrenByParentId[$pId][] = $node; |
| 87 | } |
| 88 | |
| 89 | // Build tree recursively |
| 90 | return $this->buildSubTree('root', $childrenByParentId); |
| 91 | } |
| 92 | |
| 93 | /** |
| 94 | * @param array<string, array<int, array<string, mixed>>> $childrenByParentId |
| 95 | * @return array<int, array<string, mixed>> |
| 96 | */ |
| 97 | private function buildSubTree(string $parentId, array $childrenByParentId): array |
| 98 | { |
| 99 | $items = $childrenByParentId[$parentId] ?? []; |
| 100 | usort($items, static fn(array $a, array $b): int => ($a['sort_order'] <=> $b['sort_order'])); |
| 101 | |
| 102 | foreach ($items as &$item) { |
| 103 | $recordId = is_string($item['id'] ?? null) ? (string) $item['id'] : ''; |
| 104 | $item['children'] = $this->buildSubTree($recordId, $childrenByParentId); |
| 105 | } |
| 106 | unset($item); |
| 107 | |
| 108 | return $items; |
| 109 | } |
| 110 | } |