saveRelation<TRelated extends Model<TRelated> > method
Saves a related model through a hasOne or hasMany relationship.
This sets the foreign key on the related model to point to this model, then persists the related model.
Example:
final author = await Author.query().find(1);
final post = Post(title: 'New Post', publishedAt: DateTime.now());
await author.saveRelation('posts', post);
// post.authorId is now author.id
Implementation
Future<TRelated> saveRelation<TRelated extends Model<TRelated>>(
String relationName,
TRelated related,
) async {
final def = expectDefinition();
final resolver = _resolveResolverFor(def);
final relationDef = def.relations.cast<RelationDefinition?>().firstWhere(
(r) => r?.name == relationName,
orElse: () => null,
);
if (relationDef == null) {
throw ArgumentError(
'Relation "$relationName" not found on ${def.modelName}',
);
}
if (relationDef.kind != RelationKind.hasOne &&
relationDef.kind != RelationKind.hasMany) {
throw ArgumentError(
'saveRelation() can only be used with hasOne or hasMany relations. '
'Relation "$relationName" is ${relationDef.kind}',
);
}
// Get this model's primary key value (the local key for hasOne/hasMany)
// Default to 'id' if localKey is not specified
final localKey = relationDef.localKey ?? 'id';
final localKeyValue = _getAttributeValue(localKey, def);
if (localKeyValue == null) {
throw StateError(
'Model ${def.modelName} local key "$localKey" value is null',
);
}
// Get related model definition
final foreignKey = relationDef.foreignKey;
final context = _requireQueryContext(resolver);
final relatedDef =
resolver.registry.expectByName(relationDef.targetModel)
as ModelDefinition<TRelated>;
final fkField = relatedDef.fields.firstWhere(
(f) => f.columnName == foreignKey || f.name == foreignKey,
);
// Convert related model to map and add/override foreign key
final relatedMap = relatedDef.toMap(
related,
registry: context.codecRegistry,
);
relatedMap[fkField.columnName] = localKeyValue;
// Use upsert to save (handles both insert and update cases)
final repo = context.repository<TRelated>();
final saved = await repo.upsert(relatedMap);
// Update relation cache
if (relationDef.kind == RelationKind.hasOne) {
_asRelations.setRelation(relationName, saved);
} else {
// For hasMany, add to the list
final existing = _asRelations.getRelation<List<TRelated>>(relationName);
if (existing != null) {
_asRelations.setRelation(relationName, [...existing, saved]);
} else {
_asRelations.setRelation(relationName, [saved]);
}
}
return saved;
}