@adonisjs/lucid
Version:
SQL ORM built on top of Active Record pattern
121 lines (120 loc) • 4 kB
JavaScript
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { BaseSubQueryBuilder } from '../base/sub_query_builder.js';
/**
* Extends the model query builder for executing queries in scope
* to the current relationship
*/
export class HasManyThroughSubQueryBuilder extends BaseSubQueryBuilder {
relation;
/**
* A boolean to track if query constraints for the relationship
* has been applied or not
*/
appliedConstraints = false;
/**
* Reference to the related table
*/
relatedTable;
/**
* Reference to the through table
*/
throughTable;
hasSelfRelation;
constructor(builder, client, relation) {
super(builder, client, relation, (userFn) => {
return ($builder) => {
const subQuery = new HasManyThroughSubQueryBuilder($builder, this.client, this.relation);
subQuery.isChildQuery = true;
userFn(subQuery);
subQuery.applyWhere();
};
});
this.relation = relation;
this.relatedTable = this.relation.relatedModel().table;
this.throughTable = this.relation.throughModel().table;
this.hasSelfRelation = this.relatedTable === this.relation.model.table;
}
/**
* Prefixes the through table name to a column
*/
prefixThroughTable(column) {
return column.includes('.') ? column : `${this.throughTable}.${column}`;
}
/**
* Prefixes the related table name to a column
*/
prefixRelatedTable(column) {
if (column.includes('.')) {
return column;
}
if (this.hasSelfRelation) {
return `${this.selfJoinAlias}.${column}`;
}
return `${this.relatedTable}.${column}`;
}
/**
* Transforms the selected column names by prefixing the
* table name
*/
transformRelatedTableColumns(columns) {
return columns.map((column) => {
if (typeof column === 'string') {
return this.prefixRelatedTable(this.resolveKey(column));
}
return this.transformValue(column);
});
}
/**
* The keys for constructing the join query
*/
getRelationKeys() {
return [this.relation.throughForeignKeyColumnName];
}
/**
* Select keys from the related table
*/
select(...args) {
let columns = args;
if (Array.isArray(args[0])) {
columns = args[0];
}
this.knexQuery.select(this.transformRelatedTableColumns(columns));
return this;
}
/**
* Applies constraint to limit rows to the current relationship
* only.
*/
applyConstraints() {
if (this.appliedConstraints) {
return;
}
this.appliedConstraints = true;
/**
* In case of self joins, we must alias the table selection
*/
if (this.relation.relatedModel() === this.relation.model) {
this.knexQuery.from(`${this.relatedTable} as ${this.selfJoinAlias}`);
}
this.innerJoin(this.throughTable, this.prefixThroughTable(this.relation.throughLocalKeyColumnName), this.prefixRelatedTable(this.relation.throughForeignKeyColumnName));
this.where(`${this.relation.model.table}.${this.relation.localKeyColumnName}`, this.client.ref(this.prefixThroughTable(this.relation.foreignKeyColumnName)));
}
/**
* Clones the current query
*/
clone() {
const clonedQuery = new HasManyThroughSubQueryBuilder(this.knexQuery.clone(), this.client, this.relation);
this.applyQueryFlags(clonedQuery);
clonedQuery.appliedConstraints = this.appliedConstraints;
clonedQuery.debug(this.debugQueries);
clonedQuery.reporterData(this.customReporterData);
return clonedQuery;
}
}