Skip to content

Commit

Permalink
feat(relations): add support for withTimestamps in belongsToMany
Browse files Browse the repository at this point in the history
now withTimestamps will respect timestamps in pivot table

Closes #84
  • Loading branch information
thetutlage committed Jan 5, 2017
1 parent 0a09111 commit 41dd327
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 1 deletion.
41 changes: 40 additions & 1 deletion src/Lucid/Relations/BelongsToMany.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class BelongsToMany extends Relation {
super(parent, related)
this.pivotPrefix = '_pivot_'
this.pivotItems = []
this.pivotTimestamps = false
this._setUpPivotTable(pivotTable)
this._setUpKeys(primaryKey, relatedPrimaryKey, pivotLocalKey, pivotOtherKey)
this._decorateQueryBuilder()
Expand Down Expand Up @@ -104,6 +105,25 @@ class BelongsToMany extends Relation {
this.relatedQuery.where(`${this.pivotTable}.${this.pivotLocalKey}`, this.parent[this.fromKey])
}

/**
* Returns an object of keys and values of timestamps to be
* set on pivot table. All values/keys are derived from
* the parent model. Also if parent model disables
* timestamps, the withTimestamps function will
* have no effect.
*
* @return {Object}
* @private
*/
_getTimestampsForPivotTable () {
const timestamps = {}
if (this.pivotTimestamps) {
this.parent.setCreateTimestamp(timestamps)
this.parent.setUpdateTimestamp(timestamps)
}
return timestamps
}

/**
* Returns a cloned query with the join statement to be
* used for fetching aggregates or paginate results.
Expand Down Expand Up @@ -383,7 +403,7 @@ class BelongsToMany extends Relation {

const isSaved = yield relatedInstance.save()
if (isSaved) {
yield this.attach([relatedInstance[this.toKey]])
yield this.attach([relatedInstance[this.toKey]], this._getTimestampsForPivotTable())
}
relatedInstance[`${this.pivotPrefix}${this.pivotLocalKey}`] = this.parent[this.fromKey]
relatedInstance[`${this.pivotPrefix}${this.pivotOtherKey}`] = relatedInstance[this.toKey]
Expand Down Expand Up @@ -424,6 +444,14 @@ class BelongsToMany extends Relation {
return this
}

/**
* Updates pivot table with an object of values. Optionally
* you can define the foriegn keys to be updated.
*
* @param {Object} values
* @param {Array} otherKeyValue
* @return {Promise}
*/
updatePivot (values, otherKeyValue) {
if (otherKeyValue && !_.isArray(otherKeyValue)) {
otherKeyValue = [otherKeyValue]
Expand All @@ -440,6 +468,17 @@ class BelongsToMany extends Relation {
return query.update(values)
}

/**
* Makes sure to respect the timestamps on pivot table. Also timestamps fields
* and values are derived by the parent model. Disabling timestamps on parent
* model results in no impact even after using pivotTimestamps.
*/
withTimestamps () {
this.pivotTimestamps = true
this.withPivot(this.parent.constructor.createTimestamp, this.parent.constructor.updateTimestamp)
return this
}

}

module.exports = BelongsToMany
1 change: 1 addition & 0 deletions test/unit/fixtures/relations.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ module.exports = {
table.integer('course_id')
table.boolean('is_enrolled')
table.integer('lessons_done')
table.timestamps()
}),
knex.schema.createTable('authors', function (table) {
table.increments()
Expand Down
70 changes: 70 additions & 0 deletions test/unit/lucid.relations.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const Database = require('../../src/Database')
const chai = require('chai')
const Ioc = require('adonis-fold').Ioc
const expect = chai.expect
const moment = require('moment')
const filesFixtures = require('./fixtures/files')
const relationFixtures = require('./fixtures/relations')
const config = require('./helpers/config')
Expand Down Expand Up @@ -3081,6 +3082,75 @@ describe('Relations', function () {
yield relationFixtures.truncate(Database, 'courses')
yield relationFixtures.truncate(Database, 'course_student')
})

it('should be return timestamps from the pivot table model withTimestamps method is used', function * () {
const savedStudent = yield relationFixtures.createRecords(Database, 'students', {name: 'ricky', id: 29})
const savedCourse = yield relationFixtures.createRecords(Database, 'courses', {title: 'geometry', id: 12})
class Course extends Model {
}
class Student extends Model {
courses () {
return this.belongsToMany(Course).withTimestamps()
}
}

Course.bootIfNotBooted()
const student = yield Student.find(savedStudent[0])
expect(student instanceof Student).to.equal(true)
expect(student.id).to.equal(savedStudent[0])
yield student.courses().attach(savedCourse, {is_enrolled: 1})
const courses = yield student.courses().fetch()
expect(courses.size()).to.equal(1)
expect(courses.isArray()).to.equal(true)
expect(courses.first()._pivot_created_at).to.equal(null)
expect(courses.first()._pivot_updated_at).to.equal(null)
yield relationFixtures.truncate(Database, 'students')
yield relationFixtures.truncate(Database, 'courses')
yield relationFixtures.truncate(Database, 'course_student')
})

it('should set timestamps on the pivot table when withTimestamps is set to true', function * () {
const savedStudent = yield relationFixtures.createRecords(Database, 'students', {name: 'ricky', id: 29})
class Course extends Model {
}
class Student extends Model {
courses () {
return this.belongsToMany(Course).withTimestamps()
}
}

Course.bootIfNotBooted()
const student = yield Student.find(savedStudent[0])
yield student.courses().create({title: 'geometry'})
const courses = yield student.courses().fetch()
expect(moment(courses.first()._pivot_created_at).isValid()).to.equal(true)
expect(moment(courses.first()._pivot_updated_at).isValid()).to.equal(true)
yield relationFixtures.truncate(Database, 'students')
yield relationFixtures.truncate(Database, 'courses')
yield relationFixtures.truncate(Database, 'course_student')
})

it('should work fine when withTimestamps and withPivot is used together', function * () {
const savedStudent = yield relationFixtures.createRecords(Database, 'students', {name: 'ricky', id: 29})
class Course extends Model {
}
class Student extends Model {
courses () {
return this.belongsToMany(Course).withTimestamps().withPivot('is_enrolled')
}
}

Course.bootIfNotBooted()
const student = yield Student.find(savedStudent[0])
yield student.courses().create({title: 'geometry'})
const courses = yield student.courses().fetch()
expect(moment(courses.first()._pivot_created_at).isValid()).to.equal(true)
expect(moment(courses.first()._pivot_updated_at).isValid()).to.equal(true)
expect(moment(courses.first()._pivot_course_id)).to.be.ok
yield relationFixtures.truncate(Database, 'students')
yield relationFixtures.truncate(Database, 'courses')
yield relationFixtures.truncate(Database, 'course_student')
})
})

context('HasManyThrough', function () {
Expand Down

0 comments on commit 41dd327

Please sign in to comment.