Flesh out model; add comments
This commit is contained in:
parent
b915fcedf9
commit
c0777f77b5
@ -1,76 +1,203 @@
|
|||||||
import { Builder } from '../builder/Builder.ts'
|
import Database from '../service/Database.ts'
|
||||||
|
import ModelResultOperator from './ModelResultOperator.ts'
|
||||||
|
import ObjectResultOperator from '../builder/type/result/ObjectResultOperator.ts'
|
||||||
|
import {Builder} from '../builder/Builder.ts'
|
||||||
import {FieldSet, FieldValueObject, QuerySource} from '../builder/types.ts'
|
import {FieldSet, FieldValueObject, QuerySource} from '../builder/types.ts'
|
||||||
import {make} from '../../../di/src/global.ts'
|
import {make} from '../../../di/src/global.ts'
|
||||||
import Database from '../service/Database.ts'
|
|
||||||
import {QueryRow, ModelKey} from '../db/types.ts'
|
import {QueryRow, ModelKey} from '../db/types.ts'
|
||||||
import ModelResultOperator from './ModelResultOperator.ts'
|
|
||||||
import {get_fields_meta, ModelField, set_model_fields_meta} from './Field.ts'
|
import {get_fields_meta, ModelField, set_model_fields_meta} from './Field.ts'
|
||||||
import {Collection} from '../../../lib/src/collection/Collection.ts'
|
import {Collection} from '../../../lib/src/collection/Collection.ts'
|
||||||
import {logger} from '../../../lib/src/service/logging/global.ts'
|
import {logger} from '../../../lib/src/service/logging/global.ts'
|
||||||
import ObjectResultOperator from '../builder/type/result/ObjectResultOperator.ts'
|
|
||||||
import {QueryFilter} from './filter.ts'
|
import {QueryFilter} from './filter.ts'
|
||||||
import {BehaviorSubject} from '../../../lib/src/support/BehaviorSubject.ts'
|
import {BehaviorSubject} from '../../../lib/src/support/BehaviorSubject.ts'
|
||||||
import {Scope} from '../builder/Scope.ts'
|
import {Scope} from '../builder/Scope.ts'
|
||||||
|
|
||||||
// TODO separate read/write connections
|
// TODO separate read/write connections
|
||||||
// TODO manual dirty flags
|
// TODO manual dirty flags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for database models.
|
||||||
|
* @extends Builder
|
||||||
|
*/
|
||||||
export abstract class Model<T extends Model<T>> extends Builder<T> {
|
export abstract class Model<T extends Model<T>> extends Builder<T> {
|
||||||
|
/**
|
||||||
|
* The name of the connection this model should run through.
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
protected static connection: string
|
protected static connection: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the table this model is stored in.
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
protected static table: string
|
protected static table: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the column that uniquely identifies this model.
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
protected static key: string
|
protected static key: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionally, the timestamp field set on creation.
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
protected static readonly CREATED_AT = 'created_at'
|
protected static readonly CREATED_AT = 'created_at'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionally, the timestamp field set op update.
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
protected static readonly UPDATED_AT = 'updated_at'
|
protected static readonly UPDATED_AT = 'updated_at'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, the CREATED_AT and UPDATED_AT columns will be automatically set.
|
||||||
|
* @type boolean
|
||||||
|
*/
|
||||||
protected static timestamps = true
|
protected static timestamps = true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of global scopes to be applied to this model.
|
||||||
|
* @type Scope[]
|
||||||
|
*/
|
||||||
protected static global_scopes: Scope[] = []
|
protected static global_scopes: Scope[] = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of additional fields on the class that should
|
||||||
|
* be included in the object serializations.
|
||||||
|
* @type string[]
|
||||||
|
*/
|
||||||
protected static appends: string[] = []
|
protected static appends: string[] = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of fields on the class that should be excluded
|
||||||
|
* from the object serializations.
|
||||||
|
* @type string[]
|
||||||
|
*/
|
||||||
protected static masks: string[] = []
|
protected static masks: string[] = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The original query row that was fetched from the database.
|
||||||
|
* @type QueryRow
|
||||||
|
*/
|
||||||
protected _original?: QueryRow
|
protected _original?: QueryRow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behavior subject that fires after the model is populated.
|
||||||
|
*/
|
||||||
protected retrieved$ = new BehaviorSubject<Model<T>>()
|
protected retrieved$ = new BehaviorSubject<Model<T>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behavior subject that fires right before the model is saved.
|
||||||
|
*/
|
||||||
protected saving$ = new BehaviorSubject<Model<T>>()
|
protected saving$ = new BehaviorSubject<Model<T>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behavior subject that fires right after the model is saved.
|
||||||
|
*/
|
||||||
protected saved$ = new BehaviorSubject<Model<T>>()
|
protected saved$ = new BehaviorSubject<Model<T>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behavior subject that fires right before the model is updated.
|
||||||
|
*/
|
||||||
protected updating$ = new BehaviorSubject<Model<T>>()
|
protected updating$ = new BehaviorSubject<Model<T>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behavior subject that fires right after the model is updated.
|
||||||
|
*/
|
||||||
protected updated$ = new BehaviorSubject<Model<T>>()
|
protected updated$ = new BehaviorSubject<Model<T>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behavior subject that fires right before the model is inserted.
|
||||||
|
*/
|
||||||
protected creating$ = new BehaviorSubject<Model<T>>()
|
protected creating$ = new BehaviorSubject<Model<T>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behavior subject that fires right after the model is inserted.
|
||||||
|
*/
|
||||||
protected created$ = new BehaviorSubject<Model<T>>()
|
protected created$ = new BehaviorSubject<Model<T>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behavior subject that fires right before the model is deleted.
|
||||||
|
*/
|
||||||
protected deleting$ = new BehaviorSubject<Model<T>>()
|
protected deleting$ = new BehaviorSubject<Model<T>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behavior subject that fires right after the model is deleted.
|
||||||
|
*/
|
||||||
protected deleted$ = new BehaviorSubject<Model<T>>()
|
protected deleted$ = new BehaviorSubject<Model<T>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the model table name.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public static table_name() {
|
public static table_name() {
|
||||||
return this.table
|
return this.table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the model connection name.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public static connection_name() {
|
public static connection_name() {
|
||||||
return this.connection
|
return this.connection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the model connection.
|
||||||
|
* @return Connection
|
||||||
|
*/
|
||||||
public static get_connection() {
|
public static get_connection() {
|
||||||
return make(Database).connection(this.connection_name())
|
return make(Database).connection(this.connection_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a SELECT statement scoped to the model.
|
||||||
|
* @param {...string[]} fields
|
||||||
|
* @return Select
|
||||||
|
*/
|
||||||
public static select(...fields: FieldSet[]) {
|
public static select(...fields: FieldSet[]) {
|
||||||
return this.prototype.select(...fields)
|
return this.prototype.select(...fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an UPDATE query scoped to this model.
|
||||||
|
* @return Update
|
||||||
|
*/
|
||||||
public static update() {
|
public static update() {
|
||||||
return this.prototype.update()
|
return this.prototype.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an INSERT query scoped to this model.
|
||||||
|
* @return Insert
|
||||||
|
*/
|
||||||
public static insert() {
|
public static insert() {
|
||||||
return this.prototype.insert()
|
return this.prototype.insert()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a DELETE query scoped to this model.
|
||||||
|
* @return Delete
|
||||||
|
*/
|
||||||
public static delete() {
|
public static delete() {
|
||||||
return this.prototype.delete()
|
return this.prototype.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a TRUNCATE query scoped to this model.
|
||||||
|
* @return Truncate
|
||||||
|
*/
|
||||||
public static truncate() {
|
public static truncate() {
|
||||||
return this.prototype.truncate()
|
return this.prototype.truncate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an UPDATE query scoped to this model.
|
||||||
|
* @param {QuerySource} [target]
|
||||||
|
* @param {string} [alias]
|
||||||
|
* @return Update
|
||||||
|
*/
|
||||||
public update(target?: QuerySource, alias?: string) {
|
public update(target?: QuerySource, alias?: string) {
|
||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
return super.update()
|
return super.update()
|
||||||
@ -80,6 +207,10 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
.target_operator(make(ModelResultOperator, constructor))
|
.target_operator(make(ModelResultOperator, constructor))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a SELECT query scoped to this model.
|
||||||
|
* @param {...FieldSet} fields
|
||||||
|
*/
|
||||||
public select(...fields: FieldSet[]) {
|
public select(...fields: FieldSet[]) {
|
||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
return super.select(...fields)
|
return super.select(...fields)
|
||||||
@ -89,6 +220,12 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
.target_operator(make(ModelResultOperator, constructor))
|
.target_operator(make(ModelResultOperator, constructor))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an INSERT query scoped to this model.
|
||||||
|
* @param {QuerySource} [target]
|
||||||
|
* @param {string} [alias]
|
||||||
|
* @return Insert
|
||||||
|
*/
|
||||||
public insert(target?: QuerySource, alias?: string) {
|
public insert(target?: QuerySource, alias?: string) {
|
||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
return super.insert()
|
return super.insert()
|
||||||
@ -97,6 +234,12 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
.target_operator(make(ModelResultOperator, constructor))
|
.target_operator(make(ModelResultOperator, constructor))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a DELETE query scoped to this model.
|
||||||
|
* @param {QuerySource} [target]
|
||||||
|
* @param {string} [alias]
|
||||||
|
* @return Delete
|
||||||
|
*/
|
||||||
public delete(target?: QuerySource, alias?: string) {
|
public delete(target?: QuerySource, alias?: string) {
|
||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
return super.delete()
|
return super.delete()
|
||||||
@ -106,6 +249,12 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
.target_operator(make(ModelResultOperator, constructor))
|
.target_operator(make(ModelResultOperator, constructor))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a TRUNCATE query scoped to this model.
|
||||||
|
* @param {QuerySource} [target]
|
||||||
|
* @param {string} [alias]
|
||||||
|
* @return Truncate
|
||||||
|
*/
|
||||||
public truncate(target?: QuerySource, alias?: string) {
|
public truncate(target?: QuerySource, alias?: string) {
|
||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
return super.truncate()
|
return super.truncate()
|
||||||
@ -114,6 +263,10 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
.target_operator(make(ObjectResultOperator))
|
.target_operator(make(ObjectResultOperator))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate the model.
|
||||||
|
* @param {object} [values] - optionally, row values to populate the model
|
||||||
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
values?: any
|
values?: any
|
||||||
) {
|
) {
|
||||||
@ -121,6 +274,10 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
this.boot(values)
|
this.boot(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boot the model after creation.
|
||||||
|
* @param [values]
|
||||||
|
*/
|
||||||
public boot(values?: any) {
|
public boot(values?: any) {
|
||||||
if ( values ) {
|
if ( values ) {
|
||||||
get_fields_meta(this).each(field_def => {
|
get_fields_meta(this).each(field_def => {
|
||||||
@ -131,6 +288,11 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a query row, load the values into this model.
|
||||||
|
* @param {QueryRow} row
|
||||||
|
* @return Model
|
||||||
|
*/
|
||||||
public assume_from_source(row: QueryRow) {
|
public assume_from_source(row: QueryRow) {
|
||||||
this._original = row
|
this._original = row
|
||||||
get_fields_meta(this).each(field_def => {
|
get_fields_meta(this).each(field_def => {
|
||||||
@ -142,6 +304,10 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If applicable get an object of normalized timestamps.
|
||||||
|
* @return { updated_at?: Date, created_at?: Date }
|
||||||
|
*/
|
||||||
public timestamps(): { updated_at?: Date, created_at?: Date } {
|
public timestamps(): { updated_at?: Date, created_at?: Date } {
|
||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
const timestamps: { updated_at?: Date, created_at?: Date } = {}
|
const timestamps: { updated_at?: Date, created_at?: Date } = {}
|
||||||
@ -155,22 +321,41 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
return timestamps
|
return timestamps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a Select statement to pull back this model's database fields.
|
||||||
|
* @param {...FieldSet} other_fields
|
||||||
|
*/
|
||||||
public static model_select(...other_fields: FieldSet[]) {
|
public static model_select(...other_fields: FieldSet[]) {
|
||||||
const fields = get_fields_meta(this.prototype).pluck('database_key').toArray()
|
const fields = get_fields_meta(this.prototype).pluck('database_key').toArray()
|
||||||
return this.select(...[...fields, ...other_fields])
|
return this.select(...[...fields, ...other_fields])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a collection of model instances given a filter.
|
||||||
|
* @param {QueryFilter} filter
|
||||||
|
* @return ResultCollection
|
||||||
|
*/
|
||||||
public static async find(filter: QueryFilter = {}) {
|
public static async find(filter: QueryFilter = {}) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return await this.model_select().filter(filter).results()
|
return await this.model_select().filter(filter).results()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a single instance of the model given a filter.
|
||||||
|
* @param {QueryFilter} filter
|
||||||
|
* @return undefined | Model
|
||||||
|
*/
|
||||||
public static async find_one(filter: QueryFilter = {}) {
|
public static async find_one(filter: QueryFilter = {}) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const result = await this.model_select().filter(filter).limit(1).results()
|
const result = await this.model_select().filter(filter).limit(1).results()
|
||||||
return result.first()
|
return result.first()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an instance of this model by primary key.
|
||||||
|
* @param {ModelKey} key
|
||||||
|
* @return undefined | Model
|
||||||
|
*/
|
||||||
public static async find_by_key(key: ModelKey): Promise<undefined | typeof Model> {
|
public static async find_by_key(key: ModelKey): Promise<undefined | typeof Model> {
|
||||||
const result = await this.model_select()
|
const result = await this.model_select()
|
||||||
.where(this.qualified_key_name(), '=', key)
|
.where(this.qualified_key_name(), '=', key)
|
||||||
@ -180,11 +365,19 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
return result.first()
|
return result.first()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count the number of records of this model.
|
||||||
|
* @return Promise<number>
|
||||||
|
*/
|
||||||
public static async count(): Promise<number> {
|
public static async count(): Promise<number> {
|
||||||
const result = this.model_select().results()
|
const result = this.model_select().results()
|
||||||
return await result.count()
|
return await result.count()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a filter checking if a model field is dirty.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
protected get _is_dirty() {
|
protected get _is_dirty() {
|
||||||
return (field_def: ModelField) => {
|
return (field_def: ModelField) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -192,6 +385,10 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a query row containing all database values from this model.
|
||||||
|
* @return QueryRow
|
||||||
|
*/
|
||||||
public to_row(): QueryRow {
|
public to_row(): QueryRow {
|
||||||
const row = {}
|
const row = {}
|
||||||
this.field_defs()
|
this.field_defs()
|
||||||
@ -202,6 +399,10 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a query row containing the changed values from this model.
|
||||||
|
* @return QueryRow
|
||||||
|
*/
|
||||||
public dirty_to_row(): QueryRow {
|
public dirty_to_row(): QueryRow {
|
||||||
const row = {}
|
const row = {}
|
||||||
this.field_defs()
|
this.field_defs()
|
||||||
@ -224,6 +425,12 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
return get_fields_meta(this.prototype)
|
return get_fields_meta(this.prototype)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a collection of field definitions that contains information
|
||||||
|
* on which database fields correspond to which model fields, and
|
||||||
|
* their types.
|
||||||
|
* @return Collection<ModelField>
|
||||||
|
*/
|
||||||
public field_defs(): Collection<ModelField> {
|
public field_defs(): Collection<ModelField> {
|
||||||
return (this.constructor as typeof Model).fields()
|
return (this.constructor as typeof Model).fields()
|
||||||
}
|
}
|
||||||
@ -299,6 +506,12 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
return !this.is_dirty()
|
return !this.is_dirty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given field has been changed from the original
|
||||||
|
* value fetched from the database.
|
||||||
|
* @param string field
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
public was_changed(field: string) {
|
public was_changed(field: string) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return this.field_defs().pluck('model_key').includes(field) && this[field] !== this._original[field]
|
return this.field_defs().pluck('model_key').includes(field) && this[field] !== this._original[field]
|
||||||
@ -323,6 +536,9 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
// support for soft deletes!
|
// support for soft deletes!
|
||||||
// force delete - for soft deleted models
|
// force delete - for soft deleted models
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Touch the UPDATED_AT/CREATED_AT timestamps where appropriate.
|
||||||
|
*/
|
||||||
public touch() {
|
public touch() {
|
||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
if ( constructor.timestamps ) {
|
if ( constructor.timestamps ) {
|
||||||
@ -339,10 +555,20 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the result collection for all records of this model.
|
||||||
|
* @return ResultCollection
|
||||||
|
*/
|
||||||
public static all() {
|
public static all() {
|
||||||
return this.model_select().results()
|
return this.model_select().results()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persist the model's record into the database. If we have a key,
|
||||||
|
* update the record. Otherwise, perform an insert.
|
||||||
|
* @param without_timestamps - if true, the UPDATED_AT/CREATED_AT timestamps will not be touched
|
||||||
|
* @return Promise<Model>
|
||||||
|
*/
|
||||||
public async save({ without_timestamps = false }): Promise<Model<T>> {
|
public async save({ without_timestamps = false }): Promise<Model<T>> {
|
||||||
await this.saving$.next(this)
|
await this.saving$.next(this)
|
||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
@ -409,6 +635,12 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a field value object from the values on this model to
|
||||||
|
* populate an INSERT query.
|
||||||
|
* @private
|
||||||
|
* @return FieldValueObject
|
||||||
|
*/
|
||||||
protected _build_insert_field_object(): FieldValueObject {
|
protected _build_insert_field_object(): FieldValueObject {
|
||||||
const fields = this.field_defs().whereNot('model_key', '=', this.key_name())
|
const fields = this.field_defs().whereNot('model_key', '=', this.key_name())
|
||||||
const values = {}
|
const values = {}
|
||||||
@ -419,11 +651,20 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of fields that were loaded from the DB.
|
||||||
|
* @private
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
protected _loaded_database_fields(): string[] {
|
protected _loaded_database_fields(): string[] {
|
||||||
if ( typeof this._original === 'undefined' ) return []
|
if ( typeof this._original === 'undefined' ) return []
|
||||||
return Object.keys(this._original).map(String)
|
return Object.keys(this._original).map(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete one or more records from this model's table by ID.
|
||||||
|
* @param {ModelKey | ModelKey[]} id_or_ids
|
||||||
|
*/
|
||||||
public static async destroy(id_or_ids: ModelKey | ModelKey[]) {
|
public static async destroy(id_or_ids: ModelKey | ModelKey[]) {
|
||||||
const ids = Array.isArray(id_or_ids) ? id_or_ids : [id_or_ids]
|
const ids = Array.isArray(id_or_ids) ? id_or_ids : [id_or_ids]
|
||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
@ -440,6 +681,9 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete this model's record from the database.
|
||||||
|
*/
|
||||||
public async destroy(): Promise<void> {
|
public async destroy(): Promise<void> {
|
||||||
await this.deleting$.next(this)
|
await this.deleting$.next(this)
|
||||||
|
|
||||||
@ -459,6 +703,10 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
await this.deleted$.next(this)
|
await this.deleted$.next(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert this model's record to a plain object.
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
public to_object(): { [key: string]: any } {
|
public to_object(): { [key: string]: any } {
|
||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
const obj = {}
|
const obj = {}
|
||||||
@ -481,10 +729,18 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert this model's record to a JSON string.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public to_json(): string {
|
public to_json(): string {
|
||||||
return JSON.stringify(this.to_object())
|
return JSON.stringify(this.to_object())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a fresh instance of this model's record from the database.
|
||||||
|
* @return Model
|
||||||
|
*/
|
||||||
public async fresh(): Promise<Model<T>> {
|
public async fresh(): Promise<Model<T>> {
|
||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
const fields = this.field_defs()
|
const fields = this.field_defs()
|
||||||
@ -499,6 +755,9 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
return result.first()
|
return result.first()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-fetch the fields on this model from the database.
|
||||||
|
*/
|
||||||
public async refresh() {
|
public async refresh() {
|
||||||
const constructor = (this.constructor as typeof Model)
|
const constructor = (this.constructor as typeof Model)
|
||||||
const values = await this.select(this._loaded_database_fields())
|
const values = await this.select(this._loaded_database_fields())
|
||||||
@ -511,6 +770,11 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
if ( row ) this.assume_from_source(row)
|
if ( row ) this.assume_from_source(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate the fields on the given model from the fields on this model, except for the key.
|
||||||
|
* @param Model other
|
||||||
|
* @return Model
|
||||||
|
*/
|
||||||
public populate(other: Model<T>): Model<T> {
|
public populate(other: Model<T>): Model<T> {
|
||||||
const row = this.to_row()
|
const row = this.to_row()
|
||||||
delete row[this.key_name()]
|
delete row[this.key_name()]
|
||||||
@ -518,6 +782,12 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
return other
|
return other
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given another model, determines if that model represents the same
|
||||||
|
* record as this model.
|
||||||
|
* @param Model other_instance
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
public is(other_instance: Model<any>): boolean {
|
public is(other_instance: Model<any>): boolean {
|
||||||
const this_constructor = (this.constructor as typeof Model)
|
const this_constructor = (this.constructor as typeof Model)
|
||||||
const other_constructor = (other_instance.constructor as typeof Model)
|
const other_constructor = (other_instance.constructor as typeof Model)
|
||||||
@ -527,6 +797,12 @@ export abstract class Model<T extends Model<T>> extends Builder<T> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given another model, determines if that model represents a different
|
||||||
|
* record as this model.
|
||||||
|
* @param Model other_instance
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
public is_not(other_instance: Model<any>): boolean {
|
public is_not(other_instance: Model<any>): boolean {
|
||||||
return !this.is(other_instance)
|
return !this.is(other_instance)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user