nitra/doctrine-behaviors
PHP 5.4+ trait-based behaviors for Doctrine2 entities and repositories: tree, translatable, timestampable, soft deletable, blameable, loggable, geocodable, filterable, and sluggable. Includes optional Doctrine event listeners for behavior support.
Installation:
composer require knplabs/doctrine-behaviors
For Symfony, import the YAML config:
# config/packages/doctrine_behaviors.yaml
imports:
- { resource: "@KnpDoctrineBehaviors/config/orm-services.yml" }
First Use Case:
Add a behavior trait to an entity (e.g., Timestampable):
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
class Post
{
use ORMBehaviors\Timestampable\Timestampable;
}
Ensure the entity has use Doctrine\ORM\Mapping as ORM; at the top.
Verify Listeners:
For behaviors requiring listeners (e.g., Translatable, Blameable), confirm they’re registered via Symfony’s DI or manually:
$em->getEventManager()->addEventSubscriber(new \Knp\DoctrineBehaviors\ORM\Translatable\TranslatableListener);
Combining Behaviors:
Chain traits in a single entity (e.g., Tree + Translatable):
class Category
{
use ORMBehaviors\Tree\Node,
ORMBehaviors\Translatable\Translatable;
}
getTree() in the repository to fetch hierarchical data.CategoryTranslation entity and use translate('locale')->setField().Repository-Level Behaviors:
Extend repositories with traits like Filterable or Tree:
class CategoryRepository extends EntityRepository
{
use ORMBehaviors\Tree\Tree;
use ORMBehaviors\Filterable\FilterableRepository;
public function getLikeFilterColumns() { return ['name']; }
}
Dynamic Slugs:
Override getSluggableFields() for custom slug generation:
class BlogPost
{
use ORMBehaviors\Sluggable\Sluggable;
public function getSluggableFields() { return ['title', 'author']; }
}
Geocoding:
Integrate with geocoder/geocoder for address-to-coordinates conversion:
$geocoder = new \Geocoder\Geocoder();
$listener->setGeolocationCallable(function($entity) use ($geocoder) {
$location = $geocoder->geocode($entity->getAddress());
$entity->setLocation(new Point($location->getLatitude(), $location->getLongitude()));
});
Translatable with Symfony\Bridge\Doctrine\Form\Type\EntityType for locale-aware fields.Blameable to track user actions in audit logs.isDeleted() logic to exclude records from queries:
$qb->andWhere('entity.deletedAt IS NULL');
Listener Registration:
TranslatableListener) causes silent failures. Verify via:
$em->getEventManager()->getListeners();
orm-services.yml or register manually.Annotation Driver:
Translatable require @ORM\Entity annotations. Missing use Doctrine\ORM\Mapping as ORM; breaks functionality.Tree Behavior:
id set before setChildOf():
$child->setId(2); // Required!
$child->setChildOf($parent);
postPersist lifecycle callbacks to auto-assign IDs.Slug Uniqueness:
Sluggable does not enforce uniqueness. Handle collisions manually:
$slug = $this->generateSlug();
if ($this->slugExists($slug)) {
$slug = $slug . '-' . uniqid();
}
Geocodable Dependencies:
cube/earthdistance extensions are required for spatial queries. Test with:
CREATE EXTENSION cube;
$listeners = $em->getEventManager()->getListeners();
print_r(array_keys($listeners));
__call() proxies translations correctly:
public function __call($method, $args) {
if (strpos($method, 'get') === 0) {
return $this->proxyCurrentLocaleTranslation($method, $args);
}
throw new \BadMethodCallException(...);
}
Blameable user provider):
$listener->setUserCallable(function() {
return $this->getCurrentUser(); // Your logic
});
createFilterQueryBuilder() for complex joins:
protected function createFilterQueryBuilder() {
return $this->createQueryBuilder('e')
->leftJoin('e.tags', 't')
->where('t.name = :tag');
}
updatedAt:
public function setUpdatedAt($date = null) { /* No-op */ }
How can I help you explore Laravel packages today?