Download the PHP package gsebastiao/laravel-menu without Composer
On this page you can find all versions of the php package gsebastiao/laravel-menu. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download gsebastiao/laravel-menu
More information about gsebastiao/laravel-menu
Files in gsebastiao/laravel-menu
Package laravel-menu
Short Description Menus dinâmicos e hierárquicos (N níveis) para Laravel, com controlo de permissões flexível (none | string | id) e cache. 100% independente de qualquer pacote de permissões.
License MIT
Homepage https://github.com/gsebastiao/laravel-menu
Informations about the package laravel-menu
Laravel Menu
Menus para Laravel guardados na base de dados, com os níveis que precisares (menu › submenu › sub-submenu…), que mostram a cada utilizador só o que ele pode ver.
- Funciona sem nenhum pacote de permissões — ou com o que já usas: gsebastiao/laravel-authz, Spatie, uma tabela tua.
- Rápido: a árvore fica em cache e a cache limpa-se sozinha quando mudas um item.
- Pronto a mostrar: um componente Blade desenha o menu com uma linha.
Índice
- Requisitos
- Começar em 5 minutos
- Criar itens de menu
- Mostrar o menu
- Permissões
- Proteger rotas (middleware)
- Cache
- Auditoria (opcional)
- Configuração
- Referência rápida
- Resolução de problemas
- Atualizar de uma versão anterior
- Testes
Requisitos
| Laravel | PHP |
|---|---|
| 11.x e 12.x | 8.2 ou mais recente |
| 13.x | 8.3 ou mais recente |
Começar em 5 minutos
1. Instala o pacote
2. Cria a tabela menu_items
3. Cria alguns itens. Para experimentar, usa o menu de exemplo que vem com o pacote:
O primeiro comando copia o exemplo para
database/seeders/MenuItemsSeeder.php, onde o podes editar. Podes correr o seeder as vezes que quiseres: os itens são atualizados, não duplicados.
4. Mostra o menu. No teu layout (ex.: resources/views/layouts/app.blade.php):
Pronto: tens um menu a funcionar. Os itens do exemplo que apontam para rotas que ainda não existem no teu projeto aparecem sem link, sem erros.
Queres mudar alguma opção (permissões, cache, nome da tabela...)? Não precisas de publicar o config: basta acrescentar variáveis
MENU_*ao teu.env. A lista completa, com o padrão de cada uma, está em Configuração. Se fores mudar o nome da tabela (MENU_TABLE), faz isso antes domigrate.
5. (Opcional) Dá-lhe um estilo simples:
Criar itens de menu
Cada item é uma linha da tabela menu_items. Crias, editas e apagas itens como qualquer model do Laravel:
Podes pôr este código num seeder (como o de exemplo) ou experimentá-lo no php artisan tinker. A cache do menu é limpa automaticamente sempre que crias, editas, apagas ou restauras um item assim.
Os campos de um item
| Campo | Obrigatório | Para que serve | Exemplo |
|---|---|---|---|
name |
sim | Identificador interno do item. Usa um nome diferente para cada item. | 'admin.users' |
label |
sim | O texto que aparece no menu. | 'Utilizadores' |
parent_id |
não | O id do item pai. Vazio = item de topo. |
$admin->id |
route |
não | Para onde o item leva (ver tabela abaixo). Vazio = item sem link (ex.: um grupo). | 'users.index' |
params |
não | Parâmetros da rota. | ['user' => 5] |
order |
não (0) |
Posição entre os irmãos: os números menores aparecem primeiro. | 1 |
icon |
não | Classes CSS do ícone (do pacote de ícones que usas). | 'bi bi-people' |
badge |
não | Texto pequeno ao lado do nome. | 'novo' |
description |
não | Texto de ajuda (aparece ao passar o rato). | 'Gerir contas' |
target |
não | '_blank' abre o link num novo separador. |
'_blank' |
permission |
não | Permissão necessária para ver o item (ver Permissões). | 'users.view' |
is_active |
não (true) |
false esconde o item e todos os filhos dele. |
false |
is_separator |
não (false) |
Mostra uma linha divisória em vez de um link. | true |
O que pode ir no campo route
| Escreves | O item leva a… |
|---|---|
'users.index' |
a rota com esse nome (route('users.index', $params)) |
'/sobre' |
um caminho da tua aplicação (começa por /) |
'https://exemplo.com' |
um endereço externo (também mailto: e tel:) |
'#contactos' |
uma âncora na página |
Se o nome de rota não existir (ou faltar um parâmetro obrigatório), o item aparece sem link em vez de dar erro na página. Por segurança, endereços javascript: e data: nunca viram link.
Dica: num painel de administração, gere os itens com um CRUD normal (controller + formulário) sobre o model
MenuItem.
Mostrar o menu
Com o componente pronto
Mostra o menu do utilizador autenticado, já filtrado pelas permissões, com os submenus aninhados. Também aceita outros itens e atributos para a <ul> principal:
O HTML gerado usa estas classes, para poderes dar-lhe o estilo que quiseres:
| Classe | Onde aparece |
|---|---|
menu |
cada <ul> do menu |
menu-submenu |
cada <ul> de um submenu |
menu-item |
cada <li> com um item |
is-active |
o <li> da página atual e dos seus pais (para abrires o submenu certo) |
has-children |
o <li> de um item com submenu |
menu-link |
o <a> de cada item (sem href quando o item não tem link) |
menu-icon, menu-label, menu-badge |
o ícone, o texto e o badge |
menu-separator |
o <li> de um separador |
Além das classes, o <a> da página atual leva aria-current="page", para que um leitor de ecrã a anuncie. Só esse: os itens-pai ficam apenas com a classe is-active.
Mudar o HTML do componente
Publica a view e edita a cópia (ex.: para usares as classes do Bootstrap ou do Tailwind):
A cópia fica em resources/views/vendor/laravel-menu/components/menu.blade.php e passa a ser usada no lugar da original.
Montar o teu próprio HTML
Se preferires escrever tudo à mão, estas são as peças que o componente usa:
Menu::forUser()devolve os itens de topo que o utilizador pode ver. Cada item é um array com todos os campos da tabela e uma chavechildrencom os filhos.Menu::url($item)devolve o link do item, ounullse não tiver.Menu::isActive($item)diz se o item (ou um dos filhos) é a página atual. Um item com a rotausers.indextambém fica ativo emusers.create,users.edit, etc.
Permissões
Por omissão não há permissões: todos os itens aparecem a toda a gente. Para esconder itens, são dois passos.
Passo 1: escolhe o modo
No .env:
| Modo | O que guardas no campo permission de cada item |
|---|---|
none |
Nada — as permissões são ignoradas (é o padrão). |
string |
O nome da permissão, ex.: users.view. |
id |
O id da permissão numa tabela tua, ex.: 42 (ver Modo id). |
Passo 2: diz ao pacote quais são as permissões do utilizador
Usas o gsebastiao/laravel-authz? Não precisas de fazer nada: salta para Com o gsebastiao/laravel-authz.
Usas o spatie/laravel-permission? Também não: o pacote usa $user->getAllPermissions() automaticamente.
Tens outra lógica? Define-a no boot() do app/Providers/AppServiceProvider.php:
O $user é o utilizador autenticado (ou null para visitantes). Podes devolver um array ou uma Collection de textos, models, arrays ou enums (de um model ou array é lida a coluna name; no modo id, a coluna id).
Alternativa: indicar uma classe no config/menu.php
**Não escrevas uma função (closure) diretamente no ficheiro de config:** funciona, mas o `php artisan config:cache` deixa de funcionar.
Com o gsebastiao/laravel-authz
O gsebastiao/laravel-authz traz grupos e permissões (RBAC) ao teu projeto. Com ele instalado, o menu descobre-o sozinho — não escreves resolver nenhum.
1. Instala e prepara o pacote (se ainda não o fizeste):
2. Põe o trait no teu model User:
3. Escolhe o modo string e guarda o nome da permissão em cada item:
É tudo. A partir daqui cada pessoa só vê o que pode, com a cascata do laravel-authz respeitada: negações individuais, regras dos grupos, validade por datas e o tenant atual. A cache do menu guarda a árvore, não as permissões de cada pessoa — quem perde uma permissão deixa de ver o item logo no pedido seguinte.
O middleware faz a mesma leitura, por isso a rota fica protegida com a mesma permissão que esconde o item:
Guardar o id da permissão em vez do nome
Se preferires que o campo `permission` guarde o id da permissão (sobrevive a uma mudança de nome), usa o modo `id`: `$item->resolvedPermissionLabel()` passa a devolver o nome da permissão, lido da tabela `auth_permissions`. Com `MENU_RESOLVER_COLUMN=label` recebes antes o nome amigável (ex.: «Ver utilizadores»), que costuma ser o que queres mostrar num painel de administração.Escolher a fonte das permissões em vez de a deixar ser descoberta
Sem configuração, o pacote procura por esta ordem: laravel-authz, depois `$user->getAllPermissions()` (Spatie). Para não deixar nada ao acaso, diz qual é no `.env`: Os valores aceites são `auto` (o padrão), `authz` e `spatie`. Se escolheres `authz` sem o pacote instalado, a aplicação pára no arranque com uma mensagem a explicar o que falta — em vez de esconder o menu todo em silêncio. Duas notas: se o teu model `User` não tiver o trait `HasAuthz`, o menu lê as permissões à mesma, pelo id do utilizador; e um `Menu::resolvePermissionsUsing(...)` que tenhas escrito continua a ter a última palavra sobre tudo isto.Quem vê o quê
| Situação | Resultado |
|---|---|
Item sem permission |
Aparece a todos. |
Item com permission |
Aparece só a quem tiver essa permissão. |
Item com is_active = false |
Não aparece a ninguém, nem os filhos. |
| Item cujo pai o utilizador não pode ver | Aparece na mesma, e o pai também (para não ficar um "buraco" na navegação). |
| Grupo sem link cujos filhos ficaram todos escondidos | Desaparece (não fica um grupo vazio). |
| Separador | Não conta como filho visível; separadores soltos (no início, no fim ou repetidos) são removidos. |
Verificar permissões no teu código
No modo none, hasPermission() devolve sempre true.
Modo id
Usa este modo se o campo permission guardar ids de uma tabela tua. Indica a tabela no .env:
- O resolver de permissões (passo 2) deve devolver os ids das permissões do utilizador.
$item->resolvedPermissionLabel()devolve o nome legível da permissão do item, lido dessa tabela (útil num painel de administração).- Com o gsebastiao/laravel-authz, os valores certos são
auth_permissions/id/permission— ver Com o gsebastiao/laravel-authz.
Proteger rotas (middleware)
O pacote regista o middleware menu.permission:
- Quem não tiver a permissão recebe um erro 403.
- No modo
none, o middleware deixa passar sempre — o mesmo código serve para projetos com e sem permissões. - Junta-o ao middleware
auth, para que um visitante seja enviado para o login em vez de receber um 403.
Cache
A árvore do menu é guardada em cache e limpa-se sozinha quando um item é criado, editado, apagado ou restaurado através do model (create(), update(), save(), delete(), restore()).
A cache não é limpa sozinha quando alteras os itens por outras vias:
- updates em massa:
MenuItem::where(...)->update([...]); saveQuietly(),updateQuietly()ouMenuItem::withoutEvents(...);- alterações diretas na base de dados (SQL,
DB::table(...), outra aplicação).
Nesses casos, limpa-a à mão:
As opções da cache ficam no .env (ver a tabela em Configuração):
Auditoria (opcional)
Com o pacote gsebastiao/laravel-auditable, fica registado quem criou, editou, apagou ou restaurou cada item de menu, quando e o que mudou. O laravel-menu não depende desse pacote: só o instalas se quiseres.
1. Instala o pacote de auditoria:
O pacote de auditoria traz a própria migration e o migrate a executa: não precisas de publicar nada. (Só publica o config ou a migration se os quiseres editar — vê o README do laravel-auditable.)
2. Liga a auditoria no .env:
Não tens de mudar mais nada: MenuItem::create(), $item->update(), $item->delete() e $item->restore() passam a ficar registados. Em vez do parent_id, a auditoria guarda o nome do item pai.
Se ligares a auditoria sem o pacote instalado, a aplicação pára no arranque com uma mensagem a explicar como instalá-lo. Assim nunca ficas a pensar que a auditoria está a gravar quando não está.
Mudar o que é gravado
Cria um model teu que estenda o `MenuItem` e sobrescreve `getAuditOptions()`: Indica-o no `config/menu.php` (`'model' => \App\Models\MenuItem::class`) e usa-o no teu código. Se quiseres os métodos extra do laravel-auditable (`auditAction()`, `operations()`…), acrescenta `use \Gsebastiao\Auditable\Concerns\Auditable;` a essa classe — o laravel-menu deteta-o e não grava nada em duplicado.Configuração
Tudo funciona sem configurar nada, e não precisas de publicar o ficheiro de configuração. Para mudar alguma coisa, usa o .env:
| Variável | Padrão | Para que serve |
|---|---|---|
MENU_TABLE |
menu_items |
Nome da tabela (muda antes do migrate) |
MENU_MODEL |
Gsebastiao\LaravelMenu\Models\MenuItem |
Model dos itens (uma classe tua que estenda o do pacote) |
MENU_PERMISSION_MODE |
none |
none, string ou id (Permissões) |
MENU_RESOLVER_TABLE |
auth_permissions |
Tabela de permissões (só no modo id) |
MENU_RESOLVER_KEY |
id |
Coluna comparada com o campo permission (só no modo id) |
MENU_RESOLVER_COLUMN |
name |
Coluna com o nome legível da permissão (com o laravel-authz: permission ou label) |
MENU_USER_PERMISSION |
(não definir) | Fonte das permissões do utilizador: auto (o padrão), authz, spatie, ou uma classe com __invoke($user) |
MENU_CACHE_ENABLED |
true |
Liga/desliga a cache |
MENU_CACHE_STORE |
(não definir) | Cache a usar (sem valor = a padrão da aplicação) |
MENU_CACHE_TTL |
(não definir) | Validade da cache em segundos (sem valor ou 0 = sem prazo) |
MENU_CACHE_KEY |
laravel_menu |
Prefixo da chave na cache |
MENU_AUDITING_ENABLED |
false |
Liga a auditoria |
Não deixes uma variável em branco (
MENU_CACHE_STORE=): o Laravel lê isso como texto vazio. Para voltar ao padrão, apaga a linha. Comphp artisan config:cache, corre-o de novo depois de mudar o.env.
Se preferires editar o ficheiro, publica a configuração:
Tudo o que podes publicar:
| Comando | Cria |
|---|---|
php artisan vendor:publish --tag=laravel-menu-config |
config/menu.php |
php artisan vendor:publish --tag=laravel-menu-views |
resources/views/vendor/laravel-menu/components/menu.blade.php |
php artisan vendor:publish --tag=laravel-menu-seeders |
database/seeders/MenuItemsSeeder.php |
php artisan vendor:publish --tag=laravel-menu-migrations |
a migration, em database/migrations/ (só se a quiseres alterar) |
Referência rápida
Facade Menu (use Gsebastiao\LaravelMenu\Facades\Menu; — nas views Blade já está disponível):
| Método | O que faz |
|---|---|
Menu::forUser($user = null) |
Itens que o utilizador pode ver, prontos a mostrar. Sem argumentos usa o utilizador autenticado. |
Menu::forUser(null, ['a', 'b']) |
O mesmo, para uma lista de permissões que indicas. |
Menu::tree() |
Todos os itens ativos, sem filtrar permissões. |
Menu::url($item) |
Link do item, ou null. |
Menu::isActive($item) |
true se o item ou um dos filhos é a página atual. |
Menu::hasPermission('a') |
true se o utilizador tiver a permissão (ou uma de várias). |
Menu::resolvePermissionsUsing(fn ($user) => ...) |
Define como obter as permissões do utilizador. |
Menu::resolveUserPermissions($user = null) |
As permissões do utilizador, tal como o pacote as vê (útil para depurar). |
Menu::rebuildCache() / Menu::flushCache() |
Reconstrói / limpa a cache. |
Menu::cacheEnabled() |
A cache está ligada? (o mesmo que config('menu.cache.enabled'), com o padrão aplicado). |
Model MenuItem:
| Uso | O que faz |
|---|---|
$item->parent, $item->children |
Pai e filhos diretos (pela ordem do menu). |
$item->childrenRecursive |
Todos os descendentes, carregados de uma vez. |
MenuItem::active(), ::roots(), ::ordered() |
Só ativos / só de topo / pela ordem do menu. |
$item->isVisibleTo($permissoes) |
O item é visível com estas permissões? |
$item->resolvedPermissionLabel() |
Nome legível da permissão (no modo id, lido da tua tabela). |
$item->audits |
Histórico de auditoria (requer o laravel-auditable). |
Comando: php artisan laravel-menu:cache (com --flush só limpa).
Resolução de problemas
O menu aparece vazio.
Confirma que há itens com is_active = true. Se usas permissões, vê o que o pacote recebe com dd(Menu::resolveUserPermissions()) — se vier vazio, revê o passo 2 das permissões.
Instalei o gsebastiao/laravel-authz e o menu continua vazio.
Confirma três coisas: o MENU_PERMISSION_MODE está em string (ou em id, se guardares ids); o campo permission de cada item é exatamente igual ao nome da permissão no auth_permissions (a comparação distingue maiúsculas de minúsculas); e a permissão chega mesmo ao utilizador — dd(Menu::resolveUserPermissions()) mostra o que o pacote está a ver.
Alterei itens e o menu não mudou.
Provavelmente alteraste-os sem passar pelos eventos do model (update em massa, SQL direto…). Corre php artisan laravel-menu:cache. Ver Cache.
Um item aparece sem link.
O nome de rota no campo route não existe (confirma com php artisan route:list), falta um parâmetro obrigatório em params, ou é um caminho sem / no início (usa /sobre, não sobre).
O super-administrador não vê todos os itens.
O pacote compara listas de permissões e não passa pelo Gate. Faz o resolver devolver todas as permissões nesse caso:
php artisan config:cache falha com "Your configuration files are not serializable".
Tens uma closure no config/menu.php (normalmente em user_permissions). Troca-a por Menu::resolvePermissionsUsing() ou por uma classe — ver Permissões.
Target class [Database\Seeders\MenuItemsSeeder] does not exist.
O seeder foi publicado por uma versão anterior a 2.1, que o copiava com o namespace errado. Publica-o de novo: php artisan vendor:publish --tag=laravel-menu-seeders --force.
Erro no arranque: "A auditoria do laravel-menu está ativada … mas o pacote … não está instalado".
Instala o pacote de auditoria (ver Auditoria) ou define MENU_AUDITING_ENABLED=false.
Erro: "Valor inválido em config('menu.permission_mode')".
O MENU_PERMISSION_MODE tem um valor desconhecido (ex.: strings). Usa none, string ou id.
Atualizar de uma versão anterior
De 2.1 para 2.2
Não há passos obrigatórios. Uma única coisa a rever: se já tens o gsebastiao/laravel-authz instalado e não configuraste nenhum resolver de permissões, o menu deixa de esconder todos os itens com permission e passa a mostrar os que cada pessoa pode ver — ver Com o gsebastiao/laravel-authz. Se tens um Menu::resolvePermissionsUsing(...) ou uma classe em config('menu.user_permissions'), nada muda.
De 2.0 para 2.1
Não há passos obrigatórios. Revê apenas:
- Se publicaste o seeder na 2.0, publica-o de novo:
php artisan vendor:publish --tag=laravel-menu-seeders --force(a cópia antiga tinha o namespace errado). - Middleware com várias permissões:
menu.permission:a,bpassou a aceitar quem tiveraoub(antes sóaera verificada). - Menus filtrados: grupos sem link que ficam vazios e separadores soltos deixaram de aparecer.
MENU_PERMISSION_MODEcom um valor desconhecido passa a dar erro (antes funcionava comostringsem avisar).
O CHANGELOG tem a lista completa.
De 1.x (gsebastiao/laravel-dynamic-menu) para 2.x
Na 2.0 o pacote mudou de nome. A tabela e os dados não mudam, e a migration não volta a correr.
-
Troca o pacote:
- No teu código, substitui
Gsebastiao\DynamicMenu\porGsebastiao\LaravelMenu\. - Se publicaste a config, renomeia
config/dynamic-menu.phpparaconfig/menu.phpe trocaconfig('dynamic-menu.…')porconfig('menu.…'). - No
.env, troca o prefixoDYNAMIC_MENU_porMENU_(ex.:DYNAMIC_MENU_PERMISSION_MODE→MENU_PERMISSION_MODE). - Modo
idsem config publicada: a tabela padrão passou depermissionsparaauth_permissions. Se usaspermissions, defineMENU_RESOLVER_TABLE=permissions. - Nos scripts de deploy, troca
php artisan dynamic-menu:cacheporphp artisan laravel-menu:cache, e as tagsdynamic-menu-*dovendor:publishporlaravel-menu-*. - Corre
php artisan laravel-menu:cache(a chave da cache mudou dedynamic_menuparalaravel_menu).
Testes
A suíte usa Pest com Orchestra Testbench e corre no CI com Laravel 11, 12 e 13, em PHP 8.2 a 8.4, com e sem o pacote de auditoria.
Licença
MIT. Ver LICENSE.
All versions of laravel-menu with dependencies
illuminate/cache Version ^11.0|^12.0|^13.0
illuminate/console Version ^11.0|^12.0|^13.0
illuminate/contracts Version ^11.0|^12.0|^13.0
illuminate/database Version ^11.0|^12.0|^13.0
illuminate/http Version ^11.0|^12.0|^13.0
illuminate/routing Version ^11.0|^12.0|^13.0
illuminate/support Version ^11.0|^12.0|^13.0
illuminate/view Version ^11.0|^12.0|^13.0