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
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/
staticonly; 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:
- Notes
- Code
- Register listeners once during app startup.
- Global listeners receive events from every
DataSource.
- saving / created
- updated / deleted
- retrieved
bus.on<ModelSavingEvent>((event) {
if (!_forUser(event.modelType)) return;
modelEventLog.add('saving ${event.attributes['email']}');
});
bus.on<ModelCreatedEvent>((event) {
if (!_forUser(event.modelType)) return;
modelEventLog.add('created ${event.model.id}');
});
bus.on<ModelUpdatedEvent>((event) {
if (!_forUser(event.modelType)) return;
modelEventLog.add('updated ${event.model.id}');
});
bus.on<ModelDeletedEvent>((event) {
if (!_forUser(event.modelType)) return;
final mode = event.forceDelete ? 'forceDelete' : 'trash';
modelEventLog.add('$mode ${event.model.id}');
});
bus.on<ModelRetrievedEvent>((event) {
if (!_forUser(event.modelType)) return;
modelEventLog.add('retrieved ${event.model.id}');
});
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.
- Handler
- Usage
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}');
}
});
}
Future<void> guardedDeleteExample(DataSource dataSource) async {
final inactive = await dataSource.repo<$EventUser>().insert(
$EventUser(id: 0, email: 'inactive@example.com', active: false),
);
final affected = await dataSource
.query<$EventUser>()
.whereEquals('id', inactive.id)
.delete();
// => 0 (delete is cancelled)
print(affected);
}
Full lifecycle walkthrough
- Setup
- Use / Verify
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();
This performs insert/update/delete operations so you can observe event ordering.
final alice = await dataSource.repo<$EventUser>().insert(
$EventUser(
id: 0,
name: 'Alice',
email: 'alice@example.com',
active: true,
),
);
await dataSource.query<$EventUser>().whereEquals('id', alice.id).update({
'name': 'Alice Updated',
});
await dataSource
.query<$EventUser>()
.whereEquals('id', alice.id)
.deleteReturning();
await dataSource
.query<$EventUser>()
.withTrashed()
.whereEquals('id', alice.id)
.restore();
print(modelEventLog);
Event ordering
For a create + update + soft delete + restore flow, events fire in this order:
saving→creating→created→savedsaving→updating→updated→saveddeleting→deleted→trashed(soft delete)restoring→restored
Hard deletes emit deleting → deleted → forceDeleted.
Replication
ModelReplicatingEvent fires before Repository.replicate returns the copy. Cancel it to block replication or attach metadata to the new instance.