chamber-orchestra/breadcrumbs
Laravel breadcrumbs helper for Chamber Orchestra apps. Define breadcrumb trails in code, generate navigable links for pages, and keep UI navigation consistent across routes/views. Lightweight package aimed at simple breadcrumb management in PHP.
Installation Add the package via Composer:
composer require chamber-orchestra/breadcrumbs
No Laravel-specific setup is needed since this is a Symfony package, but it integrates seamlessly with Laravel’s service container.
First Use Case: Basic Breadcrumb Collection Create a breadcrumb collection in a controller or service:
use ChamberOrchestra\Breadcrumbs\BreadcrumbCollection;
$breadcrumbs = new BreadcrumbCollection();
$breadcrumbs->add('Home', '/');
$breadcrumbs->add('Products', '/products');
$breadcrumbs->add('Laptops', '/products/laptops');
Render in a Blade view:
@foreach ($breadcrumbs as $breadcrumb)
<a href="{{ $breadcrumb->getUrl() }}">{{ $breadcrumb->getTitle() }}</a>
@if (!$loop->last) > @endif
@endforeach
Where to Look First
BreadcrumbCollection, Breadcrumb, and interfaces (ArrayAccess, IteratorAggregate, Countable) in the package’s src/ directory.BreadcrumbCollection in controllers/services.Dynamic Breadcrumbs in Controllers Build breadcrumbs dynamically based on route parameters:
public function show(Product $product)
{
$breadcrumbs = new BreadcrumbCollection();
$breadcrumbs->add('Home', '/');
$breadcrumbs->add('Products', '/products');
$breadcrumbs->add($product->name, route('products.show', $product));
return view('product.show', compact('breadcrumbs'));
}
Reusable Breadcrumb Services Extract breadcrumb logic into a dedicated service:
class ProductBreadcrumbService
{
public function build(Product $product): BreadcrumbCollection
{
$collection = new BreadcrumbCollection();
$collection->add('Home', '/');
$collection->add('Products', '/products');
$collection->add($product->category->name, route('categories.show', $product->category));
$collection->add($product->name, route('products.show', $product));
return $collection;
}
}
Inject the service into controllers:
public function __construct(private ProductBreadcrumbService $breadcrumbService) {}
public function show(Product $product)
{
$breadcrumbs = $this->breadcrumbService->build($product);
// ...
}
Middleware for Global Breadcrumbs Attach breadcrumbs to the request in middleware:
public function handle(Request $request, Closure $next)
{
$breadcrumbs = new BreadcrumbCollection();
// Logic to populate breadcrumbs (e.g., from route, auth, etc.)
$request->breadcrumbs = $breadcrumbs;
return $next($request);
}
Access in views:
@foreach (request()->breadcrumbs as $breadcrumb)
{{ $breadcrumb->getTitle() }}
@endforeach
Integration with Laravel’s View Composers Use a composer to inject breadcrumbs into all views:
public function compose(View $view)
{
$view->with('breadcrumbs', $this->breadcrumbService->buildForCurrentRoute());
}
Route facade to generate URLs dynamically:
$breadcrumbs->add('Profile', route('profile.show'));
Breadcrumb to support localized titles:
class LocalizedBreadcrumb extends Breadcrumb
{
public function __construct(string $titleKey, array $parameters = [])
{
$this->title = trans($titleKey, $parameters);
}
}
$breadcrumbs = Cache::remember('breadcrumbs.home', now()->addHours(1), function () {
return $this->buildHomeBreadcrumbs();
});
Symfony vs. Laravel Namespace Conflicts
use ChamberOrchestra\Breadcrumbs\BreadcrumbCollection as SymfonyBreadcrumbCollection;
View::share() or request binding over global static instances to avoid polluting the global namespace.Immutable Collections
BreadcrumbCollection is mutable, but methods like add() return void. Chain operations carefully:
// Correct: Modify the existing collection
$breadcrumbs->add('Step 1');
$breadcrumbs->add('Step 2');
// Incorrect: Assumes a new collection is returned (it doesn’t)
$newBreadcrumbs = $breadcrumbs->add('Step 1')->add('Step 2');
URL Generation in Tests
route() helper or use URL::to() in tests to avoid "No route defined" errors:
$breadcrumbs->add('Test', URL::to('/test'));
Performance with Large Collections
ArrayAccess to limit rendering:
@foreach ($breadcrumbs as $index => $breadcrumb)
@if ($index < 5) {{ $breadcrumb->getTitle() }} @endif
@endforeach
Dump the Collection
Use dd() to inspect the collection structure:
dd($breadcrumbs->getIterator()->getArrayCopy());
Output:
[
0 => Breadcrumb {#323
title: "Home",
url: "/",
},
1 => Breadcrumb {#324
title: "Products",
url: "/products",
},
]
Check for Circular References If breadcrumbs cause memory issues, ensure no circular references exist (e.g., a breadcrumb linking to itself).
Symfony Component Logs Enable Symfony’s debug mode to catch low-level issues:
$this->container->setParameter('kernel.debug', true);
Extend Breadcrumb for Custom Logic
Create a custom breadcrumb class for reusable behavior:
class ActiveBreadcrumb extends Breadcrumb
{
public function isActive(): bool
{
return request()->is($this->getUrl());
}
}
Usage in Blade:
<li class="{{ $breadcrumb->isActive() ? 'active' : '' }}">
<a href="{{ $breadcrumb->getUrl() }}">{{ $breadcrumb->getTitle() }}</a>
</li>
Use Countable for Conditional Rendering
Check if breadcrumbs exist before rendering:
@if (count($breadcrumbs) > 0)
<nav class="breadcrumbs">
@foreach ($breadcrumbs as $breadcrumb)
{{ $breadcrumb->getTitle() }}
@endforeach
</nav>
@endif
Laravel Service Provider Binding Bind the collection to the container for easier access:
public function register()
{
$this->app->bind(BreadcrumbCollection::class, function ($app) {
return new BreadcrumbCollection();
});
}
Now autowire BreadcrumbCollection in any class:
public function __construct(private BreadcrumbCollection $breadcrumbs) {}
Avoid Hardcoding URLs
Use Laravel’s route() or URL::to() helpers to ensure URLs are generated correctly in all environments (local/dev/prod):
$breadcrumbs->add('Dashboard', route('dashboard'));
Testing Breadcrumbs Test breadcrumb logic in isolation:
public function test_breadcrumbs()
{
$collection = new BreadcrumbCollection();
$collection->add('Home', '/');
$this->assertCount(1, $collection);
$this->assertEquals('Home', $collection->first()->getTitle());
}
How can I help you explore Laravel packages today?