(schema: SchemaDef, model: string, field: string)
| 342 | } |
| 343 | |
| 344 | function computeManyToManyRelation(schema: SchemaDef, model: string, field: string) { |
| 345 | const fieldDef = requireField(schema, model, field); |
| 346 | if (!fieldDef.array || !fieldDef.relation?.opposite) { |
| 347 | return undefined; |
| 348 | } |
| 349 | |
| 350 | // in case the m2m relation field is inherited from a delegate base, get the base model |
| 351 | const realModel = fieldDef.originModel ?? model; |
| 352 | |
| 353 | const oppositeFieldDef = requireField(schema, fieldDef.type, fieldDef.relation.opposite); |
| 354 | if (oppositeFieldDef.array) { |
| 355 | // Prisma's convention for many-to-many relation: |
| 356 | // - model are sorted alphabetically by name |
| 357 | // - join table is named _<model1>To<model2>, unless an explicit name is provided by `@relation` |
| 358 | // - foreign keys are named A and B (based on the order of the model) |
| 359 | const sortedModelNames = [realModel, fieldDef.type].sort(); |
| 360 | |
| 361 | let orderedFK: [string, string]; |
| 362 | if (realModel !== fieldDef.type) { |
| 363 | // not a self-relation, model name's sort order determines fk order |
| 364 | orderedFK = sortedModelNames[0] === realModel ? ['A', 'B'] : ['B', 'A']; |
| 365 | } else { |
| 366 | // for self-relations, since model names are identical, relation field name's |
| 367 | // sort order determines fk order |
| 368 | const sortedFieldNames = [field, oppositeFieldDef.name].sort(); |
| 369 | orderedFK = sortedFieldNames[0] === field ? ['A', 'B'] : ['B', 'A']; |
| 370 | } |
| 371 | |
| 372 | const modelIdFields = requireIdFields(schema, realModel); |
| 373 | invariant(modelIdFields.length === 1, 'Only single-field ID is supported for many-to-many relation'); |
| 374 | const otherIdFields = requireIdFields(schema, fieldDef.type); |
| 375 | invariant(otherIdFields.length === 1, 'Only single-field ID is supported for many-to-many relation'); |
| 376 | |
| 377 | return { |
| 378 | parentFkName: orderedFK[0], |
| 379 | parentPKName: modelIdFields[0]!, |
| 380 | otherModel: fieldDef.type, |
| 381 | otherField: fieldDef.relation.opposite, |
| 382 | otherFkName: orderedFK[1], |
| 383 | otherPKName: otherIdFields[0]!, |
| 384 | joinTable: fieldDef.relation.name |
| 385 | ? `_${fieldDef.relation.name}` |
| 386 | : `_${sortedModelNames[0]}To${sortedModelNames[1]}`, |
| 387 | }; |
| 388 | } else { |
| 389 | return undefined; |
| 390 | } |
| 391 | } |
| 392 | |
| 393 | /** |
| 394 | * Convert filter like `{ id1_id2: { id1: 1, id2: 1 } }` to `{ id1: 1, id2: 1 }` |
no test coverage detected