syncWithoutDetaching method
Syncs a manyToMany relationship without detaching existing records.
Unlike sync, this method only attaches new IDs while keeping existing
pivot records intact. Useful when you want to add new relations without
removing existing ones.
Example:
final post = await Post.query().find(1);
// Currently has tags: [1, 2]
await post.syncWithoutDetaching('tags', [2, 3, 4]);
// Now has tags: [1, 2, 3, 4] (1 kept, 2 kept, 3 and 4 added)
Implementation
Future<TModel> syncWithoutDetaching(
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(
'syncWithoutDetaching() can only be used with manyToMany relations. '
'Relation "$relationName" is ${relationDef.kind}',
);
}
if (ids.isEmpty) {
return _self();
}
// Get existing attached IDs
final existingIds = await _getPivotRelatedIds(relationDef, def, resolver);
// Filter out IDs that already exist
final newIds = ids.where((id) => !existingIds.contains(id)).toList();
// Only attach the new ones
if (newIds.isNotEmpty) {
await attach(relationName, newIds, pivotData: pivotData);
} else {
// Reload relation to sync cache even if nothing changed
await load(relationName);
}
return _self();
}