attach method
Attaches related models in a manyToMany relationship.
Inserts new pivot table records to establish the many-to-many relationship. Optionally accepts pivot data for additional columns on the pivot table.
After attaching, the relation is reloaded to sync the cache.
Example:
final post = await Post.query().find(1);
await post.attach('tags', [1, 2, 3]);
// Pivot records created for tag IDs 1, 2, 3
// With pivot data:
await post.attach('tags', [4], pivotData: {'order': 1});
Implementation
Future<TModel> attach(
String relationName,
List<dynamic> ids, {
Map<String, dynamic>? pivotData,
}) 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.manyToMany) {
throw ArgumentError(
'attach() can only be used with manyToMany relations. '
'Relation "$relationName" is ${relationDef.kind}',
);
}
if (ids.isEmpty) {
return _self();
}
// Get this model's primary key value
final pk = def.primaryKeyField;
if (pk == null) {
throw StateError('Model ${def.modelName} must have a primary key');
}
final pkValue = _primaryKeyValue(def);
if (pkValue == null) {
throw StateError('Model ${def.modelName} primary key value is null');
}
// Build pivot table rows
final pivotTable = relationDef.through;
if (pivotTable == null) {
throw StateError(
'Relation "$relationName" is missing pivot table name (through)',
);
}
final pivotForeignKey = relationDef.pivotForeignKey!;
final pivotRelatedKey = relationDef.pivotRelatedKey!;
// Get the related model definition to determine column types
final relatedModelName = relationDef.targetModel;
final relatedDef = resolver.registry.expectByName(relatedModelName);
final relatedPk = relatedDef.primaryKeyField;
if (relatedPk == null) {
throw StateError(
'Related model $relatedModelName must have a primary key',
);
}
final rows = ids.map((id) {
final row = <String, dynamic>{
pivotForeignKey: pkValue,
pivotRelatedKey: id,
};
if (pivotData != null) {
row.addAll(pivotData);
}
return row;
}).toList();
// Build pivot table definition with proper column types
final pivotDef = _createPivotDefinition(pivotTable, def.schema, {
pivotForeignKey: pk,
pivotRelatedKey: relatedPk,
...?pivotData?.map((key, _) => MapEntry(key, null)),
});
final plan = MutationPlan.insert(definition: pivotDef, rows: rows);
await resolver.runMutation(plan);
// Reload the relation to sync cache
await load(relationName);
return _self();
}