📡 Events
Hector ORM dispatches PSR-14 events during the entity lifecycle. This allows you to hook into save and delete operations — for example, to add audit logging, enforce validation rules, or prevent an operation from executing.
Setup
Pass a PSR-14 EventDispatcherInterface when bootstrapping the ORM:
use Hector\Orm\OrmFactory;
use Psr\EventDispatcher\EventDispatcherInterface;
/** @var EventDispatcherInterface $eventDispatcher */
$orm = OrmFactory::orm(
options: ['schemas' => ['my_database']],
connection: $connection,
eventDispatcher: $eventDispatcher,
);
If no event dispatcher is provided, events are silently ignored (zero overhead).
Event reference
| Event | Dispatched | Stoppable | Extra data |
|---|---|---|---|
EntityBeforeSaveEvent |
Before an insert or update | ✔ | isUpdate() |
EntityAfterSaveEvent |
After a successful insert or update | — | isUpdate() |
EntityBeforeDeleteEvent |
Before a delete | ✔ | — |
EntityAfterDeleteEvent |
After a successful delete | — | — |
All events extend EntityEvent and carry:
| Method | Return type | Description |
|---|---|---|
getEntity() |
Entity |
The entity being saved or deleted |
getTime() |
DateTimeImmutable |
Timestamp of the event |
Save events (EntityBeforeSaveEvent and EntityAfterSaveEvent) also carry:
| Method | Return type | Description |
|---|---|---|
isUpdate() |
bool |
true if updating an existing entity, false if inserting a new one |
Stoppable events
EntityBeforeSaveEvent and EntityBeforeDeleteEvent implement StoppableEventInterface. If a listener calls
stopPropagation(), the operation is cancelled — the entity is not saved or deleted.
use Hector\Orm\Event\EntityBeforeSaveEvent;
$listener = function (EntityBeforeSaveEvent $event): void {
$entity = $event->getEntity();
// Prevent saving if validation fails
if (empty($entity->email)) {
$event->stopPropagation();
}
};
Warning: When propagation is stopped, the entity remains in its current state in the storage. No database operation is performed, and no “after” event is dispatched.
Examples
Audit logging
use Hector\Orm\Event\EntityAfterSaveEvent;
use Hector\Orm\Event\EntityAfterDeleteEvent;
$onSave = function (EntityAfterSaveEvent $event): void {
$action = $event->isUpdate() ? 'updated' : 'created';
$class = get_class($event->getEntity());
$logger->info("Entity {$class} was {$action}");
};
$onDelete = function (EntityAfterDeleteEvent $event): void {
$class = get_class($event->getEntity());
$logger->info("Entity {$class} was deleted");
};
Automatically setting timestamps
use Hector\Orm\Event\EntityBeforeSaveEvent;
$listener = function (EntityBeforeSaveEvent $event): void {
$entity = $event->getEntity();
if (!$event->isUpdate() && property_exists($entity, 'createdAt')) {
$entity->createdAt = new DateTimeImmutable();
}
if (property_exists($entity, 'updatedAt')) {
$entity->updatedAt = new DateTimeImmutable();
}
};
Preventing deletion
use Hector\Orm\Event\EntityBeforeDeleteEvent;
$listener = function (EntityBeforeDeleteEvent $event): void {
$entity = $event->getEntity();
// Soft-delete: mark as deleted instead of actually deleting
if (property_exists($entity, 'deletedAt')) {
$entity->deletedAt = new DateTimeImmutable();
$entity->save();
$event->stopPropagation();
}
};
Class hierarchy
AbstractEvent
├── EntityEvent
│ ├── EntitySaveEvent
│ │ ├── EntityBeforeSaveEvent (stoppable)
│ │ └── EntityAfterSaveEvent
│ └── EntityDeleteEvent
│ ├── EntityBeforeDeleteEvent (stoppable)
│ └── EntityAfterDeleteEvent
All classes live in the Hector\Orm\Event namespace.