toggle method
Toggles the attachment of related models in a manyToMany relationship.
For each ID:
- If currently attached → detach it
- If not attached → attach it
Example:
final post = await Post.query().find(1);
// Currently has tags: [1, 2]
await post.toggle('tags', [2, 3]);
// Now has tags: [1, 3] (2 was detached, 3 was attached)
Implementation
Future<TModel> toggle(
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(
'toggle() 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);
// Separate into IDs to attach and IDs to detach
final toDetach = ids.where((id) => existingIds.contains(id)).toList();
final toAttach = ids.where((id) => !existingIds.contains(id)).toList();
// Detach existing ones
if (toDetach.isNotEmpty) {
await detach(relationName, toDetach);
}
// Attach new ones
if (toAttach.isNotEmpty) {
await attach(relationName, toAttach, pivotData: pivotData);
}
// If nothing changed, still reload to sync cache
if (toDetach.isEmpty && toAttach.isEmpty) {
await load(relationName);
}
return _self();
}