|
|
|
@ -1,12 +1,13 @@
|
|
|
|
|
import {ModelKey, QueryRow, QuerySource} from "../types";
|
|
|
|
|
import {Container, Inject} from "../../di";
|
|
|
|
|
import {DatabaseService} from "../DatabaseService";
|
|
|
|
|
import {ModelBuilder} from "./ModelBuilder";
|
|
|
|
|
import {getFieldsMeta, ModelField} from "./Field";
|
|
|
|
|
import {deepCopy, BehaviorSubject, Pipe, Collection} from "../../util";
|
|
|
|
|
import {EscapeValueObject} from "../dialect/SQLDialect";
|
|
|
|
|
import {AppClass} from "../../lifecycle/AppClass";
|
|
|
|
|
import {Logging} from "../../service/Logging";
|
|
|
|
|
import {ModelKey, QueryRow, QuerySource} from '../types'
|
|
|
|
|
import {Container, Inject} from '../../di'
|
|
|
|
|
import {DatabaseService} from '../DatabaseService'
|
|
|
|
|
import {ModelBuilder} from './ModelBuilder'
|
|
|
|
|
import {getFieldsMeta, ModelField} from './Field'
|
|
|
|
|
import {deepCopy, BehaviorSubject, Pipe, Collection} from '../../util'
|
|
|
|
|
import {EscapeValueObject} from '../dialect/SQLDialect'
|
|
|
|
|
import {AppClass} from '../../lifecycle/AppClass'
|
|
|
|
|
import {Logging} from '../../service/Logging'
|
|
|
|
|
import {Connection} from '../connection/Connection'
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Base for classes that are mapped to tables in a database.
|
|
|
|
@ -19,7 +20,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
* The name of the connection this model should run through.
|
|
|
|
|
* @type string
|
|
|
|
|
*/
|
|
|
|
|
protected static connection: string = 'default'
|
|
|
|
|
protected static connection = 'default'
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The name of the table this model is stored in.
|
|
|
|
@ -36,7 +37,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
/**
|
|
|
|
|
* If false (default), the primary key will be excluded from INSERTs.
|
|
|
|
|
*/
|
|
|
|
|
protected static populateKeyOnInsert: boolean = false
|
|
|
|
|
protected static populateKeyOnInsert = false
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Optionally, the timestamp field set on creation.
|
|
|
|
@ -74,7 +75,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
* The original row fetched from the database.
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
protected _original?: QueryRow
|
|
|
|
|
protected originalSourceRow?: QueryRow
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Behavior subject that fires after the model is populated.
|
|
|
|
@ -124,7 +125,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
/**
|
|
|
|
|
* Get the table name for this model.
|
|
|
|
|
*/
|
|
|
|
|
public static tableName() {
|
|
|
|
|
public static tableName(): string {
|
|
|
|
|
return this.table
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -144,15 +145,16 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
/**
|
|
|
|
|
* Get the name of the connection where this model's table is found.
|
|
|
|
|
*/
|
|
|
|
|
public static connectionName() {
|
|
|
|
|
public static connectionName(): string {
|
|
|
|
|
return this.connection
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the database connection instance for this model's connection.
|
|
|
|
|
*/
|
|
|
|
|
public static getConnection() {
|
|
|
|
|
return Container.getContainer().make<DatabaseService>(DatabaseService).get(this.connectionName());
|
|
|
|
|
public static getConnection(): Connection {
|
|
|
|
|
return Container.getContainer().make<DatabaseService>(DatabaseService)
|
|
|
|
|
.get(this.connectionName())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -164,14 +166,17 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
* const user = await UserModel.query<UserModel>().where('name', 'LIKE', 'John Doe').first()
|
|
|
|
|
* ```
|
|
|
|
|
*/
|
|
|
|
|
public static query<T2 extends Model<T2>>() {
|
|
|
|
|
public static query<T2 extends Model<T2>>(): ModelBuilder<T2> {
|
|
|
|
|
const builder = <ModelBuilder<T2>> Container.getContainer().make<ModelBuilder<T2>>(ModelBuilder, this)
|
|
|
|
|
const source: QuerySource = this.querySource()
|
|
|
|
|
|
|
|
|
|
builder.connection(this.getConnection())
|
|
|
|
|
|
|
|
|
|
if ( typeof source === 'string' ) builder.from(source)
|
|
|
|
|
else builder.from(source.table, source.alias)
|
|
|
|
|
if ( typeof source === 'string' ) {
|
|
|
|
|
builder.from(source)
|
|
|
|
|
} else {
|
|
|
|
|
builder.from(source.table, source.alias)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getFieldsMeta(this.prototype).each(field => {
|
|
|
|
|
builder.field(field.databaseKey)
|
|
|
|
@ -185,7 +190,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
* Pre-fill the model's properties from the given values.
|
|
|
|
|
* Calls `boot()` under the hood.
|
|
|
|
|
*/
|
|
|
|
|
values?: {[key: string]: any}
|
|
|
|
|
values?: {[key: string]: any},
|
|
|
|
|
) {
|
|
|
|
|
super()
|
|
|
|
|
this.boot(values)
|
|
|
|
@ -199,7 +204,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
*
|
|
|
|
|
* @param values
|
|
|
|
|
*/
|
|
|
|
|
public boot(values?: any) {
|
|
|
|
|
public boot(values?: {[key: string]: unknown}): void {
|
|
|
|
|
if ( values ) {
|
|
|
|
|
getFieldsMeta(this).each(field => {
|
|
|
|
|
this.setFieldFromObject(field.modelKey, String(field.modelKey), values)
|
|
|
|
@ -216,8 +221,8 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
*
|
|
|
|
|
* @param row
|
|
|
|
|
*/
|
|
|
|
|
public async assumeFromSource(row: QueryRow) {
|
|
|
|
|
this._original = row
|
|
|
|
|
public async assumeFromSource(row: QueryRow): Promise<this> {
|
|
|
|
|
this.originalSourceRow = row
|
|
|
|
|
|
|
|
|
|
getFieldsMeta(this).each(field => {
|
|
|
|
|
this.setFieldFromObject(field.modelKey, field.databaseKey, row)
|
|
|
|
@ -236,7 +241,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
*
|
|
|
|
|
* @param object
|
|
|
|
|
*/
|
|
|
|
|
public async assume(object: { [key: string]: any }) {
|
|
|
|
|
public async assume(object: { [key: string]: any }): Promise<this> {
|
|
|
|
|
getFieldsMeta(this).each(field => {
|
|
|
|
|
if ( field.modelKey in object ) {
|
|
|
|
|
this.setFieldFromObject(field.modelKey, String(field.modelKey), object)
|
|
|
|
@ -249,26 +254,24 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
/**
|
|
|
|
|
* Get the value of the primary key of this model, if it exists.
|
|
|
|
|
*/
|
|
|
|
|
public key() {
|
|
|
|
|
public key(): string {
|
|
|
|
|
const ctor = this.constructor as typeof Model
|
|
|
|
|
|
|
|
|
|
const field = getFieldsMeta(this)
|
|
|
|
|
.firstWhere('databaseKey', '=', ctor.key)
|
|
|
|
|
|
|
|
|
|
if ( field ) {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
return this[field.modelKey]
|
|
|
|
|
return (this as any)[field.modelKey]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
return this[ctor.key]
|
|
|
|
|
return (this as any)[ctor.key]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns true if this instance's record has been persisted into the database.
|
|
|
|
|
*/
|
|
|
|
|
public exists() {
|
|
|
|
|
return !!this._original && !!this.key()
|
|
|
|
|
public exists(): boolean {
|
|
|
|
|
return Boolean(this.originalSourceRow) && Boolean(this.key())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -284,11 +287,13 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
const timestamps: { updated?: Date, created?: Date } = {}
|
|
|
|
|
|
|
|
|
|
if ( ctor.timestamps ) {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
if ( ctor.CREATED_AT ) timestamps.created = this[ctor.CREATED_AT]
|
|
|
|
|
if ( ctor.CREATED_AT ) {
|
|
|
|
|
timestamps.created = (this as any)[ctor.CREATED_AT]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
if ( ctor.UPDATED_AT ) timestamps.updated = this[ctor.UPDATED_AT]
|
|
|
|
|
if ( ctor.UPDATED_AT ) {
|
|
|
|
|
timestamps.updated = (this as any)[ctor.UPDATED_AT]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return timestamps
|
|
|
|
@ -312,8 +317,11 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
|
|
|
|
|
builder.connection(ModelClass.getConnection())
|
|
|
|
|
|
|
|
|
|
if ( typeof source === 'string' ) builder.from(source)
|
|
|
|
|
else builder.from(source.table, source.alias)
|
|
|
|
|
if ( typeof source === 'string' ) {
|
|
|
|
|
builder.from(source)
|
|
|
|
|
} else {
|
|
|
|
|
builder.from(source.table, source.alias)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getFieldsMeta(this).each(field => {
|
|
|
|
|
builder.field(field.databaseKey)
|
|
|
|
@ -343,15 +351,17 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
/**
|
|
|
|
|
* Get an array of all instances of this model.
|
|
|
|
|
*/
|
|
|
|
|
public async all() {
|
|
|
|
|
return this.query().get().all()
|
|
|
|
|
public async all(): Promise<T[]> {
|
|
|
|
|
return this.query().get()
|
|
|
|
|
.all()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Count all instances of this model in the database.
|
|
|
|
|
*/
|
|
|
|
|
public async count(): Promise<number> {
|
|
|
|
|
return this.query().get().count()
|
|
|
|
|
return this.query().get()
|
|
|
|
|
.count()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -365,7 +375,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
*
|
|
|
|
|
* @param column
|
|
|
|
|
*/
|
|
|
|
|
public qualify(column: string) {
|
|
|
|
|
public qualify(column: string): string {
|
|
|
|
|
const ctor = this.constructor as typeof Model
|
|
|
|
|
return `${ctor.tableName()}.${column}`
|
|
|
|
|
}
|
|
|
|
@ -384,7 +394,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
* a.qualifyKey() // => 'table_a.a_id'
|
|
|
|
|
* ```
|
|
|
|
|
*/
|
|
|
|
|
public qualifyKey() {
|
|
|
|
|
public qualifyKey(): string {
|
|
|
|
|
const ctor = this.constructor as typeof Model
|
|
|
|
|
return this.qualify(ctor.key)
|
|
|
|
|
}
|
|
|
|
@ -400,7 +410,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
*
|
|
|
|
|
* @param column
|
|
|
|
|
*/
|
|
|
|
|
public static qualify(column: string) {
|
|
|
|
|
public static qualify(column: string): string {
|
|
|
|
|
return `${this.tableName()}.${column}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -417,7 +427,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
* A.qualifyKey() // => 'table_a.a_id'
|
|
|
|
|
* ```
|
|
|
|
|
*/
|
|
|
|
|
public static qualifyKey() {
|
|
|
|
|
public static qualifyKey(): string {
|
|
|
|
|
return this.qualify(this.key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -426,7 +436,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
* return the unqualified name of the database column it corresponds to.
|
|
|
|
|
* @param modelKey
|
|
|
|
|
*/
|
|
|
|
|
public static propertyToColumn(modelKey: string) {
|
|
|
|
|
public static propertyToColumn(modelKey: string): string {
|
|
|
|
|
return getFieldsMeta(this)
|
|
|
|
|
.firstWhere('modelKey', '=', modelKey)?.databaseKey || modelKey
|
|
|
|
|
}
|
|
|
|
@ -434,7 +444,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
/**
|
|
|
|
|
* Get the unqualified name of the column corresponding to the primary key of this model.
|
|
|
|
|
*/
|
|
|
|
|
public keyName() {
|
|
|
|
|
public keyName(): string {
|
|
|
|
|
const ctor = this.constructor as typeof Model
|
|
|
|
|
return ctor.key
|
|
|
|
|
}
|
|
|
|
@ -446,11 +456,10 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
* Only fields with `@Field()` annotations will be included.
|
|
|
|
|
*/
|
|
|
|
|
public toQueryRow(): QueryRow {
|
|
|
|
|
const row = {}
|
|
|
|
|
const row: QueryRow = {}
|
|
|
|
|
|
|
|
|
|
getFieldsMeta(this).each(field => {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
row[field.databaseKey] = this[field.modelKey]
|
|
|
|
|
row[field.databaseKey] = (this as any)[field.modelKey]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return row
|
|
|
|
@ -462,13 +471,12 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
* the record was fetched from the database or created.
|
|
|
|
|
*/
|
|
|
|
|
public dirtyToQueryRow(): QueryRow {
|
|
|
|
|
const row = {}
|
|
|
|
|
const row: QueryRow = {}
|
|
|
|
|
|
|
|
|
|
getFieldsMeta(this)
|
|
|
|
|
.filter(this._isDirty)
|
|
|
|
|
.filter(this.isDirtyCheck)
|
|
|
|
|
.each(field => {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
row[field.databaseKey] = this[field.modelKey]
|
|
|
|
|
row[field.databaseKey] = (this as any)[field.modelKey]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return row
|
|
|
|
@ -478,8 +486,8 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
* Get an object of the database field => value mapping that was originally
|
|
|
|
|
* fetched from the database. Excludes changes to model properties.
|
|
|
|
|
*/
|
|
|
|
|
public getOriginalValues() {
|
|
|
|
|
return deepCopy(this._original)
|
|
|
|
|
public getOriginalValues(): QueryRow | undefined {
|
|
|
|
|
return deepCopy(this.originalSourceRow)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -495,12 +503,11 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
*
|
|
|
|
|
* @param fields
|
|
|
|
|
*/
|
|
|
|
|
public only(...fields: string[]) {
|
|
|
|
|
const row = {}
|
|
|
|
|
public only(...fields: string[]): QueryRow {
|
|
|
|
|
const row: QueryRow = {}
|
|
|
|
|
|
|
|
|
|
for ( const field of fields ) {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
row[field] = this[field]
|
|
|
|
|
row[field] = (this as any)[field]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return row
|
|
|
|
@ -512,8 +519,8 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
*
|
|
|
|
|
* Only fields with `@Field()` annotations are checked.
|
|
|
|
|
*/
|
|
|
|
|
public isDirty() {
|
|
|
|
|
return getFieldsMeta(this).some(this._isDirty)
|
|
|
|
|
public isDirty(): boolean {
|
|
|
|
|
return getFieldsMeta(this).some(this.isDirtyCheck)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -523,7 +530,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
*
|
|
|
|
|
* Only fields with `@Field()` annotations are checked.
|
|
|
|
|
*/
|
|
|
|
|
public isClean() {
|
|
|
|
|
public isClean(): boolean {
|
|
|
|
|
return !this.isDirty()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -532,18 +539,25 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
* the database, or if the given field never existed in the database.
|
|
|
|
|
* @param field
|
|
|
|
|
*/
|
|
|
|
|
public wasChanged(field: string) {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
return getFieldsMeta(this).pluck('modelKey').includes(field) && this[field] !== this._original[field]
|
|
|
|
|
public wasChanged(field: string): boolean {
|
|
|
|
|
return (
|
|
|
|
|
getFieldsMeta(this)
|
|
|
|
|
.pluck('modelKey')
|
|
|
|
|
.includes(field)
|
|
|
|
|
&& (
|
|
|
|
|
!this.originalSourceRow
|
|
|
|
|
|| (this as any)[field] !== this.originalSourceRow[field]
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns an array of MODEL fields that have been modified since this record
|
|
|
|
|
* was fetched from the database or created.
|
|
|
|
|
*/
|
|
|
|
|
public getDirtyFields() {
|
|
|
|
|
public getDirtyFields(): string[] {
|
|
|
|
|
return getFieldsMeta(this)
|
|
|
|
|
.filter(this._isDirty)
|
|
|
|
|
.filter(this.isDirtyCheck)
|
|
|
|
|
.pluck('modelKey')
|
|
|
|
|
.toArray()
|
|
|
|
|
}
|
|
|
|
@ -554,17 +568,15 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
* If the model doesn't yet exist, set the CREATED_AT date. Always
|
|
|
|
|
* sets the UPDATED_AT date.
|
|
|
|
|
*/
|
|
|
|
|
public touch() {
|
|
|
|
|
public touch(): this {
|
|
|
|
|
const constructor = (this.constructor as typeof Model)
|
|
|
|
|
if ( constructor.timestamps ) {
|
|
|
|
|
if ( constructor.UPDATED_AT ) {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
this[constructor.UPDATED_AT] = new Date()
|
|
|
|
|
(this as any)[constructor.UPDATED_AT] = new Date()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !this.exists() && constructor.CREATED_AT ) {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
this[constructor.CREATED_AT] = new Date()
|
|
|
|
|
(this as any)[constructor.CREATED_AT] = new Date()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return this
|
|
|
|
@ -587,8 +599,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
await this.updating$.next(this)
|
|
|
|
|
|
|
|
|
|
if ( !withoutTimestamps && ctor.timestamps && ctor.UPDATED_AT ) {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
this[ctor.UPDATED_AT] = new Date()
|
|
|
|
|
(this as any)[ctor.UPDATED_AT] = new Date()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await this.query()
|
|
|
|
@ -602,7 +613,9 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const data = result.rows.firstWhere(this.keyName(), '=', this.key())
|
|
|
|
|
if ( data ) await this.assumeFromSource(data)
|
|
|
|
|
if ( data ) {
|
|
|
|
|
await this.assumeFromSource(data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await this.updated$.next(this)
|
|
|
|
|
} else if ( !this.exists() ) {
|
|
|
|
@ -610,17 +623,15 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
|
|
|
|
|
if ( !withoutTimestamps ) {
|
|
|
|
|
if ( ctor.timestamps && ctor.CREATED_AT ) {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
this[ctor.CREATED_AT] = new Date()
|
|
|
|
|
(this as any)[ctor.CREATED_AT] = new Date()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( ctor.timestamps && ctor.UPDATED_AT ) {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
this[ctor.UPDATED_AT] = new Date()
|
|
|
|
|
(this as any)[ctor.UPDATED_AT] = new Date()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const row = this._buildInsertFieldObject()
|
|
|
|
|
const row = this.buildInsertFieldObject()
|
|
|
|
|
const returnable = new Collection<string>([this.keyName(), ...Object.keys(row)])
|
|
|
|
|
|
|
|
|
|
const result = await this.query()
|
|
|
|
@ -633,7 +644,9 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const data = result.rows.first()
|
|
|
|
|
if ( data ) await this.assumeFromSource(result)
|
|
|
|
|
if ( data ) {
|
|
|
|
|
await this.assumeFromSource(result)
|
|
|
|
|
}
|
|
|
|
|
await this.created$.next(this)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -646,22 +659,19 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
*
|
|
|
|
|
* Only fields with `@Field()` annotations are included.
|
|
|
|
|
*/
|
|
|
|
|
public toObject(): { [key: string]: any } {
|
|
|
|
|
public toObject(): QueryRow {
|
|
|
|
|
const ctor = this.constructor as typeof Model
|
|
|
|
|
const obj = {}
|
|
|
|
|
const obj: QueryRow = {}
|
|
|
|
|
|
|
|
|
|
getFieldsMeta(this).each(field => {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
obj[field.modelKey] = this[field.modelKey]
|
|
|
|
|
obj[String(field.modelKey)] = (this as any)[field.modelKey]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ctor.appends.forEach(field => {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
obj[field] = this[field]
|
|
|
|
|
obj[field] = (this as any)[field]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ctor.masks.forEach(field => {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
delete obj[field]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
@ -673,8 +683,8 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
*
|
|
|
|
|
* Only fields with `@Field()` annotations are included.
|
|
|
|
|
*/
|
|
|
|
|
public toJSON(): string {
|
|
|
|
|
return JSON.stringify(this.toObject())
|
|
|
|
|
public toJSON(): QueryRow {
|
|
|
|
|
return this.toObject()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -696,7 +706,7 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
*
|
|
|
|
|
* Overwrites any un-persisted changes in the current instance.
|
|
|
|
|
*/
|
|
|
|
|
public async refresh() {
|
|
|
|
|
public async refresh(): Promise<void> {
|
|
|
|
|
const results = this.query()
|
|
|
|
|
.clearFields()
|
|
|
|
|
.fields(...this.getLoadedDatabaseFields())
|
|
|
|
@ -705,7 +715,9 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
.get()
|
|
|
|
|
|
|
|
|
|
const row = await results.first()
|
|
|
|
|
if ( row ) await this.assumeFromSource(row)
|
|
|
|
|
if ( row ) {
|
|
|
|
|
await this.assumeFromSource(row)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -766,10 +778,9 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
protected get _isDirty() {
|
|
|
|
|
protected get isDirtyCheck(): (field: ModelField) => boolean {
|
|
|
|
|
return (field: ModelField) => {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
return this[field.modelKey] !== this._original[field.databaseKey]
|
|
|
|
|
return !this.originalSourceRow || (this as any)[field.modelKey] !== this.originalSourceRow[field.databaseKey]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -778,15 +789,18 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
protected getLoadedDatabaseFields(): string[] {
|
|
|
|
|
if ( !this._original ) return []
|
|
|
|
|
return Object.keys(this._original).map(String)
|
|
|
|
|
if ( !this.originalSourceRow ) {
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Object.keys(this.originalSourceRow).map(String)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Build an object mapping database fields to the values that should be inserted for them.
|
|
|
|
|
* @private
|
|
|
|
|
*/
|
|
|
|
|
private _buildInsertFieldObject(): EscapeValueObject {
|
|
|
|
|
private buildInsertFieldObject(): EscapeValueObject {
|
|
|
|
|
const ctor = this.constructor as typeof Model
|
|
|
|
|
|
|
|
|
|
return getFieldsMeta(this)
|
|
|
|
@ -795,19 +809,17 @@ export abstract class Model<T extends Model<T>> extends AppClass {
|
|
|
|
|
return fields.where('modelKey', '!=', this.keyName())
|
|
|
|
|
})
|
|
|
|
|
.get()
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
.keyMap('databaseKey', inst => this[inst.modelKey])
|
|
|
|
|
.keyMap('databaseKey', inst => (this as any)[inst.modelKey])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets a property on `this` to the value of a given property in `object`.
|
|
|
|
|
* @param this_field_name
|
|
|
|
|
* @param object_field_name
|
|
|
|
|
* @param thisFieldName
|
|
|
|
|
* @param objectFieldName
|
|
|
|
|
* @param object
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
protected setFieldFromObject(this_field_name: string | symbol, object_field_name: string, object: { [key: string]: any }) {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
this[this_field_name] = object[object_field_name]
|
|
|
|
|
protected setFieldFromObject(thisFieldName: string | symbol, objectFieldName: string, object: QueryRow): void {
|
|
|
|
|
(this as any)[thisFieldName] = object[objectFieldName]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|