persistent-knex-model-mixin.js

'use strict'
const _ = require('lodash')

module.exports = function PersistentKnexModelMixin (Model, knex, inOptions, userOptions) {
  if (typeof Model.convertFromPersistent !== 'function') {
    Model.convertFromPersistent = function (data) {
      return data
    }
  }

  if (typeof Model.convertToPersistent !== 'function') {
    Model.convertToPersistent = function (data) {
      return data
    }
  }

  /**
   * Set data from database.
   *
   * Applies filters for conversion from database format to normal format.
   */
  Model.prototype.setDataPersistent = function (data) {
    this.setData(this._applyFilter(this.constructor.convertFromPersistent, data))
  }

  Model.prototype.getDataPersistent = function () {
    return this._applyFilter(this.constructor.convertToPersistent, this.getData())
  }

  Model.prototype.save = function (options) {
    const myKnex = knexOrTransaction(knex, options)(Model.tableName)
    const data = this.getDataPersistent()
    if (this.id) {
      return myKnex.update(data).where('id', this.id).then()
    } else {
      return myKnex.insert(data).then()
    }
  }

  Model.prototype.destroy = function (options) {
    const myKnex = knexOrTransaction(knex, options)
    return myKnex(Model.tableName).where('id', this.id).del().then()
  }

  Model.fromDatabaseModel = function (data) {
    if (!data) return null
    // If result is an array (return from knex select query), take the first object
    if (Array.isArray(data)) {
      data = data[0]
    }
    const model = new Model()
    model.setDataPersistent(data)
    return model
  }

  Model.fromDataPersistent = function (data) {
    const model = new Model()
    model.setDataPersistent(data)
    return model
  }

  Model.findAll = function * findAll (options) {
    // this returns an array of JSON objects
    const myKnex = knexOrTransaction(knex, options)
    return myKnex.from(Model.tableName).select().then()
  }

  Model.findWhere = function * findWhere (where, options) {
    const myKnex = knexOrTransaction(knex, options)
    return myKnex.from(Model.tableName).select().where(where).then()
  }

  Model.findById = function * findById (id, options) {
    const myKnex = knexOrTransaction(knex, options)
    return myKnex.from(Model.tableName).select().where('id', id).then(fromDatabaseModel)
  }

  Model.findByKey = function findByKey (keyName, value, options) {
    const myKnex = knexOrTransaction(knex, options)
    return myKnex.from(Model.tableName).select().where(keyName, value).then(fromDatabaseModel)
  }

  // Optional: transaction
  // set this to knex transaction if you want this operation to be done inside transaction.
  Model.create = function * create (values, options) {
    const model = Model.fromData(values)
    const modelPersistent = model.getDataPersistent()
    const myKnex = knexOrTransaction(knex, options)
    return myKnex.insert(modelPersistent).into(Model.tableName)
  }

  Model.createExternal = function createExternal (values, options) {
    const model = Model.fromDataExternal(values)
    return Model.create(model.getDataPersistent(), options)
  }

  Model.bulkCreate = function * bulkCreate (records, options) {
    const models = records
      .map(Model.fromData.bind(Model))
      .map(_.method('getDataPersistent'))

    yield bulkCreateInternal(models, options)
  }

  Model.bulkCreateExternal = function * bulkCreateExternal (records, options) {
    const models = records
      .map(Model.fromDataExternal.bind(Model))
      .map(_.method('getDataPersistent'))

    yield bulkCreateInternal(models, options)
  }

  // Helper functions
  function * bulkCreateInternal (models, options) {
    const myKnex = knexOrTransaction(knex, options)
    if (models.length > 0) {
      return myKnex(Model.tableName).insert(models)
    }
  }

  Model.destroy = function (options) {
    const myKnex = knexOrTransaction(knex, options)
    const where = options.where || {id: this.id}
    return myKnex(Model.tableName).where(where).del().then()
  }

  function knexOrTransaction (knex, options) {
    return !options ? knex : (!options.transaction ? knex : options.transaction)
  }

  function fromDatabaseModel (result) {
    return result && Array.isArray(result) && result.length > 0
      ? Model.fromDatabaseModel(result) : null
  }
}