|
|
@ -1,5 +1,5 @@
|
|
|
|
import {ModelKey, QueryRow, QuerySource} from '../types'
|
|
|
|
import {ModelKey, QueryRow, QuerySource} from '../types'
|
|
|
|
import {Container, Inject, Instantiable, StaticClass} from '../../di'
|
|
|
|
import {Container, Inject, Instantiable, isInstantiable, StaticClass} from '../../di'
|
|
|
|
import {DatabaseService} from '../DatabaseService'
|
|
|
|
import {DatabaseService} from '../DatabaseService'
|
|
|
|
import {ModelBuilder} from './ModelBuilder'
|
|
|
|
import {ModelBuilder} from './ModelBuilder'
|
|
|
|
import {getFieldsMeta, ModelField} from './Field'
|
|
|
|
import {getFieldsMeta, ModelField} from './Field'
|
|
|
@ -21,6 +21,7 @@ import {Relation, RelationValue} from './relation/Relation'
|
|
|
|
import {HasOne} from './relation/HasOne'
|
|
|
|
import {HasOne} from './relation/HasOne'
|
|
|
|
import {HasMany} from './relation/HasMany'
|
|
|
|
import {HasMany} from './relation/HasMany'
|
|
|
|
import {HasOneOrMany} from './relation/HasOneOrMany'
|
|
|
|
import {HasOneOrMany} from './relation/HasOneOrMany'
|
|
|
|
|
|
|
|
import {Scope, ScopeClosure} from './scope/Scope'
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Base for classes that are mapped to tables in a database.
|
|
|
|
* Base for classes that are mapped to tables in a database.
|
|
|
@ -87,6 +88,12 @@ export abstract class Model<T extends Model<T>> extends AppClass implements Bus
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
protected static masks: string[] = []
|
|
|
|
protected static masks: string[] = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Relations that should be eager-loaded by default.
|
|
|
|
|
|
|
|
* @protected
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
protected with: (keyof T)[] = []
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* The original row fetched from the database.
|
|
|
|
* The original row fetched from the database.
|
|
|
|
* @protected
|
|
|
|
* @protected
|
|
|
@ -105,6 +112,8 @@ export abstract class Model<T extends Model<T>> extends AppClass implements Bus
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public relationCache: Collection<{ accessor: string | symbol, relation: Relation<T, any, any> }> = new Collection<{accessor: string | symbol; relation: Relation<T, any, any>}>()
|
|
|
|
public relationCache: Collection<{ accessor: string | symbol, relation: Relation<T, any, any> }> = new Collection<{accessor: string | symbol; relation: Relation<T, any, any>}>()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected scopes: Collection<{ accessor: string | Instantiable<Scope>, scope: ScopeClosure }> = new Collection<{accessor: string | Instantiable<Scope>; scope: ScopeClosure}>()
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Get the table name for this model.
|
|
|
|
* Get the table name for this model.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
@ -165,6 +174,14 @@ export abstract class Model<T extends Model<T>> extends AppClass implements Bus
|
|
|
|
builder.field(field.databaseKey)
|
|
|
|
builder.field(field.databaseKey)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for ( const relation of this.prototype.with ) {
|
|
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
|
|
|
builder.with(relation)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
builder.withScopes(this.prototype.scopes)
|
|
|
|
|
|
|
|
|
|
|
|
return builder
|
|
|
|
return builder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -317,6 +334,12 @@ export abstract class Model<T extends Model<T>> extends AppClass implements Bus
|
|
|
|
builder.field(field.databaseKey)
|
|
|
|
builder.field(field.databaseKey)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for ( const relation of this.with ) {
|
|
|
|
|
|
|
|
builder.with(relation)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
builder.withScopes(this.scopes)
|
|
|
|
|
|
|
|
|
|
|
|
return builder
|
|
|
|
return builder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -1013,7 +1036,7 @@ export abstract class Model<T extends Model<T>> extends AppClass implements Bus
|
|
|
|
* @param name
|
|
|
|
* @param name
|
|
|
|
* @protected
|
|
|
|
* @protected
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
protected getRelation<T2 extends Model<T2>>(name: keyof this): Relation<T, T2, RelationValue<T2>> {
|
|
|
|
public getRelation<T2 extends Model<T2>>(name: keyof this): Relation<T, T2, RelationValue<T2>> {
|
|
|
|
const relFn = this[name]
|
|
|
|
const relFn = this[name]
|
|
|
|
|
|
|
|
|
|
|
|
if ( relFn instanceof Relation ) {
|
|
|
|
if ( relFn instanceof Relation ) {
|
|
|
@ -1029,4 +1052,60 @@ export abstract class Model<T extends Model<T>> extends AppClass implements Bus
|
|
|
|
|
|
|
|
|
|
|
|
throw new TypeError(`Cannot get relation of name: ${name}. Method does not return a Relation.`)
|
|
|
|
throw new TypeError(`Cannot get relation of name: ${name}. Method does not return a Relation.`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Register a scope on the model.
|
|
|
|
|
|
|
|
* @param scope
|
|
|
|
|
|
|
|
* @protected
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
protected scope(scope: Instantiable<Scope> | ScopeClosure): this {
|
|
|
|
|
|
|
|
if ( isInstantiable(scope) ) {
|
|
|
|
|
|
|
|
if ( !this.hasScope(scope) ) {
|
|
|
|
|
|
|
|
this.scopes.push({
|
|
|
|
|
|
|
|
accessor: scope,
|
|
|
|
|
|
|
|
scope: builder => (this.make<Scope>(scope)).apply(builder),
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
this.scopes.push({
|
|
|
|
|
|
|
|
accessor: uuid4(),
|
|
|
|
|
|
|
|
scope,
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Register a scope on the model with a specific name.
|
|
|
|
|
|
|
|
* @param name
|
|
|
|
|
|
|
|
* @param scope
|
|
|
|
|
|
|
|
* @protected
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
protected namedScope(name: string, scope: Instantiable<Scope> | ScopeClosure): this {
|
|
|
|
|
|
|
|
if ( isInstantiable(scope) ) {
|
|
|
|
|
|
|
|
if ( !this.hasScope(scope) ) {
|
|
|
|
|
|
|
|
this.scopes.push({
|
|
|
|
|
|
|
|
accessor: name,
|
|
|
|
|
|
|
|
scope: builder => (this.make<Scope>(scope)).apply(builder),
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
this.scopes.push({
|
|
|
|
|
|
|
|
accessor: name,
|
|
|
|
|
|
|
|
scope,
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns true if the current model has a scope with the given identifier.
|
|
|
|
|
|
|
|
* @param name
|
|
|
|
|
|
|
|
* @protected
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
protected hasScope(name: string | Instantiable<Scope>): boolean {
|
|
|
|
|
|
|
|
return Boolean(this.scopes.firstWhere('accessor', '=', name))
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|