chamber-orchestra/doctrine-slug-bundle
Install the Bundle
Add to your composer.json:
composer require chamber-orchestra/doctrine-slug-bundle
Enable in config/bundles.php:
return [
// ...
ChamberOrchestra\DoctrineSlugBundle\ChamberOrchestraDoctrineSlugBundle::class => ['all' => true],
];
Annotate an Entity
Use the #[Slug] attribute on a property (e.g., title):
use ChamberOrchestra\DoctrineSlugBundle\Attribute\Slug;
#[ORM\Entity]
class Article
{
#[Slug]
#[ORM\Column(length: 255)]
private string $title;
#[ORM\Column(length: 255, unique: true)]
private string $slug;
}
First Use Case
Save an Article instance—slugs auto-generate on persist()/flush():
$article = new Article();
$article->setTitle("Laravel 10 Features");
$entityManager->persist($article);
$entityManager->flush(); // Slug auto-populates (e.g., "laravel-10-features")
Attribute-Based Configuration Customize slug behavior via attributes:
#[Slug(
generator: 'custom', // Use a custom generator service
separator: '-',
unique: true, // Auto-increment on collision (e.g., "-1")
fields: ['title', 'category'] // Multi-field slugs
)]
Event-Driven Hooks
Subscribe to SlugGenerateEvent for pre/post-processing:
use ChamberOrchestra\DoctrineSlugBundle\Event\SlugGenerateEvent;
$dispatcher->addListener(SlugGenerateEvent::class, function (SlugGenerateEvent $event) {
$event->setSlug(strtolower($event->getSlug()));
});
Multi-Entity Integration Reuse generators across entities by tagging services:
# config/services.yaml
services:
App\Slug\CustomGenerator:
tags: [chamber_orchestra.doctrine_slug.generator]
doctrine/orm (v2.11+) via spatie/laravel-doctrine-orm for Laravel integration.handle():
public function handle(): void
{
$this->validate();
$model = new Article($this->validated());
$entityManager->persist($model);
$entityManager->flush(); // Slug auto-populates
}
Collision Handling
-1, -2 on duplicates. Override via unique: false or a custom generator.SlugGenerateEvent payload for collision details.Circular Dependencies
Article slug → Category slug).#[Slug(unique: false)] or lazy-load slugs.Case Sensitivity
#[Slug(separator: '_', unique: true)]
private string $name; // "Hello World" → "hello_world"
SlugGenerateEvent:
$dispatcher->addListener(SlugGenerateEvent::class, function ($event) {
\Log::debug('Slug generation:', [
'entity' => $event->getEntity(),
'input' => $event->getInput(),
'slug' => $event->getSlug(),
]);
});
php bin/console cache:clear
Custom Generators
Implement SlugGeneratorInterface:
use ChamberOrchestra\DoctrineSlugBundle\SlugGenerator\SlugGeneratorInterface;
class CustomSlugGenerator implements SlugGeneratorInterface
{
public function generate(array $input): string
{
return str_replace(' ', '-', $input['title']);
}
}
Register in services.yaml:
services:
App\Slug\CustomSlugGenerator:
tags: [chamber_orchestra.doctrine_slug.generator]
Doctrine Lifecycle Events
Hook into prePersist/preUpdate for manual control:
use Doctrine\ORM\Event\LifecycleEventArgs;
$entity->addLifecycleCallback(function ($entity, LifecycleEventArgs $args) {
if ($args->getEventName() === 'prePersist') {
$entity->setSlug('custom-slug');
}
});
Testing Mock slug generation in PHPUnit:
$this->getContainer()->get('chamber_orchestra.doctrine_slug.generator.custom')
->expects($this->once())
->method('generate')
->willReturn('test-slug');
How can I help you explore Laravel packages today?