|
|
@ -3,7 +3,7 @@ import {Container, Inject, Instantiable, isInstantiable} 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'
|
|
|
|
import {deepCopy, Collection, uuid4, isKeyof, Pipeline} from '../../util'
|
|
|
|
import {deepCopy, Collection, uuid4, isKeyof, Pipeline, hasOwnProperty} from '../../util'
|
|
|
|
import {EscapeValueObject} from '../dialect/SQLDialect'
|
|
|
|
import {EscapeValueObject} from '../dialect/SQLDialect'
|
|
|
|
import {Logging} from '../../service/Logging'
|
|
|
|
import {Logging} from '../../service/Logging'
|
|
|
|
import {Connection} from '../connection/Connection'
|
|
|
|
import {Connection} from '../connection/Connection'
|
|
|
@ -96,6 +96,13 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
protected originalSourceRow?: QueryRow
|
|
|
|
protected originalSourceRow?: QueryRow
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Database fields that should be run on the next save, even if the
|
|
|
|
|
|
|
|
* fields are not mapped to members on the model.
|
|
|
|
|
|
|
|
* @protected
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
protected dirtySourceRow?: QueryRow
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Cache of relation instances by property accessor.
|
|
|
|
* Cache of relation instances by property accessor.
|
|
|
|
* This is used by the `@Relation()` decorator to cache Relation instances.
|
|
|
|
* This is used by the `@Relation()` decorator to cache Relation instances.
|
|
|
@ -175,8 +182,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
builder.withScopes(inst.scopes)
|
|
|
|
inst.applyScopes(builder)
|
|
|
|
|
|
|
|
|
|
|
|
return builder
|
|
|
|
return builder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -214,6 +220,49 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public getColumn(key: string): unknown {
|
|
|
|
|
|
|
|
if ( this.dirtySourceRow && hasOwnProperty(this.dirtySourceRow, key) ) {
|
|
|
|
|
|
|
|
return this.dirtySourceRow[key]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const field = getFieldsMeta(this)
|
|
|
|
|
|
|
|
.firstWhere('databaseKey', '=', key)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( field ) {
|
|
|
|
|
|
|
|
return (this as any)[field.modelKey]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this.originalSourceRow?.[key]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Sets a value in the override row and (if applicable) associated class property
|
|
|
|
|
|
|
|
* for the given database column.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param key
|
|
|
|
|
|
|
|
* @param value
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public setColumn(key: string, value: unknown): this {
|
|
|
|
|
|
|
|
// Set the property on the database result row, if one exists
|
|
|
|
|
|
|
|
if ( !this.dirtySourceRow ) {
|
|
|
|
|
|
|
|
this.dirtySourceRow = {}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.dirtySourceRow[key] = value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Set the property on the mapped field on the class, if one exists
|
|
|
|
|
|
|
|
const field = getFieldsMeta(this)
|
|
|
|
|
|
|
|
.firstWhere('databaseKey', '=', key)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( field ) {
|
|
|
|
|
|
|
|
this.setFieldFromObject(field.modelKey, field.databaseKey, {
|
|
|
|
|
|
|
|
[key]: value,
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Given a row from the database, set the properties on this model that correspond to
|
|
|
|
* Given a row from the database, set the properties on this model that correspond to
|
|
|
|
* fields on that database.
|
|
|
|
* fields on that database.
|
|
|
@ -256,7 +305,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Get the value of the primary key of this model, if it exists.
|
|
|
|
* Get the value of the primary key of this model, if it exists.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public key(): string {
|
|
|
|
public key(): string|number {
|
|
|
|
const ctor = this.constructor as typeof Model
|
|
|
|
const ctor = this.constructor as typeof Model
|
|
|
|
|
|
|
|
|
|
|
|
const field = getFieldsMeta(this)
|
|
|
|
const field = getFieldsMeta(this)
|
|
|
@ -333,8 +382,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
|
|
|
|
builder.with(relation)
|
|
|
|
builder.with(relation)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
builder.withScopes(this.scopes)
|
|
|
|
this.applyScopes(builder)
|
|
|
|
|
|
|
|
|
|
|
|
return builder
|
|
|
|
return builder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -445,6 +493,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
|
|
|
|
* @param modelKey
|
|
|
|
* @param modelKey
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public static propertyToColumn(modelKey: string): string {
|
|
|
|
public static propertyToColumn(modelKey: string): string {
|
|
|
|
|
|
|
|
console.log('propertyToColumn', modelKey, getFieldsMeta(this), this) // eslint-disable-line no-console
|
|
|
|
return getFieldsMeta(this)
|
|
|
|
return getFieldsMeta(this)
|
|
|
|
.firstWhere('modelKey', '=', modelKey)?.databaseKey || modelKey
|
|
|
|
.firstWhere('modelKey', '=', modelKey)?.databaseKey || modelKey
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -487,7 +536,10 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
|
|
|
|
row[field.databaseKey] = (this as any)[field.modelKey]
|
|
|
|
row[field.databaseKey] = (this as any)[field.modelKey]
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
return row
|
|
|
|
return {
|
|
|
|
|
|
|
|
...row,
|
|
|
|
|
|
|
|
...(this.dirtySourceRow || {}),
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -621,10 +673,12 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const data = result.rows.firstWhere(this.keyName(), '=', this.key())
|
|
|
|
const data = result.rows.firstWhere(this.keyName(), '=', this.key())
|
|
|
|
|
|
|
|
this.logging.debug({updata: data})
|
|
|
|
if ( data ) {
|
|
|
|
if ( data ) {
|
|
|
|
await this.assumeFromSource(data)
|
|
|
|
await this.assumeFromSource(data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
delete this.dirtySourceRow
|
|
|
|
await this.push(new ModelUpdatedEvent<T>(this as any))
|
|
|
|
await this.push(new ModelUpdatedEvent<T>(this as any))
|
|
|
|
} else if ( !this.exists() ) {
|
|
|
|
} else if ( !this.exists() ) {
|
|
|
|
await this.push(new ModelCreatingEvent<T>(this as any))
|
|
|
|
await this.push(new ModelCreatingEvent<T>(this as any))
|
|
|
@ -654,10 +708,12 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const data = result.rows.first()
|
|
|
|
const data = result.rows.first()
|
|
|
|
|
|
|
|
this.logging.debug({inserta: data})
|
|
|
|
if ( data ) {
|
|
|
|
if ( data ) {
|
|
|
|
await this.assumeFromSource(data)
|
|
|
|
await this.assumeFromSource(data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
delete this.dirtySourceRow
|
|
|
|
await this.push(new ModelCreatedEvent<T>(this as any))
|
|
|
|
await this.push(new ModelCreatedEvent<T>(this as any))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -808,7 +864,14 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
protected get isDirtyCheck(): (field: ModelField) => boolean {
|
|
|
|
protected get isDirtyCheck(): (field: ModelField) => boolean {
|
|
|
|
return (field: ModelField) => {
|
|
|
|
return (field: ModelField) => {
|
|
|
|
return !this.originalSourceRow || (this as any)[field.modelKey] !== this.originalSourceRow[field.databaseKey]
|
|
|
|
return Boolean(
|
|
|
|
|
|
|
|
!this.originalSourceRow
|
|
|
|
|
|
|
|
|| (this as any)[field.modelKey] !== this.originalSourceRow[field.databaseKey]
|
|
|
|
|
|
|
|
|| (
|
|
|
|
|
|
|
|
this.dirtySourceRow
|
|
|
|
|
|
|
|
&& hasOwnProperty(this.dirtySourceRow, field.databaseKey)
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -833,12 +896,17 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
|
|
|
|
|
|
|
|
|
|
|
|
this.logging.debug(`buildInsertFieldObject populateKeyOnInsert? ${ctor.populateKeyOnInsert}; keyName: ${this.keyName()}`)
|
|
|
|
this.logging.debug(`buildInsertFieldObject populateKeyOnInsert? ${ctor.populateKeyOnInsert}; keyName: ${this.keyName()}`)
|
|
|
|
|
|
|
|
|
|
|
|
return Pipeline.id<Collection<ModelField>>()
|
|
|
|
const row = Pipeline.id<Collection<ModelField>>()
|
|
|
|
.unless(ctor.populateKeyOnInsert, fields => {
|
|
|
|
.unless(ctor.populateKeyOnInsert, fields => {
|
|
|
|
return fields.where('databaseKey', '!=', this.keyName())
|
|
|
|
return fields.where('databaseKey', '!=', this.keyName())
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.apply(getFieldsMeta(this))
|
|
|
|
.apply(getFieldsMeta(this))
|
|
|
|
.keyMap('databaseKey', inst => (this as any)[inst.modelKey])
|
|
|
|
.keyMap('databaseKey', inst => (this as any)[inst.modelKey])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
...row,
|
|
|
|
|
|
|
|
...(this.dirtySourceRow || {}),
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -918,7 +986,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
|
|
|
|
* @param related
|
|
|
|
* @param related
|
|
|
|
* @param relationName
|
|
|
|
* @param relationName
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public belongsToOne<T2 extends Model<T2>>(related: Instantiable<T>, relationName: keyof T2): HasOne<T, T2> {
|
|
|
|
public belongsToOne<T2 extends Model<T2>>(related: Instantiable<T2>, relationName: keyof T2): HasOne<T, T2> {
|
|
|
|
const relatedInst = this.make(related) as T2
|
|
|
|
const relatedInst = this.make(related) as T2
|
|
|
|
const relation = relatedInst.getRelation(relationName)
|
|
|
|
const relation = relatedInst.getRelation(relationName)
|
|
|
|
|
|
|
|
|
|
|
@ -1055,4 +1123,12 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
|
|
|
|
protected hasScope(name: string | Instantiable<Scope>): boolean {
|
|
|
|
protected hasScope(name: string | Instantiable<Scope>): boolean {
|
|
|
|
return Boolean(this.scopes.firstWhere('accessor', '=', name))
|
|
|
|
return Boolean(this.scopes.firstWhere('accessor', '=', name))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Apply the default scopes to this model to the given query builder.
|
|
|
|
|
|
|
|
* @param builder
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public applyScopes(builder: ModelBuilder<T>): void {
|
|
|
|
|
|
|
|
builder.withScopes(this.scopes)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|