alshenetsky/easyadmin-breadcrumbs
Install the package:
composer require alshenetsky/easyadmin-breadcrumbs
Ensure your project meets the requirements: EasyAdmin 4.5+, PHP 8.0+, and Symfony 5.4+.
Create a breadcrumb class for your first CRUD action (e.g., UserIndexBreadcrumb):
// src/Controller/Admin/Breadcrumb/UserIndexBreadcrumb.php
namespace App\Controller\Admin\Breadcrumb;
use Alshenetsky\EasyAdminBreadcrumbs\Breadcrumb\AbstractBreadcrumb;
use Alshenetsky\EasyAdminBreadcrumbs\Breadcrumb\BreadcrumbType;
use App\Entity\User;
class UserIndexBreadcrumb extends AbstractBreadcrumb
{
public function getType(): BreadcrumbType { return BreadcrumbType::INDEX; }
public function getEntityFqdn(): string { return User::class; }
public function getName(): string { return 'Users'; }
}
Override EasyAdmin’s layout to render breadcrumbs:
{# templates/bundles/EasyAdminBundle/layout.html.twig #}
{% extends '@EasyAdmin/layout.html.twig' %}
{% block content_header_wrapper %}
{{ breadcrumbs() }}
{{ parent() }}
{% endblock %}
Clear cache and test:
php bin/console cache:clear
Add breadcrumbs to a list view (e.g., /admin/user). The UserIndexBreadcrumb class above will automatically render a "Users" breadcrumb when the index action of the User CRUD is accessed.
Define Breadcrumb Hierarchy:
UserIndexBreadcrumb, UserEditBreadcrumb).getParent() to link child breadcrumbs to their parents.class UserEditBreadcrumb extends AbstractBreadcrumb
{
public function getParent(): ?string { return UserIndexBreadcrumb::class; }
// ...
}
Gather and Provide Data:
gather() to extract data from AdminContext (e.g., filters, entity IDs).provide() to pass data up the hierarchy for parent breadcrumbs.public function gather(AdminContext $context): BreadcrumbData
{
return parent::gather($context)
->set('userId', $context->getEntity()->getId());
}
public function provide(BreadcrumbData $data): BreadcrumbData
{
return parent::provide($data)
->set('userId', $data->get('userId'));
}
Configure Dynamic Content:
configure() to set names, URLs, or filters dynamically.public function configure(BreadcrumbData $data): void
{
$this->setName('Editing User #'.$data->get('userId'))
->setUrl($this->getDefaultUrl()->setEntityId($data->get('userId')));
}
Handle Edge Cases:
supports() to conditionally render breadcrumbs (e.g., based on filters).public function supports(AdminContext $context): bool
{
return $context->getRequest()->query->has('active');
}
Nested CRUD Navigation:
UserEditBreadcrumb → OrderIndexBreadcrumb), pass filter data via provide().class OrderIndexBreadcrumb extends AbstractBreadcrumb
{
public function gather(AdminContext $context): BreadcrumbData
{
return parent::gather($context)
->set('userId', $context->getRequest()->get('filters')['user']['value']);
}
public function configure(BreadcrumbData $data): void
{
$this->setName('Orders for User #'.$data->get('userId'))
->setFilters(['user' => ['value' => $data->get('userId')]]);
}
}
AbstractBreadcrumb to avoid boilerplate.getEntityReference(): Fetch entities efficiently without lazy-loading issues:
$user = $this->getEntityReference(User::class, $data->get('userId'));
getDefaultUrl() or use setUrl() for non-standard routes.easyadmin.breadcrumb.gather) for custom logic.Broken Hierarchy:
provide() doesn’t match gather() keys.provide() returns the exact keys the parent expects (e.g., userId).dump($data->all()) in configure() to inspect passed data.Filter Reset Issues:
userId) breaks breadcrumbs.BreadcrumbNotApplicableException in gather()/provide():
public function gather(AdminContext $context): BreadcrumbData
{
$userId = $context->getRequest()->get('filters', [])['userId']['value'] ?? throw new BreadcrumbNotApplicableException();
return parent::gather($context)->set('userId', $userId);
}
Entity Not Found:
getEntityReference() fails if the entity ID is invalid.configure() or use try-catch:
try {
$user = $this->getEntityReference(User::class, $id);
} catch (\Exception) {
$this->setName('Editing User (ID: '.$id.')');
}
Circular Dependencies:
A → B → A).getParent() references. Use a flat hierarchy or middleware to resolve dependencies.Twig Template Overrides:
{{ breadcrumbs() }} doesn’t render.templates/bundles/EasyAdminBundle/layout.html.twig) and the block is extended properly.gather()/configure():
$this->logger->debug('Breadcrumb data:', $data->all());
AdminContext: Verify the current route matches getType()/getEntityFqdn():
dump($context->getCrud()->getEntityFqcn(), $context->getRequest()->get('_route'));
Custom Breadcrumb Types:
BreadcrumbType enum for non-CRUD actions (e.g., CUSTOM_ACTION).enum BreadcrumbType { INDEX; EDIT; CUSTOM_ACTION; }
Dynamic Parent Resolution:
getParent() to resolve parents dynamically (e.g., based on route parameters):
public function getParent(): ?string
{
return match ($this->getEntityFqdn()) {
User::class => UserIndexBreadcrumb::class,
default => null,
};
}
Breadcrumb Twig Extensions:
breadcrumbs() function to customize rendering:
{% macro breadcrumbs() %}
<nav aria-label="Breadcrumbs">
{% for breadcrumb in breadcrumbs %}
<a href="{{ breadcrumb.url }}">{{ breadcrumb.name }}</a>
{% endfor %}
</nav>
{% endmacro %}
Event Listeners:
easyadmin.breadcrumb.gather to modify data globally:
// src/EventListener/BreadcrumbListener.php
public function onGather(BreadcrumbGatherEvent $event)
{
$event->setData($event->getData()->set('customKey', 'value'));
}
Performance:
gather()/configure(). Use lazy-loading for entities:
$user = $this->getEntityManager()->getRepository(User::class)->find($id);
How can I help you explore Laravel packages today?