Skip to main content

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 except parameter

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);
}
}
warning

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:

  1. Compares primary key values
  2. Compares table names
  3. 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

ScenarioUseReason
Compare before/afterfresh()Need both versions
Discard local changesrefresh()Want to reset state
Check for external changesfresh()Compare instances
Reload after failed saverefresh()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:

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);
}

Notes:

  • Default connection is set when you await dataSource.init() (first one auto-defaults) or when you call dataSource.setAsDefault() (stored in ConnectionManager).
  • 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 with Model.unbindConnectionResolver() (useful in tests or teardown).