承接 offload-project/laravel-navigation 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

offload-project/laravel-navigation

Composer 安装命令:

composer require offload-project/laravel-navigation

包简介

Configurable navigation package for Laravel with breadcrumb and tree generation

README 文档

README

Latest Version on Packagist GitHub Tests Action Status Total Downloads

Laravel Navigation

A powerful, flexible navigation management package for Laravel. Define multiple navigation structures with breadcrumbs, active state detection, and pre-compiled icons — perfect for Inertia.js, React, Vue, and Blade applications.

Features

  • Multiple Navigations — Define unlimited nav structures (main, footer, sidebar, user menu)
  • Fluent Builder API — IDE-friendly builder with full autocomplete support
  • Sections & Groups — Organize items with top-level sections and collapsible groups
  • Route-Based — Use Laravel route names with full IDE autocomplete
  • Breadcrumb Generation — Auto-generate breadcrumbs from your navigation config
  • Active State Detection — Smart detection of active items and their parents
  • Authorization — Built-in can and visible attributes for permissions
  • Pre-compiled Icons — Compile Lucide icons to inline SVG for optimal performance
  • Action Support — POST/DELETE actions for logout, form submissions, etc.
  • Wildcard Parameters — Match dynamic routes like /users/{id}/edit in breadcrumbs
  • Custom Metadata — Attach badges, feature flags, or any data to nav items

Table of Contents

Requirements

  • PHP 8.3+
  • Laravel 11/12/13

Installation

composer require offload-project/laravel-navigation

Optionally publish the configuration:

php artisan vendor:publish --tag=navigation-config

Quick Start

Using the Fluent Builder (Recommended)

The fluent builder provides full IDE autocomplete and a clean, readable syntax:

use OffloadProject\Navigation\Item;

return [
    'navigations' => [
        'main' => [
            Item::make('Dashboard')->route('dashboard')->icon('home'),
            Item::make('Users')
                ->route('users.index')
                ->icon('users')
                ->can('view-users')
                ->children([
                    Item::make('All Users')->route('users.index'),
                    Item::make('Roles')->route('roles.index'),
                ]),
            Item::make('Settings')->route('settings')->icon('settings'),
        ],
    ],
];

Using Helper Functions

Global helper functions provide the most concise syntax:

return [
    'navigations' => [
        'main' => [
            nav_item('Dashboard', 'dashboard', 'home'),
            nav_item('Users', 'users.index', 'users')
                ->can('view-users')
                ->children([
                    nav_item('All Users', 'users.index'),
                    nav_item('Roles', 'roles.index'),
                ]),
            nav_separator(),
            nav_item('Settings', 'settings', 'settings'),
            nav_external('Documentation', 'https://docs.example.com', 'book'),
            nav_action('Logout', 'logout', 'post', 'log-out'),
        ],
    ],
];

Available helpers:

  • nav_item($label, $route?, $icon?) — Standard navigation item
  • nav_group($label, $children?, $icon?) — Collapsible group
  • nav_section($label, $children?, $icon?) — Top-level section containing items and groups
  • nav_separator() — Visual separator
  • nav_divider($spacing?) — Divider with optional spacing
  • nav_external($label, $url, $icon?) — External link
  • nav_action($label, $route, $method?, $icon?) — POST/DELETE action

Using Shorthand Syntax

For quick configuration, use the shorthand array syntax:

return [
    'navigations' => [
        'main' => [
            ['Dashboard', 'dashboard', 'home'],           // [label, route, icon]
            ['Users', 'users.index', 'users'],            // [label, route, icon]
            ['Docs', 'https://docs.example.com', 'book'], // URLs auto-detected
        ],
    ],
];

Using Array Syntax

The traditional array syntax is still fully supported:

return [
    'navigations' => [
        'main' => [
            ['label' => 'Dashboard', 'route' => 'dashboard', 'icon' => 'home'],
            [
                'label' => 'Users',
                'route' => 'users.index',
                'icon' => 'users',
                'can' => 'view-users',
                'children' => [
                    ['label' => 'All Users', 'route' => 'users.index'],
                    ['label' => 'Roles', 'route' => 'roles.index'],
                ],
            ],
        ],
    ],
];

Runtime Registration

Register navigations at runtime in your service provider or middleware:

use OffloadProject\Navigation\Facades\Navigation;
use OffloadProject\Navigation\Item;

// Fluent builder
Navigation::register('sidebar')
    ->item('Dashboard', 'dashboard', 'home')
    ->item('Users', 'users.index', 'users')
        ->child('All Users', 'users.index')
        ->child('Create User', 'users.create')
    ->separator()
    ->section('Admin', [
        Item::make('Roles')->route('admin.roles'),
        Item::group('Settings', [
            Item::make('General')->route('settings.general'),
        ]),
    ], 'shield')
    ->item('Settings', 'settings', 'settings')
    ->done();

// Or with Item instances
Navigation::addNavigation('sidebar', [
    Item::make('Dashboard')->route('dashboard')->icon('home'),
    Item::make('Users')->route('users.index')->icon('users'),
]);

Getting Navigation Data

use OffloadProject\Navigation\Facades\Navigation;

// Get navigation items
$items = Navigation::get('main')->items();

// Get breadcrumbs (auto-detects current route)
$breadcrumbs = Navigation::breadcrumbs('main');

// Check if navigation exists
if (Navigation::has('sidebar')) {
    // ...
}

// Get all navigation names
$names = Navigation::names();

Pass to your frontend:

// Inertia.js
return inertia('Dashboard', [
    'navigation' => Navigation::get('main')->items(),
    'breadcrumbs' => Navigation::breadcrumbs('main'),
]);

Fluent Builder API

The Item class provides a fluent interface with full IDE support:

use OffloadProject\Navigation\Item;

// Basic item
Item::make('Dashboard')->route('dashboard')->icon('home')

// Shorthand with route and icon
Item::to('Dashboard', 'dashboard', 'home')

// External link
Item::external('Documentation', 'https://docs.example.com', 'book')

// Action (POST/DELETE)
Item::action('Logout', 'logout', 'post', 'log-out')

// Separator
Item::separator()

// Divider with spacing
Item::divider('large')

// Group (collapsible sub-menu)
Item::group('Settings', [
    Item::make('Profile')->route('settings.profile'),
])

// Section (top-level container for items and groups)
Item::section('Workspace', [
    Item::make('Dashboard')->route('dashboard'),
    Item::group('Settings', [...]),
])

// With children
Item::make('Settings')
    ->route('settings')
    ->icon('settings')
    ->children([
        Item::make('Profile')->route('settings.profile'),
        Item::make('Security')->route('settings.security'),
    ])

// With authorization
Item::make('Admin')
    ->route('admin')
    ->can('access-admin')

// With visibility
Item::make('Dashboard')
    ->route('dashboard')
    ->visible(fn () => auth()->check())
    ->whenAuthenticated()  // Shorthand for authenticated users
    ->whenGuest()          // Shorthand for guests only

// With badge
Item::make('Notifications')
    ->route('notifications')
    ->badge(5)
    ->badge(fn () => auth()->user()->unreadCount(), 'red')

// For breadcrumbs only
Item::make(fn ($user) => "Edit: {$user->name}")
    ->route('users.edit')
    ->params(['user' => '*'])
    ->breadcrumbOnly()

// For navigation only
Item::make('Admin Section')
    ->route('admin')
    ->navOnly()

// Custom metadata
Item::make('Dashboard')
    ->route('dashboard')
    ->meta('badge', 5)
    ->meta('feature', 'beta')

Groups & Sections

Use groups for collapsible sub-menus and sections for top-level structural containers that can hold both items and groups.

Groups

Organize navigation items into collapsible groups with headers:

use OffloadProject\Navigation\Item;

return [
    'navigations' => [
        'sidebar' => [
            Item::make('Dashboard')->route('dashboard')->icon('home'),

            Item::group('Settings', [
                Item::make('Profile')->route('settings.profile'),
                Item::make('Security')->route('settings.security'),
                Item::make('Notifications')->route('settings.notifications'),
            ])->icon('cog'),

            Item::group('Administration', [
                Item::make('Users')->route('admin.users'),
                Item::make('Roles')->route('admin.roles'),
            ])->icon('shield')->collapsed(), // Starts collapsed
        ],
    ],
];

Or with helper functions:

return [
    'navigations' => [
        'sidebar' => [
            nav_item('Dashboard', 'dashboard', 'home'),

            nav_group('Settings', [
                nav_item('Profile', 'settings.profile'),
                nav_item('Security', 'settings.security'),
            ], 'cog'),

            nav_group('Admin', [], 'shield')
                ->collapsed()
                ->can('access-admin')
                ->children([
                    nav_item('Users', 'admin.users'),
                    nav_item('Roles', 'admin.roles'),
                ]),
        ],
    ],
];

Group Options

// Basic group
Item::group('Settings', [...])

// With icon
Item::group('Settings', [...])->icon('cog')

// Not collapsible (always expanded)
Item::group('Main')->collapsible(false)

// Starts collapsed
Item::group('Advanced')->collapsed()

// With authorization
Item::group('Admin')->can('access-admin')

// With visibility
Item::group('Beta')->visible(config('features.beta'))

// Nested groups
Item::group('Settings', [
    Item::make('General')->route('settings.general'),
    Item::group('Advanced', [
        Item::make('API Keys')->route('settings.api'),
        Item::make('Webhooks')->route('settings.webhooks'),
    ])->collapsed(),
])

Group Output

Groups output with these additional fields:

[
    'id' => 'nav-sidebar-1',
    'label' => 'Settings',
    'url' => null,           // Groups don't have URLs
    'isActive' => true,      // Active if any child is active
    'icon' => '<svg>...</svg>',
    'group' => true,         // Identifies this as a group
    'collapsible' => true,   // Can be collapsed
    'collapsed' => false,    // Default collapsed state
    'children' => [...],
]

Sections

Sections are top-level structural containers that can hold both items and groups. They support all the same fluent options as groups but default to non-collapsible so they read as structural dividers (e.g., WORKSPACE, ADMIN).

use OffloadProject\Navigation\Item;

return [
    'navigations' => [
        'sidebar' => [
            Item::section('Workspace', [
                Item::make('Dashboard')->route('dashboard')->icon('home'),
                Item::group('Settings', [
                    Item::make('Profile')->route('settings.profile'),
                    Item::make('Security')->route('settings.security'),
                ])->icon('cog'),
            ])->icon('layers'),

            Item::section('Admin', [
                Item::make('Users')->route('admin.users'),
                Item::make('Roles')->route('admin.roles'),
            ])->can('access-admin'),
        ],
    ],
];

Or with helper functions:

return [
    'navigations' => [
        'sidebar' => [
            nav_section('Workspace', [
                nav_item('Dashboard', 'dashboard', 'home'),
                nav_group('Settings', [
                    nav_item('Profile', 'settings.profile'),
                ], 'cog'),
            ], 'layers'),
        ],
    ],
];

Or via the runtime builder:

Navigation::register('sidebar')
    ->section('Workspace', [
        Item::make('Dashboard')->route('dashboard'),
        Item::group('Settings', [
            Item::make('Profile')->route('settings.profile'),
        ]),
    ])
    ->done();

Section Options

Sections support every option groups support — icons, gates, badges, custom meta, visibility, and (opt-in) collapsibility:

// Default: non-collapsible structural divider
Item::section('Workspace', [...])

// Make it collapsible (and optionally start collapsed)
Item::section('Advanced', [...])->collapsible()->collapsed()

// With icon and authorization
Item::section('Admin', [...])->icon('shield')->can('access-admin')

// With a badge
Item::section('Notifications', [...])->badge(3)

Nesting Rules

Sections are top-level only. Nesting a section inside another section or inside a group throws InvalidNavigationItemException:

// Not allowed — sections cannot be nested
Item::section('Outer', [
    Item::section('Inner', [...]),  // throws
])

// Not allowed — groups cannot contain sections
Item::group('Settings', [
    Item::section('Inner', [...]),  // throws
])

Section Output

Sections output with these additional fields:

[
    'id' => 'nav-sidebar-0',
    'label' => 'Workspace',
    'isActive' => true,       // Active if any child is active
    'icon' => '<svg>...</svg>',
    'section' => true,        // Identifies this as a section
    'collapsible' => false,   // Defaults to false (opt in via ->collapsible())
    'collapsed' => false,
    'children' => [...],      // Items and/or groups
]

Route Parameters

Pass parameters for routes that require them:

// For routes like /users/{user}/posts
$items = Navigation::get('sidebar')->items(['user' => $user->id]);

Authorization

Use the can attribute to check gates or policies:

Item::make('Admin')
    ->route('admin.index')
    ->can('access-admin')

// With policy model
Item::make('Edit Post')
    ->route('posts.edit')
    ->can(['update', $post])

Items are automatically hidden when the user isn't authenticated or lacks permission.

For non-authorization logic (feature flags, environment checks), use visible:

Item::make('Beta Features')
    ->route('beta')
    ->visible(config('features.beta'))

// Or with closures
Item::make('Debug')
    ->route('debug')
    ->visible(fn () => app()->isLocal())

Action Items

Define items that trigger POST/DELETE requests:

Item::action('Logout', 'logout', 'post', 'log-out')

// Or with array syntax
['label' => 'Logout', 'route' => 'logout', 'method' => 'post', 'icon' => 'log-out']

Handle in your frontend by checking for the method key and using a form or Inertia's router.post().

Breadcrumbs & Wildcards

Handle CRUD pages elegantly with breadcrumbOnly and wildcard parameters:

Item::make('Users')
    ->route('users.index')
    ->children([
        Item::make(fn ($user) => "Edit: {$user->name}")
            ->route('users.edit')
            ->params(['user' => '*'])
            ->breadcrumbOnly(),
    ])

When visiting /users/5/edit:

  • Navigation shows only "Users"
  • Breadcrumbs show "Users > Edit: John Doe"
  • Active state marks "Users" as active

Breadcrumbs API

// Auto-detect current route, search all navigations
$breadcrumbs = Navigation::breadcrumbs();

// Search specific navigation
$breadcrumbs = Navigation::breadcrumbs('main');

// Specify route explicitly
$breadcrumbs = Navigation::breadcrumbs('main', 'users.edit');

// With route parameters
$breadcrumbs = Navigation::breadcrumbs('main', 'users.edit', ['user' => $user]);

Visibility Control

Use navOnly and breadcrumbOnly to control where items appear:

Item::make('Admin Section')
    ->route('admin.index')
    ->navOnly()  // Shows in nav, excluded from breadcrumbs
    ->children([
        Item::make('Users')->route('admin.users'),
        Item::make(fn ($user) => "Edit {$user->name}")
            ->route('admin.users.edit')
            ->params(['user' => '*'])
            ->breadcrumbOnly(),  // Shows in breadcrumbs, excluded from nav
    ])
  • navOnly — Section headers that would be redundant in breadcrumbs
  • breadcrumbOnly — Edit/show pages that shouldn't clutter navigation

Custom Metadata

Attach any data to navigation items:

Item::make('Notifications')
    ->route('notifications')
    ->badge(5)
    ->meta('badgeColor', 'red')
    ->meta('feature', 'new')

// Or with array syntax
[
    'label' => 'Notifications',
    'route' => 'notifications',
    'badge' => 5,
    'badgeColor' => 'red',
]

Custom keys pass through unchanged to your frontend.

Icon Compilation

Pre-compile Lucide icons to inline SVG for optimal performance:

php artisan navigation:compile-icons

Add to your deployment pipeline for production builds.

Route Validation

Validate all route references exist:

php artisan navigation:validate

Add to CI/CD to catch broken navigation links early.

Inertia.js Integration

Share navigation globally via middleware:

// app/Http/Middleware/HandleInertiaRequests.php
public function share(Request $request): array
{
    return [
        ...parent::share($request),
        'navigation' => Navigation::get('main')->items(),
        'breadcrumbs' => Navigation::breadcrumbs('main'),
    ];
}

Output Format

The items() method returns a frontend-ready structure:

[
    [
        'id' => 'nav-main-0',
        'label' => 'Dashboard',
        'url' => '/dashboard',
        'isActive' => true,
        'icon' => '<svg>...</svg>',
        'children' => [],
    ],
    // ...
]

The breadcrumbs() method returns:

[
    ['id' => 'breadcrumb-main-0-1', 'label' => 'Users', 'route' => 'users.index', 'url' => '/users'],
    ['id' => 'breadcrumb-main-1-0', 'label' => 'Edit: John', 'route' => 'users.edit', 'url' => '/users/1/edit', 'isActive' => true],
]

The last breadcrumb includes isActive => true to identify the current page.

Configuration Reference

Option Type Description
label string|Closure Display text (closures receive route models)
route string Laravel route name
url string External URL (alternative to route)
method string HTTP method (post, delete)
icon string Lucide icon name
children array Nested navigation items
visible bool|Closure Visibility condition
can string|array Gate/policy check
breadcrumbOnly bool Hide from nav, show in breadcrumbs
navOnly bool Show in nav, hide from breadcrumbs
params array Route parameters (['id' => '*'] for wildcards)

Error Messages

The package provides helpful error messages when configuration is invalid:

Navigation item cannot have both "route" and "url". Use "route" for internal
Laravel routes (e.g., "users.index") or "url" for external links
(e.g., "https://docs.example.com"), but not both.
See: https://github.com/offload-project/laravel-navigation#routing

Migration Guide

Upgrading to v1.1

Breaking Changes

Icon Storage Format Changed

Compiled icons are now stored as JSON instead of PHP. If you have previously compiled icons:

# Recompile your icons to use the new format
php artisan navigation:compile-icons

The old PHP format (storage/navigation/icons.php) will still be loaded for backwards compatibility, but new compilations will use JSON (storage/navigation/icons.json).

NavigationManager Constructor

If you're manually instantiating NavigationManager, the constructor now requires an ItemVisibilityResolver instance:

// Before
new NavigationManager($config, $iconCompiler);

// After
new NavigationManager($config, $iconCompiler, $visibilityResolver);

Most users won't be affected as the class is typically resolved from the container.

Deprecated Methods

The following methods are deprecated and will be removed in v2.0:

Deprecated Use Instead
->toTree() ->items()
->getBreadcrumbs() ->breadcrumbs()

AI Coding Assistant Skill

This package ships a Laravel Boost skill so coding assistants (Claude Code, Cursor, etc.) follow the package's conventions when generating code. Install it in your app with:

php artisan boost:add-skill offload-project/laravel-navigation

The skill source lives at skills/SKILL.md.

Testing

composer test

Contributing

Contributions are welcome! Please see the documents below before getting started.

Security

License

The MIT License (MIT). Please see License File for more information.

offload-project/laravel-navigation 适用场景与选型建议

offload-project/laravel-navigation 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 2.14k 次下载、GitHub Stars 达 4, 最近一次更新时间为 2025 年 12 月 16 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「navigation」 「laravel」 「nav」 「wayfinder」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 offload-project/laravel-navigation 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 offload-project/laravel-navigation 我们能提供哪些服务?
定制开发 / 二次开发

基于 offload-project/laravel-navigation 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 2.14k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 4
  • 点击次数: 31
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 4
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-12-16