Model Methods
Familiar convenience methods for working with model instances.
Model Replication
replicate()
Clone a model without primary key or timestamps:
Future<void> replicateExample(DataSource dataSource) async {
final original = await dataSource.query<$User>().find(1);
// Create a replica
if (original != null) {
final duplicate = original.replicate();
duplicate.setAttribute('email', 'new@example.com');
await dataSource.repo<$User>().insert(duplicate);
}
}
What gets excluded:
- Primary key (automatically)
- Timestamps:
created_at,updated_at,createdAt,updatedAt - Any fields specified in the
exceptparameter
Excluding Specific Fields
Future<void> replicateExcludeExample(DataSource dataSource) async {
final post = await dataSource.query<$Post>().find(1);
if (post != null) {
final duplicate = post.replicate(except: ['slug', 'viewCount']);
duplicate.setAttribute('slug', 'new-unique-slug');
duplicate.setAttribute('viewCount', 0);
await dataSource.repo<$Post>().insert(duplicate);
}
}
Cannot exclude required non-nullable fields. Only exclude nullable fields or fields with defaults.
Use Cases
Future<void> replicateUseCases(DataSource dataSource) async {
// Duplicating Records
final product = await dataSource.query<$Product>().find(1);
if (product != null) {
final duplicate = product.replicate(except: ['sku', 'barcode']);
duplicate.setAttribute('sku', 'NEW-SKU-001');
duplicate.setAttribute('name', '${product.name} (Copy)');
await dataSource.repo<$Product>().insert(duplicate);
}
// Test Fixtures
final template = $User(id: 0, name: 'Test User', email: 'template@test.com');
final user1 = template.replicate();
user1.setAttribute('email', 'user1@test.com');
final user2 = template.replicate();
user2.setAttribute('email', 'user2@test.com');
await dataSource.repo<$User>().insertMany([user1, user2]);
}
Model Comparison
isSameAs()
Check if two models represent the same database record:
Future<void> comparisonExample(DataSource dataSource) async {
final user1 = await dataSource.query<$User>().find(1);
final user2 = await dataSource.query<$User>().find(1);
if (user1 != null && user2 != null && user1.isSameAs(user2)) {
print('These are the same user');
}
}
Comparison logic:
- Compares primary key values
- Compares table names
- Compares connection names (if available)
isDifferentFrom()
Inverse of isSameAs():
Future<void> differentExample(DataSource dataSource) async {
final user1 = await dataSource.query<$User>().find(1);
final user2 = await dataSource.query<$User>().find(2);
if (user1 != null && user2 != null && user1.isDifferentFrom(user2)) {
print('These are different users');
}
}
Use Cases
Deduplication:
Future<void> dedupeUseCaseExample(DataSource dataSource) async {
final users = await dataSource.query<$User>().get();
final unique = <$User>[];
for (final user in users) {
if (!unique.any((u) => u.isSameAs(user))) {
unique.add(user);
}
}
}
Validation:
void validationUseCaseExample(User from, User to) {
if (from.isSameAs(to)) {
throw ArgumentError('Cannot transfer to the same account');
}
// Process transfer...
}
Refreshing Models
fresh()
Get a new instance from the database without modifying the current instance:
Future<void> freshExample(DataSource dataSource) async {
final user = await dataSource.query<$User>().find(1);
if (user != null) {
user.setAttribute('name', 'Changed locally');
// Get fresh data from database
final fresh = await user.fresh();
print(user.getAttribute('name')); // 'Changed locally' (original unchanged)
print(fresh?.getAttribute('name')); // Original value from database
}
}
refresh()
Reload the current instance, discarding local changes:
Future<void> refreshExample(DataSource dataSource) async {
final user = await dataSource.query<$User>().find(1);
if (user != null) {
user.setAttribute('name', 'Changed locally');
// Discard changes and reload
await user.refresh();
print(user.getAttribute('name')); // Original value (changes lost)
}
}
When to Use Which
| Scenario | Use | Reason |
|---|---|---|
| Compare before/after | fresh() | Need both versions |
| Discard local changes | refresh() | Want to reset state |
| Check for external changes | fresh() | Compare instances |
| Reload after failed save | refresh() | Reset to DB state |
With Eager Loading
Future<void> refreshWithRelations(DataSource dataSource) async {
final user = await dataSource.query<$User>().find(1);
if (user != null) {
// Load relationships when refreshing
final fresh = await user.fresh(withRelations: ['posts', 'comments']);
await user.refresh(withRelations: ['posts', 'comments']);
}
}
With Soft Deletes
Future<void> softDeleteRefreshExample(User user) async {
// Include soft-deleted records
final fresh = await user.fresh(withTrashed: true);
await user.refresh(withTrashed: true);
}
Use Cases
Optimistic Locking:
Future<void> optimisticLockExample(DataSource dataSource) async {
final user = await dataSource.query<$User>().find(1);
if (user == null) return;
final originalUpdatedAt = user.getAttribute('updatedAt');
user.setAttribute('name', 'New Name');
// Check for concurrent modifications
final fresh = await user.fresh();
if (fresh?.getAttribute('updatedAt') != originalUpdatedAt) {
throw Exception('Record modified by another user');
}
await dataSource.repo<$User>().update(user);
}
Model State
exists
Check if a model is persisted in the database:
Future<void> existsExample(DataSource dataSource, String email) async {
final user = $User(id: 0, email: email);
print(user.exists); // false
await user.save();
print(user.exists); // true
}
wasRecentlyCreated
Check if a model was just inserted:
Future<void> wasRecentlyCreatedExample(User user) async {
await user.save();
if (user.wasRecentlyCreated) {
// await sendWelcomeEmail(user);
} else {
// await sendUpdateNotification(user);
}
}
save() Upsert Behavior
The save() method uses upsert semantics:
- New models (no primary key or not persisted): performs
INSERT - Existing models (primary key present and was hydrated): performs
UPSERT
Future<void> saveUpsertExample(DataSource dataSource) async {
// Insert a new model
final user = $User(id: 100, email: 'assigned@example.com');
await user.save(); // Inserts
// Update an existing model
user.setAttribute('email', 'updated@example.com');
await user.save(); // Updates
// If externally deleted, save() will re-insert
await user.save(); // Falls back to insert if 0 rows affected
}
Static Helpers
Once a default connection is configured, Ormed provides two convenient access patterns:
- Generated helper
- Generic Model helpers
Each model gets a generated helper class (plural) like Users / Posts that exposes typed entry points:
Future<void> staticHelpersExample() async {
// Assumes a default connection is configured (see DataSource docs).
final repo = Users.repo();
await repo.insert($User(id: 0, email: 'hi@example.com'));
final users = await Users.query().orderBy('id').get();
print(users.length);
}
You can also start from the generic Model helpers (useful when the model type is dynamic):
Model.query<$User>(connection: 'default')Model.repository<$User>(connection: 'default')
These resolve through the same default connection mechanism.
Notes:
- Default connection is set when you
await dataSource.init()(first one auto-defaults) or when you calldataSource.setAsDefault()(stored inConnectionManager). - For multi-database / multi-tenant flows, prefer explicit
DataSource/QueryContext, or pass a connection name to the helper (Users.query('analytics')). - If multiple connections register the same model type, always pass
connection:to avoid relying on implicit resolution. - You can override resolution by calling
Model.bindConnectionResolver(...). Clear it withModel.unbindConnectionResolver()(useful in tests or teardown).