Installation:
composer require knplabs/doctrine-behaviors
Ensure your Laravel project uses Doctrine ORM (e.g., via doctrine/dbal and doctrine/orm).
First Use Case:
Add a behavior to an entity. For example, to make an entity Timestampable:
// src/Entity/Post.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\ORM\Timestampable\TimestampableTrait;
#[ORM\Entity]
class Post
{
use TimestampableTrait;
// Your fields and methods...
}
Run migrations to add createdAt and updatedAt columns.
Verify: Check if timestamps auto-populate when saving:
$post = new Post();
$entityManager->persist($post);
$entityManager->flush();
// createdAt and updatedAt should now have values.
Entity Behaviors:
Blameable: Track who created/updated an entity.
use Knp\DoctrineBehaviors\ORM\Blameable\BlameableTrait;
use Knp\DoctrineBehaviors\ORM\Blameable\BlameableInterface;
class Post implements BlameableInterface
{
use BlameableTrait;
}
Configure the user resolver in config/packages/knp_doctrine_behaviors.yaml:
knp_doctrine_behaviors:
blameable:
user_provider: 'security.user.provider'
SoftDeletable: Add deletedAt and soft-delete logic.
use Knp\DoctrineBehaviors\ORM\SoftDeletable\SoftDeletableTrait;
use Knp\DoctrineBehaviors\ORM\SoftDeletable\SoftDeletableInterface;
class Post implements SoftDeletableInterface
{
use SoftDeletableTrait;
}
Use SoftDeletableListener in your Doctrine config.
Repository Behaviors:
// src/Repository/CategoryRepository.php
use Knp\DoctrineBehaviors\ORM\Tree\TreeTrait;
use Knp\DoctrineBehaviors\ORM\Tree\TreeRepository;
class CategoryRepository extends TreeRepository
{
use TreeTrait;
}
Add left and right columns to your table and configure the tree in orm.xml or annotations.Translatable:
use Knp\DoctrineBehaviors\ORM\Translatable\TranslatableTrait;
use Knp\DoctrineBehaviors\ORM\Translatable\TranslatableInterface;
class Product implements TranslatableInterface
{
use TranslatableTrait;
#[ORM\Column(length: 255)]
private ?string $name = null;
#[ORM\Column(type: 'json')]
private array $translations = [];
}
Configure locales in config/packages/knp_doctrine_behaviors.yaml:
knp_doctrine_behaviors:
translatable:
locales: ['en', 'fr']
Sluggable:
use Knp\DoctrineBehaviors\ORM\Slugable\SlugableTrait;
use Knp\DoctrineBehaviors\ORM\Slugable\SlugableInterface;
class BlogPost implements SlugableInterface
{
use SlugableTrait;
#[ORM\Column(length: 255, unique: true)]
private ?string $slug = null;
}
Configure slug fields in orm.xml or annotations:
<field name="slug" type="string" length="255">
<slug generator="slugify" fields="title" />
</field>
prePersist) to customize behavior logic.
$entityManager->getEventManager()->addEventListener(
\Doctrine\ORM\Events::prePersist,
function (LifecycleEventArgs $args) {
$entity = $args->getObject();
if ($entity instanceof BlameableInterface) {
$entity->setCreatedBy($this->getCurrentUser());
}
}
);
getSlugFields() in SlugableTrait).findBySlug() (for Slugable) or getTree() (for Tree).Missing Columns:
createdAt, updatedAt, deletedAt) will cause runtime errors. Run migrations after adding behaviors.Tree, ensure left and right columns exist and are indexed.Locale Configuration:
Translatable requires locales to be defined in the config. Missing locales will throw exceptions when saving translations.User Provider:
Blameable needs a user provider (e.g., Symfony’s security user provider). Configure it in knp_doctrine_behaviors.yaml; otherwise, createdBy/updatedBy will be null.Slug Conflicts:
Slugable assumes uniqueness by default. Handle duplicates gracefully (e.g., append a suffix like -2):
knp_doctrine_behaviors:
sluggable:
unique_suffix: true
SoftDelete Queries:
SoftDeletable modifies queries to exclude deleted records by default. Use findDeleted() or createQueryBuilder()->where(...) to query deleted items explicitly.UUID Collisions:
Uuidable generates UUIDs. Ensure your database supports UUID types (e.g., uuid in PostgreSQL or char(36) in MySQL).Enable SQL Logging:
$entityManager->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
Helps verify if behaviors (e.g., SoftDeletable) are filtering queries correctly.
Check Event Listeners:
Use debug:event-dispatcher (Symfony) or inspect Doctrine events to ensure behaviors are triggered:
php bin/console debug:event-dispatcher
PHPStan: Install the PHPStan extension for type hints:
composer require --dev knplabs/doctrine-behaviors-phpstan
Configure in phpstan.neon:
includes:
- vendor/knplabs/doctrine-behaviors-phpstan/extension.neon
Custom Traits: Extend existing traits to add domain-specific logic. For example:
trait CustomTimestampableTrait extends TimestampableTrait
{
public function setCreatedAt(\DateTimeInterface $date): self
{
$this->createdAt = $date->setTime(0, 0); // Normalize to midnight
return $this;
}
}
Event Subscribers: Subscribe to Doctrine events to intercept behavior logic:
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
class CustomBlameableSubscriber implements EventSubscriber
{
public function getSubscribedEvents(): array
{
return ['prePersist', 'preUpdate'];
}
public function prePersist(LifecycleEventArgs $args): void
{
$entity = $args->getObject();
if ($entity instanceof BlameableInterface) {
$entity->setCreatedBy($this->getAdminUser());
}
}
}
Repository Methods:
Override repository methods to add custom queries. For example, for Tree:
public function findByParent(Category $parent): array
{
return $this->createQueryBuilder('c')
->where('c.lft BETWEEN :left AND :right')
->setParameter('left', $parent->getLeftValue())
->setParameter('right', $parent->getRightValue())
->getQuery()
->getResult();
}
Configuration Overrides:
Override default behavior configurations in config/packages/knp_doctrine_behaviors.yaml:
knp_doctrine_behaviors:
timestampable:
fields:
created_at: createdAt
updated_at: updated
How can I help you explore Laravel packages today?