compileWithRelations method
Compile a mutation query with potential relation operations.
This is used when CREATE or UPDATE queries include connect or
disconnect operations for many-to-many relations.
Example:
final query = JsonQueryBuilder()
.model('SlotOfAppointment')
.action(QueryAction.create)
.data({
'id': 'slot-123',
'startsAt': DateTime.now(),
'users': {
'connect': [{'id': 'user-1'}, {'id': 'user-2'}],
},
})
.build();
Implementation
CompiledMutation compileWithRelations(JsonQuery query) {
final args = query.args.arguments ?? {};
final data = args['data'] as Map<String, dynamic>? ?? {};
// Extract relation operations from data
final relationMutations = <SqlQuery>[];
final cleanData = <String, dynamic>{};
final effectiveSchema = schema ?? schemaRegistry;
for (final entry in data.entries) {
final value = entry.value;
if (value is Map<String, dynamic> &&
(value.containsKey('connect') || value.containsKey('disconnect'))) {
// This is a relation operation - look up the relation info
final relation =
effectiveSchema.getRelation(query.modelName, entry.key);
if (relation != null && relation.type == RelationType.manyToMany) {
// Get primary key field name from schema (fallback to 'id' for compatibility)
final parentModel = effectiveSchema.getModel(query.modelName);
final parentPkFieldName = parentModel?.primaryKeys.isNotEmpty == true
? parentModel!.primaryKeys.first.name
: 'id';
// Get the primary key value for the parent record
// For create, it's in the data; for update, it's in the where clause
String? parentId;
if (query.action == 'create') {
parentId = data[parentPkFieldName]?.toString();
} else if (query.action == 'update') {
final where = args['where'] as Map<String, dynamic>?;
parentId = where?[parentPkFieldName]?.toString();
}
if (parentId != null) {
// Compile connect operations
if (value.containsKey('connect')) {
final connectItems =
_normalizeConnectDisconnect(value['connect']);
relationMutations.addAll(_compileConnectOperations(
parentId: parentId,
relation: relation,
connectItems: connectItems,
effectiveSchema: effectiveSchema,
));
}
// Compile disconnect operations
if (value.containsKey('disconnect')) {
final disconnectItems =
_normalizeConnectDisconnect(value['disconnect']);
relationMutations.addAll(_compileDisconnectOperations(
parentId: parentId,
relation: relation,
disconnectItems: disconnectItems,
effectiveSchema: effectiveSchema,
));
}
}
} else if (relation != null) {
// Non-M2M relation with connect/disconnect - not supported
throw UnsupportedError(
'connect/disconnect operations are only supported for many-to-many '
'relations. Field "${entry.key}" is a ${relation.type.name} relation.',
);
}
// If relation is null, the field will be passed through and likely fail
// downstream with a more specific error about the unknown field
} else {
// Regular field - keep in clean data
cleanData[entry.key] = value;
}
}
// Create modified query with clean data (no relation operations)
final cleanArgs = Map<String, dynamic>.from(args);
cleanArgs['data'] = cleanData;
final cleanQuery = JsonQuery(
modelName: query.modelName,
action: query.action,
args: JsonQueryArgs(arguments: cleanArgs),
);
// Compile the main query
final mainQuery = compile(cleanQuery);
return CompiledMutation(
mainQuery: mainQuery,
relationMutations: relationMutations,
);
}