updateExistingPivot method

Future<OrmMigrationRecord> updateExistingPivot(
  1. String relationName,
  2. dynamic id,
  3. Map<String, dynamic> pivotData
)
inherited

Updates pivot table attributes for an existing pivot record.

Unlike attach with pivotData, this only updates the pivot attributes for an already attached related model.

Example:

final post = await Post.query().find(1);
// Post has tag 5 attached with order=1
await post.updateExistingPivot('tags', 5, {'order': 2, 'featured': true});
// Pivot record updated

Implementation

Future<TModel> updateExistingPivot(
  String relationName,
  dynamic id,
  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(
      'updateExistingPivot() can only be used with manyToMany relations. '
      'Relation "$relationName" is ${relationDef.kind}',
    );
  }

  // 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');
  }

  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
  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 pivotValues = _mergePivotTimestamps(
    relationDef,
    pivotData,
    isInsert: false,
  );

  final pivotFieldDefs = _pivotModelFieldDefinitions(
    relationDef,
    resolver,
  );
  final pivotDef = _createPivotDefinition(
    pivotTable,
    def.schema,
    _pivotColumnDefinitions(
      pivotFieldDefs: pivotFieldDefs,
      foreignKey: pivotForeignKey,
      foreignKeyField: pk,
      relatedKey: pivotRelatedKey,
      relatedKeyField: relatedPk,
      pivotValues: pivotValues,
      timestampFields: _pivotTimestampFieldDefinitions(relationDef),
    ),
  );

  // Update the pivot record
  final plan = MutationPlan.update(
    definition: pivotDef,
    rows: [
      MutationRow(
        values: pivotValues,
        keys: {pivotForeignKey: pkValue, pivotRelatedKey: id},
      ),
    ],
  );

  await resolver.runMutation(plan);
  await _touchIfTouching(relationDef, def, resolver);

  // Reload the relation to sync cache
  await load(relationName);

  return _self();
}