You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
4.7 KiB
146 lines
4.7 KiB
import {Model} from './Model'
|
|
import {AbstractBuilder} from '../builder/AbstractBuilder'
|
|
import {AbstractResultIterable} from '../builder/result/AbstractResultIterable'
|
|
import {Instantiable, StaticClass} from '../../di'
|
|
import {ModelResultIterable} from './ModelResultIterable'
|
|
import {Collection} from '../../util'
|
|
import {ConstraintOperator, ModelKey, ModelKeys} from '../types'
|
|
import {EscapeValue} from '../dialect/SQLDialect'
|
|
import {Scope, ScopeClosure} from './scope/Scope'
|
|
|
|
/**
|
|
* Implementation of the abstract builder whose results yield instances of a given Model, `T`.
|
|
*/
|
|
export class ModelBuilder<T extends Model<T>> extends AbstractBuilder<T> {
|
|
protected eagerLoadRelations: (keyof T)[] = []
|
|
|
|
protected appliedScopes: Collection<{ accessor: string | Instantiable<Scope>, scope: ScopeClosure }> = new Collection<{accessor: string | Instantiable<Scope>; scope: ScopeClosure}>()
|
|
|
|
constructor(
|
|
/** The model class that is created for results of this query. */
|
|
protected readonly ModelClass: StaticClass<T, typeof Model> & Instantiable<T>,
|
|
) {
|
|
super()
|
|
}
|
|
|
|
public withScopes(scopes: Collection<{ accessor: string | Instantiable<Scope>, scope: ScopeClosure }>): this {
|
|
this.appliedScopes = scopes.clone()
|
|
return this
|
|
}
|
|
|
|
public getNewInstance(): AbstractBuilder<T> {
|
|
return this.app().make<ModelBuilder<T>>(ModelBuilder, this.ModelClass)
|
|
}
|
|
|
|
public getResultIterable(): AbstractResultIterable<T> {
|
|
return this.app().make<ModelResultIterable<T>>(ModelResultIterable, this.finalize(), this.registeredConnection, this.ModelClass)
|
|
}
|
|
|
|
/**
|
|
* Get a copy of this builder with all of its values finalized.
|
|
* @override to apply scopes
|
|
*/
|
|
public finalize(): AbstractBuilder<T> {
|
|
const inst = super.finalize()
|
|
this.appliedScopes.each(rec => rec.scope(inst))
|
|
return inst
|
|
}
|
|
|
|
/**
|
|
* Apply a WHERE...IN... constraint on the primary key of the model.
|
|
* @param keys
|
|
*/
|
|
public whereKey(keys: ModelKeys): this {
|
|
return this.whereIn(
|
|
this.ModelClass.qualifyKey(),
|
|
this.normalizeModelKeys(keys),
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Apply a where constraint on the column corresponding the the specified
|
|
* property on the model.
|
|
* @param propertyName
|
|
* @param operator
|
|
* @param operand
|
|
*/
|
|
public whereProperty(propertyName: string, operator: ConstraintOperator, operand?: EscapeValue): this {
|
|
return this.where(
|
|
this.ModelClass.propertyToColumn(propertyName),
|
|
operator,
|
|
operand,
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Mark a relation to be eager-loaded.
|
|
* @param relationName
|
|
*/
|
|
public with(relationName: keyof T): this {
|
|
if ( !this.eagerLoadRelations.includes(relationName) ) {
|
|
// Try to load the Relation, so we fail if the name is invalid
|
|
this.make<T>(this.ModelClass).getRelation(relationName)
|
|
this.eagerLoadRelations.push(relationName)
|
|
}
|
|
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Prevent a relation from being eager-loaded.
|
|
* @param relationName
|
|
*/
|
|
public without(relationName: keyof T): this {
|
|
this.eagerLoadRelations = this.eagerLoadRelations.filter(name => name !== relationName)
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Remove all global scopes from this query.
|
|
*/
|
|
public withoutGlobalScopes(): this {
|
|
this.appliedScopes = new Collection<{accessor: string | Instantiable<Scope>; scope: ScopeClosure}>()
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Remove a specific scope from this query by its identifier.
|
|
* @param name
|
|
*/
|
|
public withoutGlobalScope(name: string | Instantiable<Scope>): this {
|
|
this.appliedScopes = this.appliedScopes.where('accessor', '=', name)
|
|
return this
|
|
}
|
|
|
|
/** Get the list of relations to eager-load. */
|
|
public getEagerLoadedRelations(): (keyof T)[] {
|
|
return [...this.eagerLoadRelations]
|
|
}
|
|
|
|
/**
|
|
* Given some format of keys of the model, try to normalize them to a flat array.
|
|
* @param keys
|
|
* @protected
|
|
*/
|
|
protected normalizeModelKeys(keys: ModelKeys): ModelKey[] {
|
|
if ( Array.isArray(keys) ) {
|
|
return keys
|
|
} else if ( keys instanceof Collection ) {
|
|
return keys.all()
|
|
}
|
|
|
|
return [keys]
|
|
}
|
|
|
|
/**
|
|
* Create a copy of this builder.
|
|
* @override to add implementation-specific pass-alongs.
|
|
*/
|
|
public clone(): ModelBuilder<T> {
|
|
const inst = super.clone() as ModelBuilder<T>
|
|
inst.eagerLoadRelations = [...this.eagerLoadRelations]
|
|
inst.appliedScopes = this.appliedScopes.clone()
|
|
return inst
|
|
}
|
|
}
|