BaseModel

Base class for models

Parameters

(None)
(defined in /src/model/index.ts:891)
public constructor(data?: object) {
    data = data || {};
    const ctor = this.constructor as typeof BaseModel;
    const schema = ctor._schema;
    const adapter = ctor._adapter;

    Object.defineProperty(this, '_prev_attributes', { writable: true, value: {} });
    if (ctor.dirty_tracking) {
      Object.defineProperty(this, '_attributes', { value: {} });
      Object.defineProperty(this, '_intermediates', { value: {} });
      for (const path of Object.keys(ctor._intermediate_paths).sort()) {
        const [obj, last] = util.getLeafOfPath(this, path);
        this._intermediates[path] = {};
        this._defineProperty(obj, last, path, false);
      }
      // tslint:disable-next-line:forin
      for (const column in schema) {
        const property = schema[column];
        if (property.primary_key) {
          continue;
        }
        const [obj, last] = util.getLeafOfPath(this, property._parts);
        this._defineProperty(obj, last, column, false);
      }
    } else {
      Object.defineProperty(this, 'isDirty', { value: _pf_isDirty });
      Object.defineProperty(this, 'getChanged', { value: _pf_getChanged });
      Object.defineProperty(this, 'get', { value: _pf_get });
      Object.defineProperty(this, 'getPrevious', { value: _pf_getPrevious });
      Object.defineProperty(this, 'set', { value: _pf_set });
      Object.defineProperty(this, 'reset', { value: _pf_reset });
    }

    if (ctor._object_column_classes) {
      for (const { column, klass } of ctor._object_column_classes) {
        (this as any)[column] = new klass();
      }
    }

    if (arguments.length === 4) {
      // if this has 4 arguments, this is called from adapter with database record data
      const id = arguments[1];
      const selected_columns = arguments[2];
      const selected_columns_raw = arguments[3];
      adapter.setValuesFromDB(this, data, schema, selected_columns);
      ctor._collapseNestedNulls(this, selected_columns_raw, ctor.dirty_tracking ? this._intermediates : undefined);
      Object.defineProperty(this, 'id', {
        configurable: false,
        enumerable: true,
        value: id,
        writable: false,
      });
      Object.defineProperty(this, '_is_persisted', {
        configurable: false,
        enumerable: false,
        value: true,
        writable: false,
      });
      this._runCallbacks('find', 'after');
    } else {
      // tslint:disable-next-line:forin
      for (const column in schema) {
        const property = schema[column];
        if (property.primary_key) {
          continue;
        }
        const parts = property._parts;
        let value = util.getPropertyOfPath(data, parts);
        if (value === undefined) {
          value = null;
        }
        util.setPropertyOfPath(this, parts, value);
      }
      ctor._collapseNestedNulls(this, null, ctor.dirty_tracking ? this._intermediates : undefined);
      Object.defineProperty(this, 'id', {
        configurable: true,
        enumerable: true,
        value: null,
        writable: false,
      });
    }
    this._runCallbacks('initialize', 'after');
  }
(defined in /src/model/index.ts:114)
public static _adapter: AdapterBase;

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:351)
public static _collapseNestedNulls(instance: any, selected_columns_raw: any, intermediates: any) {
    for (const path of Object.keys(this._intermediate_paths)) {
      if (selected_columns_raw && selected_columns_raw.indexOf(path) === -1) {
        continue;
      }
      let obj;
      let last: any;
      if (intermediates) {
        obj = intermediates;
        last = path;
      } else {
        [obj, last] = util.getLeafOfPath(instance, path);
      }
      let has_non_null = false;
      // tslint:disable-next-line:forin
      for (const key in obj[last]) {
        const value = obj[last][key];
        if (value != null) {
          has_non_null = true;
        }
      }
      if (!has_non_null) {
        obj[last] = null;
      }
    }
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:782)
public static _completeSchema() {
    for (const index of this._indexes) {
      if (!index.options.name) {
        const column_names = Object.keys(index.columns).map((column_name) => {
          return this._schema[column_name] && this._schema[column_name]._dbname_us || column_name;
        });
        index.options.name = column_names.join('_');
      }
    }
  }
(defined in /src/model/index.ts:109)
public static _connection: Connection;
(defined in /src/model/index.ts:123)
public static _indexes: IIndexProperty[];
(defined in /src/model/index.ts:881)
public _transaction?: Transaction;

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:805)
private static addCallback(type: ModelCallbackType, name: ModelCallbackName, method: ModelCallbackMethod) {
    this._checkConnection();
    if (!(type === 'before' || type === 'after') || !name) {
      return;
    }
    const callbacks_map = this._callbacks_map || (this._callbacks_map = {});
    const callbacks = callbacks_map[name] || (callbacks_map[name] = []);
    return callbacks.push({ type, method });
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:719)
public static addValidator(validator: any) {
    this._checkConnection();
    this._validators.push(validator);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:461)
public static afterCreate(method: ModelCallbackMethod) {
    this.addCallback('after', 'create', method);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:489)
public static afterDestroy(method: ModelCallbackMethod) {
    this.addCallback('after', 'destroy', method);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:433)
public static afterFind(method: ModelCallbackMethod) {
    this.addCallback('after', 'find', method);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:426)
public static afterInitialize(method: ModelCallbackMethod) {
    this.addCallback('after', 'initialize', method);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:447)
public static afterSave(method: ModelCallbackMethod) {
    this.addCallback('after', 'save', method);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:475)
public static afterUpdate(method: ModelCallbackMethod) {
    this.addCallback('after', 'update', method);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:503)
public static afterValidate(method: ModelCallbackMethod) {
    this.addCallback('after', 'validate', method);
  }
(defined in /src/model/index.ts:97)
public static archive = false;

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:454)
public static beforeCreate(method: ModelCallbackMethod) {
    this.addCallback('before', 'create', method);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:482)
public static beforeDestroy(method: ModelCallbackMethod) {
    this.addCallback('before', 'destroy', method);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:440)
public static beforeSave(method: ModelCallbackMethod) {
    this.addCallback('before', 'save', method);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:468)
public static beforeUpdate(method: ModelCallbackMethod) {
    this.addCallback('before', 'update', method);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:496)
public static beforeValidate(method: ModelCallbackMethod) {
    this.addCallback('before', 'validate', method);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:328)
public static belongsTo(target_model_or_column: string | typeof BaseModel, options?: IAssociationBelongsToOptions) {
    this._checkConnection();
    this._connection.addAssociation({ type: 'belongsTo', this_model: this, target_model_or_column, options });
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:295)
public static build<M extends BaseModel>(
    this: new (data?: any) => M,
    data?: ModelValueObject<M>,
  ): M {
    return new this(data);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:199)
public static column(
    path: string, type_or_property: types.ColumnType | types.ColumnType[] | IColumnProperty | IColumnNestedProperty,
  ): void;

If this methods was not called explicitly, this model will use Connection.defaultConnection

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:157)
public static connection(connection: Connection, name?: string) {
    if (this.hasOwnProperty('_connection')) {
      throw new Error('Model::connection was called twice');
    }
    if (!name) {
      name = this.name;
    }
    connection.models[name] = this;
    connection[name] = this;
    Object.defineProperty(this, '_connection', { value: connection });
    Object.defineProperty(this, '_adapter', { value: connection._adapter });
    Object.defineProperty(this, '_associations', { value: {} });
    Object.defineProperty(this, '_validators', { value: [] });
    Object.defineProperty(this, '_name', { value: name });
    Object.defineProperty(this, '_schema', { value: {} });
    Object.defineProperty(this, '_intermediate_paths', { value: {} });
    Object.defineProperty(this, '_indexes', { value: [] });
    Object.defineProperty(this, '_integrities', { value: [] });
    if (!this.table_name) {
      this.table_name = tableize(name);
    }
    this.column('id', 'recordid');
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:678)
public static async count(condition?: object, options?: { transaction?: Transaction }): Promise<number> {
    return await this.query(options).where(condition).count();
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:511)
public static async create<M extends BaseModel>(
    this: (new (data?: any) => M) & typeof BaseModel,
    data?: ModelValueObject<M>,
    options?: { transaction?: Transaction, skip_log?: boolean },
  ): Promise<M> {
    await this._checkReady();
    return await this.build<M>(data).save(options);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:523)
public static async createBulk<M extends BaseModel>(
    this: (new (data?: any) => M) & typeof BaseModel,
    data?: Array<ModelValueObject<M>>,
    options?: { transaction?: Transaction },
  ): Promise<M[]> {
    await this._checkReady();
    if (!Array.isArray(data)) {
      throw new Error('data is not an array');
    }
    if (data.length === 0) {
      return [];
    }
    const records = data.map((item) => {
      return this.build(item);
    });
    records.forEach((record) => record._applyDefaultValues());
    await Promise.all(records.map((record) => record.validate()));
    for (const record of records) {
      record._runCallbacks('save', 'before');
    }
    for (const record of records) {
      record._runCallbacks('create', 'before');
    }
    try {
      return await this._createBulk(records, options);
    } finally {
      for (const record of records) {
        record._runCallbacks('create', 'after');
      }
      for (const record of records) {
        record._runCallbacks('save', 'after');
      }
    }
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:695)
public static async delete(condition?: object, options?: { transaction?: Transaction }): Promise<number> {
    return await this.query(options).where(condition).delete();
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:305)
public static async deleteAll() {
    await this.delete();
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:1056)
public async destroy() {
    this._runCallbacks('destroy', 'before');
    try {
      if (this.id) {
        const ctor = this.constructor as typeof BaseModel;
        await ctor.delete({ id: this.id });
      }
    } finally {
      this._runCallbacks('destroy', 'after');
    }
  }
(defined in /src/model/index.ts:92)
public static dirty_tracking = false;

Parameters

(None)

Returns

(Nothing)

See Also:

(defined in /src/model/index.ts:281)
public static async drop() {
    try {
      // do not need to apply schema before drop, only waiting connection established
      await this._connection._promise_connection;
      await this._adapter.drop(this._name);
    } finally {
      this._connection._schema_changed = true;
    }
  }

Parameters

(None)

Returns

(Nothing)

Throws:

  • {}
(defined in /src/model/index.ts:572)
public static find<M extends BaseModel>(
    this: (new (data?: any) => M) & typeof BaseModel,
    id: types.RecordID,
    options?: { transaction?: Transaction },
  ): IQuerySingle<M>;

Parameters

(None)

Returns

(Nothing)

Throws:

  • {}
(defined in /src/model/index.ts:594)
public static findPreserve<M extends BaseModel>(
    this: (new (data?: any) => M) & typeof BaseModel,
    ids: types.RecordID[],
    options?: { transaction?: Transaction },
  ): IQueryArray<M> {
    return this.query(options).findPreserve(ids);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:992)
public get(path: any) {
    if (this._intermediates.hasOwnProperty(path)) {
      return this._intermediates[path];
    } else {
      return util.getPropertyOfPath(this._attributes, path);
    }
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:985)
public getChanged() {
    return Object.keys(this._prev_attributes);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:1003)
public getPrevious(path: any) {
    return this._prev_attributes[path];
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:648)
public static group<M extends BaseModel, G extends ModelColumnNamesWithId<M>, F>(
    this: (new (data?: any) => M) & typeof BaseModel,
    group_by: G | G[],
    fields?: F,
    options?: { transaction?: Transaction },
  ): IQueryArray<M, { [field in keyof F]: number } & Pick<M, G>>;

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:312)
public static hasMany(target_model_or_column: string | typeof BaseModel, options?: IAssociationHasManyOptions) {
    this._checkConnection();
    this._connection.addAssociation({ type: 'hasMany', this_model: this, target_model_or_column, options });
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:320)
public static hasOne(target_model_or_column: string | typeof BaseModel, options?: IAssociationHasOneOptions) {
    this._checkConnection();
    this._connection.addAssociation({ type: 'hasOne', this_model: this, target_model_or_column, options });
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:271)
public static index(columns: { [column: string]: 1 | -1 }, options: { name?: string, unique?: boolean } = {}) {
    this._checkConnection();
    this._indexes.push({ columns, options });
    this._connection._schema_changed = true;
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:978)
public isDirty() {
    return Object.keys(this._prev_attributes).length > 0;
  }
(defined in /src/model/index.ts:102)
public static lean_query = false;

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:140)
public static newModel(connection: Connection, name: string, schema: IModelSchema): typeof BaseModel {
    // tslint:disable-next-line:variable-name max-classes-per-file
    const NewModel = class extends BaseModel { };
    NewModel.connection(connection, name);
    // tslint:disable-next-line:forin
    for (const column_name in schema) {
      const property = schema[column_name];
      NewModel.column(column_name, property);
    }
    return NewModel;
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:637)
public static order<M extends BaseModel>(
    this: (new (data?: any) => M) & typeof BaseModel,
    orders: string,
    options?: { transaction?: Transaction },
  ): IQueryArray<M> {
    return this.query(options).order(orders);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:561)
public static query<M extends BaseModel>(
    this: (new (data?: any) => M) & typeof BaseModel,
    options?: { transaction?: Transaction },
  ): IQueryArray<M> {
    return new Query<M>(this).transaction(options && options.transaction);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:1044)
public reset() {
    // tslint:disable-next-line:forin
    for (const path in this._prev_attributes) {
      const value = this._prev_attributes[path];
      this.set(path, value);
    }
    this._prev_attributes = {};
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:1084)
public async save(
    options: { transaction?: Transaction, skip_log?: boolean, validate?: boolean } = {},
  ): Promise<this> {
    const ctor = this.constructor as typeof BaseModel;
    await ctor._checkReady();
    if (!this._is_persisted) {
      this._applyDefaultValues();
    }
    if (options.validate !== false) {
      await this.validate();
      return await this.save({ ...options, validate: false });
    }
    this._runCallbacks('save', 'before');
    if (this._is_persisted) {
      this._runCallbacks('update', 'before');
      try {
        await this._update(options);
      } finally {
        this._runCallbacks('update', 'after');
        this._runCallbacks('save', 'after');
      }
    } else {
      this._runCallbacks('create', 'before');
      try {
        await this._create(options);
      } finally {
        this._runCallbacks('create', 'after');
        this._runCallbacks('save', 'after');
      }
    }
    return this;
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:616)
public static select<M extends BaseModel, K extends ModelColumnNamesWithId<M>>(
    this: (new (data?: any) => M) & typeof BaseModel,
    columns: K[],
    options?: { transaction?: Transaction },
  ): IQueryArray<M, Pick<M, K>>;

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:1010)
public set(path: any, value: any) {
    if (this._intermediates.hasOwnProperty(path)) {
      const obj = this._intermediates[path];
      // tslint:disable-next-line:forin
      for (const k in obj) {
        obj[k] = undefined;
      }
      // tslint:disable-next-line:forin
      for (const k in value) {
        obj[k] = value[k];
      }
    } else {
      const parts = path.split('.');
      const prev_value = util.getPropertyOfPath(this._attributes, parts);
      if (prev_value === value) {
        return;
      }
      if (!this._prev_attributes.hasOwnProperty(path)) {
        this._prev_attributes[path] = prev_value;
      }
      let [obj, last] = util.getLeafOfPath(this, parts);
      this._defineProperty(obj, last, path, true);
      util.setPropertyOfPath(this._attributes, parts, value);
      while (parts.length > 1) {
        parts.pop();
        [obj, last] = util.getLeafOfPath(this, parts);
        this._defineProperty(obj, last, parts.join('.'), true);
      }
    }
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:702)
public static timestamps() {
    this.column('created_at', Date);
    this.column('updated_at', Date);
    this.beforeCreate(function(this: any) {
      const d = new Date();
      this.created_at = this.updated_at = d;
    });
    this.beforeUpdate(function(this: any) {
      const d = new Date();
      this.updated_at = d;
    });
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:685)
public static async update(
    updates: any, condition?: object,
    options?: { transaction?: Transaction },
  ): Promise<number> {
    return await this.query(options).where(condition).update(updates);
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:1120)
public validate() {
    this._runCallbacks('validate', 'before');
    const errors: any[] = [];
    const ctor = this.constructor as typeof BaseModel;
    const schema = ctor._schema;
    // tslint:disable-next-line:forin
    for (const column in schema) {
      const property = schema[column];
      if (property.primary_key) {
        continue;
      }
      try {
        ctor._validateColumn(this, column, property);
      } catch (error) {
        errors.push(error.message);
      }
    }
    ctor._validators.forEach((validator: any) => {
      try {
        const r = validator(this);
        if (r === false) {
          errors.push('validation failed');
        } else if (typeof r === 'string') {
          errors.push(r);
        }
      } catch (e) {
        errors.push(e.message);
      }
    });
    if (errors.length > 0) {
      this._runCallbacks('validate', 'after');
      throw new Error(errors.join(','));
    } else {
      this._runCallbacks('validate', 'after');
    }
  }

Parameters

(None)

Returns

(Nothing)
(defined in /src/model/index.ts:605)
public static where<M extends BaseModel>(
    this: (new (data?: any) => M) & typeof BaseModel,
    condition?: object,
    options?: { transaction?: Transaction },
  ): IQueryArray<M> {
    return this.query(options).where(condition);
  }
Fork me on GitHub