detach method

Future<bool> detach(
  1. Model sibling, {
  2. required ModelRelationshipType relationship,
  3. PivotTable<Model, Model>? table,
  4. DatabaseDisk disk = Model.defaultDisk,
})

Detaches sibling from this model using the given relationship type.

Behavior depends on the relationship:

  • hasOne / hasMany: clears this model's foreign key from sibling
  • belongsToOne: clears the sibling foreign key from this model
  • belongsToMany: removes the pivot-table record

For belongsToMany, table is required.

Returns true when the relationship is successfully detached; otherwise returns false.

Example:

await user.detach(
  profile,
  relationship: ModelRelationshipType.hasOne,
);

await user.detach(
  role,
  relationship: ModelRelationshipType.belongsToMany,
  table: userRolesPivot,
  disk: DatabaseDisk.sqlite,
);

Implementation

Future<bool> detach(Model sibling, {required ModelRelationshipType relationship, PivotTable? table, DatabaseDisk disk = Model.defaultDisk,}) async {

  try {

    final childPrefix = getInstanceTableSingularName();
    final siblingPrefix = sibling.getInstanceTableSingularName();

    switch (relationship) {

      case .hasOne:
      case .hasMany:

        switch (disk) {
          case .sqlite:
          case .pgsql:
            final childField = "${childPrefix}_id";

            return await sibling.update(withJson: {childField: null});

          case .file:
          case .s3:
            final childField = "${childPrefix}_uuid";
            return await sibling.update(withJson: {childField: null});
        }


      case .belongsToOne:

        switch (disk) {
          case .sqlite:
          case .pgsql:
            final siblingField = "${siblingPrefix}_id";
            return  update(withJson: {siblingField: null});

          case .file:
          case .s3:
            final siblingField = "${siblingPrefix}_uuid";
            return await update(withJson: {siblingField: null});
        }

      case .belongsToMany:

        if(table == null) {
          return false;
        }

        switch(disk) {
          case .sqlite:

            final sqliteDB = App().container.make<SQLiteDatabase>();

            final childPrefix = getInstanceTableSingularName();
            final childField = "${childPrefix}_id";

            final siblingPrefix = sibling.getInstanceTableSingularName();
            final siblingField = "${siblingPrefix}_id";

            await sqliteDB.delete(
              table.name,
              where: '$childField = ? AND $siblingField = ?',
              whereArgs: [id, sibling.id],
            );

            return true;

        // todo : pivot tables currently implemented for sqlite.
          case DatabaseDisk.file:
            throw UnimplementedError();
          case DatabaseDisk.pgsql:
            throw UnimplementedError();
          case DatabaseDisk.s3:
            throw UnimplementedError();
        }
    }

  } catch (e, stack) {
    App().archeryLogger.error("Error detaching sibling", {
      "origin": "ext ModelRelationshipOps detach()",
      "error": e.toString(),
      "stack": stack.toString()
    });

    return false;
  }

}