ramir1 / laravel-breadcrumbs-plus
Define breadcrumbs as classes instead of closures, resolved through the container, with or without registering a name - on top of diglactic/laravel-breadcrumbs.
Package info
github.com/ramir1/laravel-breadcrumbs-plus
pkg:composer/ramir1/laravel-breadcrumbs-plus
Requires
- php: ^8.1
- diglactic/laravel-breadcrumbs: ^10.0
- laravel/framework: ^10.0 || ^11.0 || ^12.0 || ^13.0
Requires (Dev)
- orchestra/testbench: ^8.0 || ^9.0 || ^10.0 || ^11.0
- phpunit/phpunit: ^10.5 || ^11.5 || ^12.5
This package is auto-updated.
Last update: 2026-07-12 05:57:04 UTC
README
Adds Breadcrumbs::rule() on top of diglactic/laravel-breadcrumbs —
register a breadcrumb by class name (resolved through the container) instead of a closure, so the class is only
instantiated when that specific breadcrumb is generated, not for every registered page on every request. The method
defaults to __invoke for single-purpose classes and supports constructor dependency injection, matching how
controllers work.
This started as a pull request against diglactic/laravel-breadcrumbs which wasn't merged upstream. Rather than
maintaining a fork of the whole package, this is a small extension package: it requires the vanilla
diglactic/laravel-breadcrumbs and points its manager-class / generator-class config hooks (already built into
that package for this exact purpose) at extended Manager and Generator classes.
Installation
composer require ramir1/laravel-breadcrumbs-plus
Laravel's package auto-discovery registers the service provider automatically. No further setup is required.
IDE type hints
render()/generate()/view() accept a [class, method] pair at runtime (see "Rendering by class
directly" below), because this package swaps the bound Manager class via
config('breadcrumbs.manager-class'). IDEs like PhpStorm don't follow that indirection - they read
the type hints straight off Diglactic\Breadcrumbs\Breadcrumbs's docblock (?string $name), so
passing an array there gets flagged even though it works correctly. Import
Ramir1\BreadcrumbsPlus\Breadcrumbs instead of Diglactic\Breadcrumbs\Breadcrumbs to get a correct
docblock - it's the same facade, resolving to the exact same singleton, just with accurate types:
use Ramir1\BreadcrumbsPlus\Breadcrumbs; // instead of Diglactic\Breadcrumbs\Breadcrumbs Breadcrumbs::render([PostBreadcrumb::class, 'show'], $post);
Usage
// routes/breadcrumbs.php use Diglactic\Breadcrumbs\Breadcrumbs; use App\Breadcrumbs\PostBreadcrumb; Breadcrumbs::rule('post', PostBreadcrumb::class); Breadcrumbs::rule('post.edit', PostEditBreadcrumb::class, 'edit');
// app/Breadcrumbs/PostBreadcrumb.php namespace App\Breadcrumbs; use Diglactic\Breadcrumbs\Generator; class PostBreadcrumb { public function __construct(private SomeService $service) { } public function __invoke(Generator $trail, Post $post): void { $trail->parent('posts'); $trail->push($post->title, route('posts.show', $post)); } }
Breadcrumbs::rule($name, $class, $method = '__invoke') behaves like Breadcrumbs::for(), except the callback is a
[$class, $method] pair. $class is resolved through the Laravel container the moment the breadcrumb is actually
generated (like a controller), so it can use constructor dependency injection without being instantiated on every
request that merely registers it.
Registering rules from a service provider instead of routes/breadcrumbs.php
routes/breadcrumbs.php is not required. Breadcrumbs::rule() / Breadcrumbs::rules() can be called from anywhere
once the container is available — including a package or module's own ServiceProvider::boot(). This is useful in a
modular app where breadcrumb classes live next to the feature they belong to, rather than in one central file that
has to know about every module:
// Modules/Posts/PostsServiceProvider.php use Diglactic\Breadcrumbs\Breadcrumbs; class PostsServiceProvider extends ServiceProvider { public function boot(): void { Breadcrumbs::rules([ 'posts' => PostsIndexBreadcrumb::class, 'posts.show' => PostBreadcrumb::class, 'posts.edit' => [PostBreadcrumb::class, 'edit'], ]); } }
Breadcrumbs::rules(array $rules) registers many rules in one call. Each entry is either a class name (defaults to
__invoke) or a [$class, $method] pair, keyed by page name — same rules as rule(), just batched.
If config('breadcrumbs.files') is left at its default (routes/breadcrumbs.php) and that file doesn't exist,
diglactic/laravel-breadcrumbs silently skips loading it, so nothing needs to be disabled explicitly.
Rendering by class directly, without registering a name first
render(), generate(), and view() also accept a [$class, $method] pair in place of a registered name -
$method defaults to __invoke, same as rule(). This calls the class/method directly through the container,
skipping the rule() registration step entirely. Useful for a one-off page whose breadcrumb isn't reused/shared
under a name:
// Instead of: // Breadcrumbs::rule('post.show', PostBreadcrumb::class); // ... // Breadcrumbs::render('post.show', $post); Breadcrumbs::render([PostBreadcrumb::class], $post);
It works the same way with a custom view - e.g. the breadcrumbs::json-ld view bundled with
diglactic/laravel-breadcrumbs, for structured data instead of the usual HTML list:
{{ Breadcrumbs::view('breadcrumbs::json-ld', [PageBreadcrumb::class, 'show'], $page) }}
Inside the class, $trail->parent() accepts the same [$class, $method] form to reference an ancestor breadcrumb
directly, instead of a registered name:
class PostBreadcrumb { public function __invoke(Generator $trail, Post $post): void { $trail->parent([PostsIndexBreadcrumb::class]); // instead of $trail->parent('posts') $trail->push($post->title, route('posts.show', $post)); } }
This complements rule() rather than replacing it - breadcrumbs reused across several pages are still worth
registering under a name. It also doesn't affect route-bound rendering: Breadcrumbs::render() called with no
arguments still resolves via the current route's name, which still needs a matching for()/rule() entry.
How it works
diglactic/laravel-breadcrumbs already resolves its Manager and Generator classes through the container and
exposes config('breadcrumbs.manager-class') / config('breadcrumbs.generator-class') specifically so they can be
subclassed for "more advanced customisations". This package's ServiceProvider sets those two config values to its
own Manager (adds rule()) and Generator (resolves [class, method] callbacks) subclasses — no files from
diglactic/laravel-breadcrumbs are modified or copied.
License
MIT