やりたいこと
Sequelizeのtimestamps機能をObjection.jsで実現する。
- insert時に、createdAtとupdatedAtカラムに現在日時を設定する。
- update時に、updatedAtカラムに現在日時を設定する。
前提
DBはPostgreSQL、Objection.jsはv1.2.2を使用しています。
方法1. カラムにデフォルト値を設定する
Knex.jsのマイグレーションファイルで、カラムに対してデフォルト値を設定します。
exports.up = function(knex, Promise) {
return knex.schema.createTable('tasks', t => {
t.increments('id').primary();
t.string('name').notNullable();
t.string('description').nullable();
t.integer('assignedTo').nullable();
t.integer('projectId').notNullable();
t.foreign('assignedTo').references('id').inTable('users');
t.foreign('projectId').references('id').inTable('projects');
t.datetime('deadline').nullable();
// デフォルト値を設定
t.timestamp('createdAt').defaultTo(knex.fn.now());
t.timestamp('updatedAt').defaultTo(knex.fn.now());
});
};
exports.down = function(knex, Promise) {
return knex.schema.dropTable('tasks');
};
ただし、この方法だと、
insert時はSequelizeのようにcreatedAtとupdatedAtの両方が更新されますが、
update時にupdatedAtが自動更新されない問題が発生してしまいます。
const createdTask = await Task.query()
.insertAndFetch({
assignedTo: 1,
projectId: 1,
name: 'hoge'
});
// insert時は、createdAtとupdatedAtが自動で設定される。
console.log(createdTask.createdAt != null); // true
console.log(createdTask.updatedAt != null); // true
const updatedTask = await Task.query()
.patchAndFetchById(createdTask.id, {
name: 'fuga'
});
console.log(updatedTask.name === 'fuga'); // true ・・・ nameは更新されている
console.log(updatedTask.updatedAt.valueOf() === createdTask.updatedAt.valueOf()); // true ・・・ 更新されていない
方法2. hookメソッドを利用する
Objection.jsのModelクラスには、hookメソッドが定義されていて、
サブクラスでオーバーライドすることで、クエリ実行時の挙動をカスタマイズできます。
レコードのinsert前は$beforeInsert
、update前は$beforeUpdate
が実行されるので、
以下のように処理することで、方法1の問題を解決することができます。
-
$beforeInsert
でcreatedAtとupdatedAtを更新 -
$beforeUpdate
でupdatedAtを更新
const { Model } = require('objection');
class Task extends Model {
static get tableName() { return 'tasks'; }
$beforeInsert() {
const now = new Date().toISOString();
this.createdAt = now;
this.updatedAt = now;
}
$beforeUpdate() {
const now = new Date().toISOString();
this.updatedAt = now;
}
}
参考