I have the following relation:
interface IWorkflow extends IBaseEntity {
forms?: Form[]
}
export class Workflow extends BaseEntity implements IWorkflow {
static get tableName() {
return 'workflows';
}
forms?: Form[];
static relationMappings: RelationMappings = {
forms: {
relation: Model.ManyToManyRelation,
modelClass: Form,
join: {
from: 'workflows.id',
through: {
from: 'workflowsForms.workflowId',
to: 'workflowsForms.formId',
extra: ['order']
},
to: 'forms.id',
}
}
}
}
I'm trying to link form to workflow using updateGraph
public async addFormToWorkflow(
workflowId: number,
formId: number,
order: number,
): Promise<Workflow> {
try {
const workflow = await Workflow
.query()
.findById(workflowId)
.eager('forms');
const form = await Form.query().findById(formId);
form.order = order; // This line will cause TS error, because order is not part of form model
workflow.forms.push(form);
return Workflow.query().upsertGraph(workflow, {
relate: true
});
} catch (e) {
throw e;
}
}
So, the problem is in line form.order = order;. I've checked it with //@ts-ignore and it works perfectly. I can create a workaround if I define model for many-to-many relation table, and add record there directly, but from my perspective using just upsertGraph is much more elegant. Could you please suggest if there is some way to pass extra fields nicely? Thank you
Just add that field for your model.
I'm not sure that it's correct to add field to the model that's actually is not related to the model itself, but it seems there is no better way to do it. Thank you
You don't need to use upsertGraph here though. This would be much simpler:
public async addFormToWorkflow(
workflowId: number,
formId: number,
order: number,
): Promise<Workflow> {
try {
const workflow = await Workflow
.query()
.findById(workflowId);
await workflow.$relatedQuery('forms').relate({
id: formId,
order
})
} catch (e) {
throw e;
}
}
That's two queries instead of something like 8 with eager and upsertGraph. You can even do that using one single query like this:
public async addFormToWorkflow(
workflowId: number,
formId: number,
order: number,
): Promise<Workflow> {
try {
await Workflow
.fromJson({ id: workflowId })
.$relatedQuery('forms')
.relate({
id: formId,
order
})
} catch (e) {
throw e;
}
}
I'm not sure that it's correct to add field to the model that's actually is not related to the model itself, but it seems there is no better way to do it
But if you use an extra, the property IS part of that model. It's not part of the model's table, but the model will always be returned with the extra field when you query it through that relation (not other relations though). You anyway need to access the field, so there's no way around adding it to the model.
@koskimas That's awesome, thanks a lot! I will try this out, currently have some TS issues with sending object in the relate, but anyway it's a great optimization. And thank you for explanation regarding table vs model
Just added typing for the object, and it worked perfectly. Thanks again!
const formRelation: Partial<Form> = {
id: formId,
order
};
return workflow.$relatedQuery('forms').relate(formRelation);
I'm not sure that it's correct to add field to the model that's actually is not related to the model itself, but it seems there is no better way to do it
But if you use an
extra, the property IS part of that model. It's not part of the model's table, but the model will always be returned with the extra field when you query it through that relation (not other relations though). You anyway need to access the field, so there's no way around adding it to the model.
What can we do about the possibility of the model to which the extra is appended already having a column by that name? If both the model table and the join table have a created_at column, for instance, what should we do to disambiguate them?
Is an alias here the only option? Is there a way of doing this with the upsertGraph operation?
I'm also having issued with shared column names like created_at. Did you find a solution for this @GreatSchool2 ?
Most helpful comment
You don't need to use
upsertGraphhere though. This would be much simpler:That's two queries instead of something like 8 with
eagerandupsertGraph. You can even do that using one single query like this: