Skip to main content

Model Events

Ormed emits lifecycle events for every model operation: saving, saved, creating, created, updating, updated, deleting, deleted, trashed, forceDeleted, restoring, restored, retrieved, and replicating.

  • Cancellable: saving, creating, updating, deleting, restoring, replicating
  • Soft delete aware: trashed (soft delete), forceDeleted (hard delete)
  • Global listeners: subscribe on the shared EventBus.instance
Snippet context

These snippets focus on event wiring and handlers. They assume you already have a DataSource or QueryContext unless the snippet explicitly shows setup.

Handler requirements

Handlers are static methods that take the event type you annotate with @OrmEvent.

Rules:

  • Signature: static void onXyz(ModelXyzEvent event)
  • Public/static only; no return value
  • Use event.cancel() on cancellable events to abort the operation
(table: 'audited_users')
class AuditedUser extends Model<AuditedUser> {
const AuditedUser({required this.id, required this.email});

(isPrimaryKey: true, autoIncrement: true)
final int id;

final String email;

(ModelSavingEvent)
static void onSaving(ModelSavingEvent event) =>
modelEventLog.add('saving ${event.attributes['email']}');

(ModelCreatedEvent)
static void onCreated(ModelCreatedEvent event) =>
modelEventLog.add('created ${event.model.id}');

(ModelForceDeletedEvent)
static void onForceDeleted(ModelForceDeletedEvent event) =>
modelEventLog.add('forceDeleted ${event.model.id}');
}

Listen globally

Attach listeners once when your app boots:

  • Register listeners once during app startup.
  • Global listeners receive events from every DataSource.

Guard operations (cancel)

Cancel dangerous deletes or updates in a listener by calling event.cancel().

Cancellation requires a model instance (so your handler can inspect fields). Use a query-based delete (delete() / deleteReturning()) or pass a tracked model to a repository method.

void enforceActiveUserDeletes(EventBus bus) {
bus.on<ModelDeletingEvent>((event) {
if (!_forUser(event.modelType)) return;
final user = event.model as EventUser;
if (!user.active) {
event.cancel(); // block delete/forceDelete
modelEventLog.add('blocked delete for inactive user ${user.id}');
}
});
}

Full lifecycle walkthrough

This wires listeners and creates a demo DataSource.

  final bus = EventBus.instance;
registerModelEventListeners(bus);
enforceActiveUserDeletes(bus);

final dataSource = DataSource(
DataSourceOptions(
name: 'events-demo',
driver: SqliteDriverAdapter.inMemory(),
entities: generatedOrmModelDefinitions,
),
);
await dataSource.init();

Event ordering

For a create + update + soft delete + restore flow, events fire in this order:

  1. savingcreatingcreatedsaved
  2. savingupdatingupdatedsaved
  3. deletingdeletedtrashed (soft delete)
  4. restoringrestored

Hard deletes emit deletingdeletedforceDeleted.

Replication

ModelReplicatingEvent fires before Repository.replicate returns the copy. Cancel it to block replication or attach metadata to the new instance.