Installation Add the package via Composer:
composer require beberlei/doctrineextensions
Register the extensions in your config/doctrine.php (or equivalent) by extending the Doctrine\ORM\Configuration:
use DoctrineExtensions\ORM\Config\DoctrineExtensions;
use Doctrine\ORM\Configuration;
$config = Configuration::create();
$config->registerExtensions(new DoctrineExtensions());
First Use Case: Soft Deletes Enable soft deletes for an entity by:
SoftDeleteable trait to your model:
use DoctrineExtensions\ORM\SoftDeleteable\Entity;
use DoctrineExtensions\ORM\SoftDeleteable\SoftDeleteableEntity;
class User extends Entity implements SoftDeleteableEntity
{
use SoftDeleteable;
}
- Adding the `deletedAt` column to your migration:
```php
$table->timestamp('deleted_at')->nullable()->after('updated_at');
$softDeletedUsers = $entityManager->getRepository(User::class)
->findBy([], ['deletedAt' => 'DESC'], null, ['SoftDeleteable']);
Soft Deletes
$user = $entityManager->find(User::class, 1);
$user->delete(); // Soft delete (sets `deletedAt` timestamp)
$user->forceDelete(); // Hard delete (removes record entirely)
$queryBuilder = $entityManager->getRepository(User::class)->createQueryBuilder('u');
$queryBuilder->where('u.deletedAt IS NULL'); // Only active records
// OR
$queryBuilder->andWhere('u.deletedAt IS NOT NULL'); // Only soft-deleted records
Sluggable Entities
use DoctrineExtensions\ORM\Sluggable\SluggableEntity;
use DoctrineExtensions\ORM\Sluggable\Sluggable;
class Post implements SluggableEntity
{
use Sluggable;
protected $slugFieldName = 'slug';
protected $sluggableFields = ['title'];
}
$post = new Post();
$post->setTitle('My Awesome Post');
$post->generateSlug(); // Generates slug from `title`
Tree Entities (Nested Sets)
use DoctrineExtensions\ORM\Tree\TreeEntity;
use DoctrineExtensions\ORM\Tree\Tree;
class Category implements TreeEntity
{
use Tree;
}
$parent = $entityManager->find(Category::class, 1);
$child = new Category();
$child->setParent($parent);
$entityManager->persist($child);
$entityManager->flush();
Sortable Entities
use DoctrineExtensions\ORM\Sortable\SortableEntity;
use DoctrineExtensions\ORM\Sortable\Sortable;
class Product implements SortableEntity
{
use Sortable;
}
$product = $entityManager->find(Product::class, 1);
$product->moveUp(); // or moveDown(), moveToTop(), moveToBottom()
Laravel-Specific Setup
Doctrine\ORM\EntityManager in your service provider to register extensions:
$entityManager->getConfiguration()->registerExtensions(new DoctrineExtensions());
Customizing Behavior
protected function generateSlugFromFields()
{
return str_slug($this->title . '-' . $this->id);
}
protected $dateTimeType = 'datetime'; // or 'timestamp'
protected $deletedAtColumn = 'deleted_at';
Querying with Extensions
$query = $entityManager->createQuery(
'SELECT u FROM App\Entity\User u WHERE u.deletedAt IS NULL'
);
Event Listeners
$entityManager->getEventManager()->addEventListener(
\Doctrine\ORM\Events::prePersist,
function (LifecycleEventArgs $args) {
$entity = $args->getEntity();
if ($entity instanceof SluggableEntity) {
$entity->generateSlug();
}
}
);
Soft Deletes and Migrations
deletedAt to migrations can cause errors.$table->timestamp('deleted_at')->nullable()->after('updated_at');
Tree Entities and Performance
MaterializedPath or NestedSet strategies wisely. For large trees, consider MaterializedPath.Slug Conflicts
generateSlug() to append a counter if needed.Sortable Entities and Concurrency
$entityManager->beginTransaction();
try {
$product->moveUp();
$entityManager->flush();
$entityManager->commit();
} catch (\Exception $e) {
$entityManager->rollBack();
throw $e;
}
Doctrine Version Compatibility
composer.json for supported versions and update Doctrine accordingly.Enable SQL Logging Debug queries to understand how extensions modify them:
$entityManager->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
Check Event Subscribers Extensions often rely on Doctrine events. Verify they are registered:
$eventManager = $entityManager->getEventManager();
$listeners = $eventManager->getListeners();
Use DQL for Complex Queries Extensions may not work as expected with QueryBuilder. Use DQL directly:
$query = $entityManager->createQuery('SELECT u FROM App\Entity\User u WHERE u.deletedAt IS NULL');
Clear Cache After Configuration Changes Extensions may require cache clearing:
php artisan doctrine:cache:clear-metadata
php artisan doctrine:cache:clear-query
Custom Soft Delete Logic
Extend the SoftDeleteable trait to add custom logic:
protected function getDeletedAtValue()
{
return new \DateTime('now', new \DateTimeZone('UTC'));
}
Add New Sluggable Fields Dynamically
Override getSluggableFields():
public function getSluggableFields()
{
return ['title', 'subtitle'];
}
Custom Tree Strategies
Implement your own tree strategy by extending DoctrineExtensions\ORM\Tree\TreeStrategy.
Hook into Lifecycle Events Use Doctrine lifecycle callbacks for custom logic:
use Doctrine\ORM\Mapping as ORM;
class User
{
/**
* @ORM\PrePersist
*/
public function prePersist()
{
$this->generateSlug();
}
}
Integrate with Laravel Events Dispatch Laravel events when extensions trigger actions:
use Illuminate\Support\Facades\Event;
$entityManager->getEventManager()->addEventListener(
\Doctrine\ORM\Events::preRemove,
function (LifecycleEventArgs $args) {
$entity = $args->getEntity();
if ($entity instanceof SoftDeleteableEntity) {
Event::dispatch('user.softDeleting', $entity);
}
}
);
How can I help you explore Laravel packages today?