Skip to content

Commit 3cc680c

Browse files
committed
Add menu caching
1 parent c615fbe commit 3cc680c

File tree

10 files changed

+209
-48
lines changed

10 files changed

+209
-48
lines changed

resources/views/tailwind/components/menu/default-item.blade.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@
1919
])
2020

2121
@php
22-
$hasChildren = $item->children && $item->children->isNotEmpty();
22+
/** @var \Statikbe\FilamentFlexibleContentBlockPages\Components\Data\MenuItemData $item */
23+
2324
$isCurrent = $item->isCurrentMenuItem();
24-
$isActive = $isCurrent || ($hasChildren && $item->hasActiveChildren());
25+
$isActive = $isCurrent || ($item->hasChildren() && $item->hasActiveChildren());
2526
2627
$wrapperTag = $isCurrent
2728
? 'span'
@@ -31,25 +32,25 @@
3132
<li @class([
3233
$itemClass,
3334
"menu-item--level-{$level}",
34-
$itemHasChildrenClass => $hasChildren,
35+
$itemHasChildrenClass => $item->hasChildren(),
3536
$currentItemClass => $isCurrent,
3637
$activeItemClass => $isActive,
3738
])>
3839
<{{ $wrapperTag }}
39-
@if (!$isCurrent) href="{{ $item->getCompleteUrl($locale) }}" @endif
40+
@if (!$isCurrent) href="{{ $item->url }}" @endif
4041
@class([
4142
$itemLinkClass,
4243
"menu-item-link--level-{$level}",
4344
$currentItemLinkClass => $isCurrent,
4445
$activeItemLinkClass => $isActive,
4546
])
46-
@if (!$isCurrent && $item->getTarget() !== '_self') target="{{ $item->getTarget() }}" @endif
47+
@if (!$isCurrent && $item->target !== '_self') target="{{ $item->target }}" @endif
4748
@if ($isCurrent) aria-current="page" @endif
4849
>
49-
{{ $item->getDisplayLabel($locale) }}
50+
{{ $item->label }}
5051
</{{ $wrapperTag }}>
5152

52-
@if ($hasChildren)
53+
@if ($item->hasChildren())
5354
<ul @class([
5455
$subMenuClass,
5556
"navigation-sub-menu--level-{$level}",

resources/views/tailwind/components/menu/default.blade.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
{{-- Default menu template --}}
2+
@php
3+
/** @var \Statikbe\FilamentFlexibleContentBlockPages\Components\Data\MenuData $menu */
4+
@endphp
25

36
@props([
47
/* provided by the component */

src/Components/Data/MenuData.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace Statikbe\FilamentFlexibleContentBlockPages\Components\Data;
4+
5+
use Illuminate\Support\Collection;
6+
use Statikbe\FilamentFlexibleContentBlockPages\Models\Menu;
7+
use Statikbe\FilamentFlexibleContentBlockPages\Models\MenuItem;
8+
9+
class MenuData
10+
{
11+
public function __construct(
12+
public string $name,
13+
public ?string $style,
14+
public Collection $items,
15+
) {}
16+
17+
public static function create(Menu $menu, string $locale): self
18+
{
19+
$maxDepth = $menu->getEffectiveMaxDepth();
20+
21+
// eager load relationships:
22+
$relations = ['linkable', 'linkable.parent', 'linkable.parent.parent'];
23+
$currentPath = '';
24+
$depth = 1;
25+
26+
while ($depth <= $maxDepth) {
27+
$currentPath .= $depth === 1 ? 'children' : '.children';
28+
$relations[$currentPath] = function ($query) {
29+
$query->visible()->ordered()->with('linkable', 'linkable.parent', 'linkable.parent.parent');
30+
};
31+
$depth++;
32+
}
33+
34+
// Get only top-level menu items with their visible children based on max depth
35+
/** @phpstan-ignore-next-line method.notFound */
36+
$menuItemsData = $menu->menuItems()
37+
->with($relations)
38+
->visible()
39+
->ordered()
40+
->get()
41+
->map(function (MenuItem $item) use ($locale) {
42+
return MenuItemData::create($item, $locale);
43+
});
44+
45+
return new self($menu->name, $menu->style, $menuItemsData);
46+
}
47+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace Statikbe\FilamentFlexibleContentBlockPages\Components\Data;
4+
5+
use Illuminate\Support\Collection;
6+
use Statikbe\FilamentFlexibleContentBlockPages\Models\MenuItem;
7+
8+
class MenuItemData
9+
{
10+
public function __construct(
11+
public string $label,
12+
public string $url,
13+
public ?string $target,
14+
public ?string $icon,
15+
public ?Collection $children,
16+
) {}
17+
18+
public static function create(MenuItem $item, string $locale): self
19+
{
20+
return new self(
21+
$item->getDisplayLabel($locale),
22+
$item->getCompleteUrl($locale),
23+
$item->getTarget(),
24+
$item->icon,
25+
$item->children->map(function (MenuItem $childItem) use ($locale) {
26+
return static::create($childItem, $locale);
27+
})
28+
);
29+
}
30+
31+
public function hasChildren(): bool
32+
{
33+
return $this->children && $this->children->isNotEmpty();
34+
}
35+
36+
public function isCurrentMenuItem(): bool
37+
{
38+
$currentUrl = request()->url();
39+
$itemUrl = $this->url;
40+
41+
return MenuItem::urlsMatch($itemUrl, $currentUrl);
42+
}
43+
44+
public function hasActiveChildren(): bool
45+
{
46+
if (! $this->children?->isEmpty()) {
47+
return false;
48+
}
49+
50+
return $this->children->some(function ($child) {
51+
/** @var static $child */
52+
return $child->isCurrentMenuItem() || $child->hasActiveChildren();
53+
});
54+
}
55+
}

src/Components/Menu.php

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22

33
namespace Statikbe\FilamentFlexibleContentBlockPages\Components;
44

5-
use Illuminate\Database\Eloquent\Collection;
5+
use Illuminate\Support\Collection;
6+
use Illuminate\Support\Facades\Cache;
67
use Illuminate\View\Component;
8+
use Statikbe\FilamentFlexibleContentBlockPages\Components\Data\MenuData;
79
use Statikbe\FilamentFlexibleContentBlockPages\Facades\FilamentFlexibleContentBlockPages;
810
use Statikbe\FilamentFlexibleContentBlockPages\FilamentFlexibleContentBlockPagesServiceProvider;
911

1012
class Menu extends Component
1113
{
12-
public ?Menu $menu;
14+
const CACHE_MENU_KEY = 'menu_%s_%s';
15+
16+
public ?MenuData $menu;
1317

1418
public ?Collection $items;
1519

@@ -22,19 +26,29 @@ public function __construct(
2226
?string $style = null,
2327
?string $locale = null
2428
) {
25-
$this->menu = $this->getMenuByCode($code);
2629
$this->locale = $locale ?: app()->getLocale();
30+
$this->menu = $this->getMenuData($code);
2731

2832
// Determine the style to use with proper fallback chain
2933
if ($style) {
3034
$this->style = $style;
31-
} elseif ($this->menu) {
32-
$this->style = $this->menu->getEffectiveStyle();
3335
} else {
34-
$this->style = FilamentFlexibleContentBlockPages::config()->getDefaultMenuStyle();
36+
$this->style = $this->getEffectiveStyle();
3537
}
3638

37-
$this->items = $this->menu ? $this->getMenuItems($this->menu, $this->locale) : [];
39+
$this->items = $this->menu ? $this->menu->items : collect();
40+
}
41+
42+
public static function getMenuCacheKey(string $code, string $locale): string
43+
{
44+
return flexiblePagesPrefix(sprintf(self::CACHE_MENU_KEY, $locale, $code));
45+
}
46+
47+
public static function clearMenuCache(string $code): void
48+
{
49+
foreach (FilamentFlexibleContentBlockPages::config()->getSupportedLocales() as $locale) {
50+
Cache::forget(self::getMenuCacheKey($code, $locale));
51+
}
3852
}
3953

4054
public function render()
@@ -53,44 +67,30 @@ public function render()
5367
return view($defaultTemplate);
5468
}
5569

56-
protected function getMenuByCode(string $code): ?Menu
57-
{
58-
$menuModel = FilamentFlexibleContentBlockPages::config()->getMenuModel();
59-
60-
return $menuModel::getByCode($code);
61-
}
62-
63-
protected function getMenuItems($menu, ?string $locale = null): Collection
70+
protected function getMenuData(string $code): ?MenuData
6471
{
65-
if (! $menu) {
66-
return collect();
67-
}
72+
return Cache::rememberForever(self::getMenuCacheKey($code, $this->locale), function () use ($code) {
73+
$menuModel = FilamentFlexibleContentBlockPages::config()->getMenuModel();
74+
$menu = $menuModel::getByCode($code);
6875

69-
$maxDepth = $menu->getEffectiveMaxDepth();
70-
$eagerLoadRelations = $this->buildEagerLoadRelations($maxDepth);
76+
if (! $menu) {
77+
return null;
78+
}
7179

72-
// Get only top-level menu items with their visible children based on max depth
73-
return $menu->menuItems()
74-
->with($eagerLoadRelations)
75-
->visible()
76-
->ordered()
77-
->get();
80+
return MenuData::create($menu, $this->locale);
81+
});
7882
}
7983

80-
protected function buildEagerLoadRelations(int $maxDepth): array
84+
public function getEffectiveStyle(): string
8185
{
82-
$relations = ['linkable', 'linkable.parent', 'linkable.parent.parent'];
83-
$currentPath = '';
84-
$depth = 1;
85-
86-
while ($depth <= $maxDepth) {
87-
$currentPath .= $depth === 1 ? 'children' : '.children';
88-
$relations[$currentPath] = function ($query) {
89-
$query->visible()->ordered()->with('linkable', 'linkable.parent', 'linkable.parent.parent');
90-
};
91-
$depth++;
86+
// Return the menu's style if set, otherwise fall back to config default
87+
if ($this->menu && ! empty($this->menu->style)) {
88+
$availableStyles = FilamentFlexibleContentBlockPages::config()->getMenuStyles();
89+
if (in_array($this->menu->style, $availableStyles)) {
90+
return $this->menu->style;
91+
}
9292
}
9393

94-
return $relations;
94+
return FilamentFlexibleContentBlockPages::config()->getDefaultMenuStyle();
9595
}
9696
}

src/Components/MenuItem.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@
33
namespace Statikbe\FilamentFlexibleContentBlockPages\Components;
44

55
use Illuminate\View\Component;
6+
use Statikbe\FilamentFlexibleContentBlockPages\Components\Data\MenuItemData;
67
use Statikbe\FilamentFlexibleContentBlockPages\Facades\FilamentFlexibleContentBlockPages;
78
use Statikbe\FilamentFlexibleContentBlockPages\FilamentFlexibleContentBlockPagesServiceProvider;
8-
use Statikbe\FilamentFlexibleContentBlockPages\Models\MenuItem as MenuItemModel;
99

1010
class MenuItem extends Component
1111
{
12-
public MenuItemModel $item;
12+
public MenuItemData $item;
1313

1414
public string $style;
1515

1616
public ?string $locale;
1717

1818
public function __construct(
19-
MenuItemModel $item,
19+
MenuItemData $item,
2020
?string $style = null,
2121
?string $locale = null
2222
) {

src/Models/Menu.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
namespace Statikbe\FilamentFlexibleContentBlockPages\Models;
44

5+
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
56
use Illuminate\Database\Eloquent\Factories\HasFactory;
67
use Illuminate\Database\Eloquent\Model;
78
use Illuminate\Database\Eloquent\Relations\HasMany;
89
use Statikbe\FilamentFlexibleContentBlockPages\Facades\FilamentFlexibleContentBlockPages;
10+
use Statikbe\FilamentFlexibleContentBlockPages\Observers\MenuObserver;
911
use Statikbe\FilamentFlexibleContentBlocks\Models\Concerns\HasCodeTrait;
1012
use Statikbe\FilamentFlexibleContentBlocks\Models\Contracts\HasCode;
1113

@@ -21,6 +23,7 @@
2123
* @property \Illuminate\Database\Eloquent\Collection<\Statikbe\FilamentFlexibleContentBlockPages\Models\MenuItem> $menuItems
2224
* @property \Illuminate\Database\Eloquent\Collection<\Statikbe\FilamentFlexibleContentBlockPages\Models\MenuItem> $allMenuItems
2325
*/
26+
#[ObservedBy(MenuObserver::class)]
2427
class Menu extends Model implements HasCode
2528
{
2629
use HasCodeTrait;

src/Models/MenuItem.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Statikbe\FilamentFlexibleContentBlockPages\Models;
44

5+
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
56
use Illuminate\Database\Eloquent\Factories\HasFactory;
67
use Illuminate\Database\Eloquent\Model;
78
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -10,6 +11,7 @@
1011
use Spatie\Translatable\HasTranslations;
1112
use Statikbe\FilamentFlexibleContentBlockPages\Facades\FilamentFlexibleContentBlockPages;
1213
use Statikbe\FilamentFlexibleContentBlockPages\Models\Contracts\HasMenuLabel;
14+
use Statikbe\FilamentFlexibleContentBlockPages\Observers\MenuItemObserver;
1315
use Statikbe\FilamentFlexibleContentBlocks\Models\Contracts\Linkable;
1416

1517
/**
@@ -31,6 +33,7 @@
3133
* @property \Illuminate\Database\Eloquent\Model|null $linkable
3234
* @property Menu $menu
3335
*/
36+
#[ObservedBy(MenuItemObserver::class)]
3437
class MenuItem extends Model
3538
{
3639
use HasFactory;
@@ -165,6 +168,11 @@ public function isCurrentMenuItem(): bool
165168
$currentUrl = request()->url();
166169
$itemUrl = $this->getCompleteUrl();
167170

171+
return static::urlsMatch($itemUrl, $currentUrl);
172+
}
173+
174+
public static function urlsMatch(string $itemUrl, string $currentUrl): bool
175+
{
168176
// Remove trailing slashes for comparison
169177
$currentUrl = rtrim($currentUrl, '/');
170178
$itemUrl = rtrim($itemUrl, '/');

src/Observers/MenuItemObserver.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Statikbe\FilamentFlexibleContentBlockPages\Observers;
4+
5+
use Statikbe\FilamentFlexibleContentBlockPages\Components\Menu as MenuComponent;
6+
use Statikbe\FilamentFlexibleContentBlockPages\Models\MenuItem;
7+
8+
/**
9+
* Clear menu cache
10+
*/
11+
class MenuItemObserver
12+
{
13+
public function updated(MenuItem $menuItem): void
14+
{
15+
MenuComponent::clearMenuCache($menuItem->menu->code);
16+
}
17+
18+
public function deleted(MenuItem $menuItem): void
19+
{
20+
MenuComponent::clearMenuCache($menuItem->menu->code);
21+
}
22+
}

0 commit comments

Comments
 (0)