toggle method

Future<OrmMigrationRecord> toggle(
  1. String relationName,
  2. List ids, {
  3. Map<String, dynamic>? pivotData,
})
inherited

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