acerex / filament-menux
Another menu builder plugin for filament php
Fund package maintenance!
Rex-noir
Installs: 10
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/acerex/filament-menux
Requires
- php: ^8.2
- filament/filament: ^4.0
- kalnoy/nestedset: ^6.0
- spatie/laravel-package-tools: ^1.15.0
- spatie/laravel-sluggable: ^3.7
Requires (Dev)
- larastan/larastan: ^2.9
- laravel/pint: ^1.0
- nunomaduro/collision: ^8.0
- orchestra/testbench: ^9.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-arch: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
- phpstan/extension-installer: ^1.1
- phpstan/phpstan-deprecation-rules: ^1.0
- phpstan/phpstan-phpunit: ^1.0
- spatie/laravel-ray: ^1.26
README
Inspired by existing menu builders, but simplified and easier to customize. Most of the customizations might look trivial, but having to roll your own custom resource and extending the plugin's classes to customize these little things can sometimes become a pain-in-ass (At least in my experience).
Table of Contents
- Installation
- Example Usage
- Static Menus
- Static Menu Items
- Using Custom Models
- Add Model-Based Menu Items
- Modifying the Base Resource Class
- Custom Forms And Tables
- Action Modifiers
- Using Custom Link Target Enum
Installation
You can install the package via composer:
composer require acerex/filament-menux
You can publish and run the migrations with:
php artisan vendor:publish --tag="filament-menux-migrations"
php artisan migrate
Optionally, you can publish the views using
php artisan vendor:publish --tag="filament-menux-views"
Example Usage
To start using, add the plugin to the panel you want.
FilamentMenuxPlugin::make() ->useStaticMenus([ 'header' => 'Header', 'footer' => 'Footer', ]) ->addStaticMenuItem('Home', '/') ->setNavigationLabel('Custom Menu Label') ->setPerPage(4) ->setActionModifierUsing(MenuxActionType::EDIT_MENU_ITEM, function (Action $action) { return $action->icon(Heroicon::MagnifyingGlassCircle); }) ->addMenuxableModel(Post::class) ->setNavigationGroup('Custom Menu Group') ->addStaticMenuItem('Contact Us', '/contact-us') ->setLinkTargetEnum(linkTargetEnum: LinkTarget::class) ->addMenuxableModel(model: Page::class),
Registering to panel
Just like any other panel plugins, you can register this in your panel provider
->plugins([ \AceREx\FilamentMenux\FilamentMenuxPlugin::make() ])
Static Menus
With static menus you can limit how many menus can be created except the menus you provided. This is useful, especially for projects where the frontend fetches the menus statically via slug. To pass static menus, you pass the menus to the useStaticMenus method. If you want the plugin to check and create the defined static menus on boot, pass the third second parameter boolean value. By default, it is disabled.
\AceREx\FilamentMenux\FilamentMenuxPlugin::make() ->useStaticMenus([ 'slug'=>'label', 'header'=>"Header" ], true)
Static Menu Items
Static menu items are shown menu items that you provide from the panel configuration. You can add static menu items like this.
->addStaticMenuItem('Home', '/', '_self')
The third argument is optional and can also be any type of backed enum. For consistency, you should use the enum you use for the item form. See Using custom link target enum
Grouped Static Menu Items
You can also pass grouped static menu items. For example:
->addGroupedMenuItems('Social Medias', [ [ 'title' => 'Title', 'url' => 'https://title.com', 'target' => MenuxLinkTarget::BLANK, ], [ 'title' => 'Facebook', 'url' => 'https://www.facebook.com', 'target' => MenuxLinkTarget::SELF, ], ])
Or you can pass a function that returns an array. For example:
->addGroupedMenuItems('Social Medias', function () { return [ [ 'title' => 'Title', 'url' => 'https://title.com', 'target' => MenuxLinkTarget::BLANK, ], [ 'title' => 'Facebook', 'url' => 'https://www.facebook.com', 'target' => MenuxLinkTarget::SELF, ], ]; })
The passed function is deferred till the plugin is fully booted.
Add Model-Based Menu Items
Inspired by Menu Builder by Ngô Quốc Đạt, this plugin supports registering models and rendering them into menu item list selection.
->addMenuxableModel(Post::class)
The model must implement interfaces;
\AceREx\FilamentMenux\Contracts\Interfaces\Menuxable
For example;
use Illuminate\Database\Eloquent\Builder;class Post extends Model implements Menuxable { /** @use HasFactory<\Database\Factories\PostFactory> */ use HasFactory; protected $fillable = [ 'title', 'slug', ]; public static function getMenuxLabel(): string { return 'Posts'; } public function getMenuxTitle(): string { return $this->title; } public function getMenuxUrl(): string { return "https://www.google.com/{$this->slug}"; } public function getMenuxTarget(): BackedEnum { return MenuxLinkTarget::SELF; } public static function getMenuxablesUsing(Builder $builder, $page, $perPage, ?string $q): Builder { if (filled($q)) { return $builder->whereLike('title', $q); } return $builder; } /** * This is optional. You can return an empty collection. * This will for each group, so that you don't have to create multiple classes of Model when * you only have a single table and want separate tabs for each filtering */ public static function getMenuxableGroups(): Collection { $collection = collect(); $collection->put('OnlyDoctors', function (Builder $builder, $page, $perPage, ?string $q) { $builder->where('role', 'Doctor'); if(filled($q)){ $builder->where('title', 'like', "%{$q}%"); } return $builder; }); return $collection; }} }
Set Records Per Page For Menu-Based Menu Items
By default, all menuxable menus are paginated with four records per page. However, you can customize this by providing the number of records you want to query per page via:
->setMenuxablesPerPage(4)
Using Custom Models
You can pass your custom models instead of using the default model classes
For Custom Menu Model
->useCustomMenuModel(YourModel::class)
Note: Your custom model must extend the plugin's model.
For Custom Menu Item Model
->useCustomMenuItemModel(CustomMenuItemModel::class)
Note: Your custom model must extend the plugin's model.
Modifying the Base Resource Class
The plugin supports modifying the base resource class without having to extend the base class.
Navigation Group
To Set navigation group of the resource, you can do it like this:
->setNavigationGroup('Group')
Navigation Label
To Set the navigation label of the resource, you can do it like this:
->setNavigationLabel('Label')
Navigation Icon
To set the navigation icon of the resource, you can do it like this:
->setNavigationIcon(\Filament\Support\Icons\Heroicon::AcademicCap)
Custom Forms and Table
The plugin also allows you to set custom form and table.
Custom Menu Form With Traditional Class
->setMenuForm(CustomForm::class)
The custom form must extend the plugin's MenuForm class.
For example:
use AceREx\FilamentMenux\Filament\Resources\Menus\Schemas\MenuForm as BaseMenuForm; use Filament\Forms\Components\TextInput; class MenuForm extends BaseMenuForm { public static function configure(): array { return [ TextInput::make('name'), ]; } }
Custom Menu Form With Anonymouse Class
Also, you can pass the anonymouse class
->setMenuForm(fn() => new class extends \App\Forms\MenuForm { public static function configure(): array { return [ Section::make('Menu') ->collapsible() ->headerActions([ DeleteAction::make(), Action::make('save') ->label('Save') ->button() ->action('save'), ]) ->schema([ TextInput::make('name') ->label('Name') ->required() ->maxLength(255), ]), ]; } });
Custom Menus Table
You can also pass custom menus table with both traditional class and function that returns anonymouse class that extends the base MenuTables class, For example.
->setMenusTable(\App\Tables\MainMenusTable::class)
or
->setMenusTable(fn() => new class extends \AceREx\FilamentMenux\Filament\Resources\Menus\Tables\MenusTable { public static function configure(Table $table): Table { return $table ->columns([ TextColumn::make('name') ->label('Name') ->sortable() ->searchable(), ]) ->filters([ // ]) ->recordActions([ \Filament\Actions\DeleteAction::make(), \Filament\Actions\EditAction::make() ]) ->toolbarActions([ BulkActionGroup::make([ DeleteBulkAction::make(), ]), ]); } });
Custom Menu Item Form
You can also use your own custom menu item form. Pass your menu item for the class that extends the plugin base class.
->setMenuItemForm(YourCustomMenuItemForm::class)
You can also pass callable that returns anonymouse class too.
->setMenuItemForm(function (){ return new class Extends \AceREx\FilamentMenux\Filament\Resources\Menus\Schemas\MenuItemForm { public function make() : array { return [ \Filament\Forms\Components\TextInput::make('title') ]; } } })
To modify the creation of menu items, see the Action Modifiers section.
Action Modifiers
You can modify actions defined in the AceREx\FilamentMenux\Contracts\Enums\MenuxActionType.
List of actions modifiable via the panel,
enum MenuxActionType: string { /** * Used in menu items builder inside the action group. */ case DELETE_MENU_ITEM = 'delete-item'; /** *Used in the menu items builder to delete the selected items. */ case DELETE_SELECTED_MENU_ITEMS = 'delete-selected-items'; /** * Used in menu items builder inside the action group. */ case DUPLICATE__MENU_ITEM = 'duplicate-item'; /** * Used in the menu items builder to edit created menu item. */ case EDIT_MENU_ITEM = 'edit-item'; /** * Used for adding a menu item not defined in the menu tabs. */ case ADD_CUSTOM_MENU_ITEM = 'add-custom-item'; /** * Used for adding a sub menu-item right under the item. */ case CREATE_SUB_MENU_ITEM = 'create-sub-menu-item'; /** * Used for creating the menu from the {@see ListMenus} or the resource header. */ case CREATE_MENU = 'create-menu'; /** * Used inside {@see MenusTable} actions. If you use your own Menus Table, * then it might be more practical to modify it inside that custom table. */ case DELETE_MENU = 'delete-menu'; /** * Used inside {@see MenusTable} actions. If you use your own Menus Table, * then it might be more practical to modify it inside that custom table. */ case EDIT_MENU = 'edit-menu'; }
To modify each action, you pass the action type and return the Action instance. For example, with closure:
->setActionModifierUsing(MenuxActionType::DUPLICATE__MENU_ITEM, function (Action $action) { return $action ->icon(Heroicon::MagnifyingGlassCircle);
Here the closure receives the Filament Action instance, and the plugin merges it with the default configurations.
Also, you can pass class-based Action Modifiers. For example,
use AceREx\FilamentMenux\Contracts\Interfaces\ActionModifier; use Filament\Actions\Action; class CustomCreateSubMenuItemAction implements ActionModifier { public function modify(Action $action): Action { return $action->label('Custom Gigidy'); } }
and in the panel register the modifier
->setActionModifier(MenuxActionType::CREATE_SUB_MENU_ITEM, CustomCreateSubMenuItemAction::class)
Using Custom Link Target Enum
By default, the plugin uses MenuxLinkTarget for model cast and inside menu item form.
But sometimes, you would like to show fewer options or modify the labeling. Or add some more functionality.
To do that, you can pass your own enum, and that enum will be used inside the menu item form and the model cast.
->setLinkTargetEnum(linkTargetEnum: LinkTarget::class)
The custom enum must implement two interfaces;
\Filament\Actions\Concerns\HasLabel
\AceREx\FilamentMenux\Contracts\Interfaces\HasStaticDefaultValue
For example
enum LinkTarget: string implements HasLabel, HasStaticDefaultValue { case SELF = 'self'; public function getLabel(): string { return match ($this) { self::SELF => 'SELF' }; } public static function getStaticDefaultValue(): HasStaticDefaultValue { return self::SELF; } public function getSomething(): string { return 'something'; } }
License
The MIT License (MIT). Please see License File for more information.



