Installation:
composer require sonata-project/doctrine-extensions
Add to config/app.php under providers:
Sonata\DoctrineExtensions\Provider\DoctrineExtensionsProvider::class,
Enable Extensions:
Register the extensions in config/doctrine-extensions.php (auto-generated after installation):
return [
'orm' => [
'timestampable' => true,
'sluggable' => true,
'tree' => true,
// ...other extensions
],
];
First Use Case:
Add Timestampable to an entity to auto-track createdAt/updatedAt:
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @Gedmo\Timestampable(on="create")
*/
private $createdAt;
/**
* @Gedmo\Timestampable(on="update")
*/
private $updatedAt;
Slug Generation:
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @Gedmo\Slug(fields={"title"}, unique=true)
*/
private $slug;
updateSlug() manually if title changes outside form submission.Tree Structures:
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @Gedmo\Tree(type="nested")
*/
private $parent;
TreeManager to reorder nodes:
$treeManager = $entityManager->getRepository('Gedmo\Tree\Entity\Repository\NestedTreeRepository');
$treeManager->moveNode($node, $newParent);
Soft Deletes:
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @Gedmo\SoftDeleteable(fieldName="deletedAt")
*/
private $deletedAt;
$queryBuilder->andWhere('e.deletedAt IS NULL');
Doctrine Lifecycle Events:
Use prePersist/preUpdate to trigger custom logic alongside extensions (e.g., validate slug uniqueness before save).
public function prePersist(LifecycleEventArgs $args) {
$entity = $args->getEntity();
if ($entity instanceof SluggableInterface) {
$entity->setSlug($entity->generateSlug());
}
}
Custom Fields:
Extend AbstractTimestampableEntity or AbstractTreeEntity for reusable base classes.
QueryBuilder Shortcuts:
Use TreeRepository methods like getChildren() or getSubtree() for nested queries.
Annotation vs. YAML/XML:
Slug Uniqueness:
unique=true in @Slug requires a unique constraint in the DB. Add it via migration:
$table->unique(['slug']);
Tree Performance:
type="nested") are slower for deep hierarchies. Use materialized_path for read-heavy apps.Soft Deletes + Queries:
$qb->andWhere('e.deletedAt IS NULL');
Timestampable Overrides:
setCreatedAt()/setUpdatedAt() methods can break auto-updates. Avoid overriding unless necessary.Enable SQL Logging:
$entityManager->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
Helps verify extension-generated queries (e.g., slug uniqueness checks).
Check Event Subscribers:
Extensions rely on Doctrine events. Verify they’re registered in config/packages/doctrine.yaml:
doctrine:
orm:
event_subscribers:
- Gedmo\Timestampable\TimestampableListener
- Gedmo\Slugable\SlugableListener
Custom Slug Strategies:
Override Gedmo\Slugable\SluggableListener to add logic (e.g., language-aware slugs).
Tree Types:
Implement Gedmo\Tree\Type\TreeTypeInterface for custom tree storage (e.g., closure table).
Timestampable Fields:
Extend Gedmo\Timestampable\TimestampableEntity to add createdBy/updatedBy fields:
/**
* @ORM\ManyToOne(targetEntity="User")
* @Gedmo\Timestampable(on="create")
*/
private $createdBy;
Event Hooks:
Listen for onFlush to modify entities before save:
$eventManager->addEventSubscriber(new class {
public function onFlush(OnFlushEventArgs $args) {
foreach ($args->getEntityChanges() as $entity) {
if ($entity instanceof SluggableInterface) {
$entity->updateSlug();
}
}
}
});
Repository Shortcuts:
Extend TreeRepository to add custom methods (e.g., getSiblings()).
How can I help you explore Laravel packages today?