-
Notifications
You must be signed in to change notification settings - Fork 642
Description
There are some issues with way the hooks currently work (#71, #81 #94, #112).
The hooks are somewhat counter intuitive because of the way objection.js works. Objection.js is not a full ORM in the sense that you don't always work with model instances. Sometimes (most of the time) you just write queries that result in models, but before the query you don't actually have the model instances that get modified. For example:
Person
.query()
.patch({a: 10})
.where('b', '<', 100)It would be intuitive to call the $beforeUpdate and $afterUpdate hook for all rows that get updated, but we don't have them and I certainly think we shouldn't fetch them implicitly just so that we can call the hooks for them. In this example objection simply calls the hooks for the patch object {a:10}. This allows you to do common stuff like
$beforeUpdate(options, context) {
this.updatedAt = Date.now();
}and it just works, since all the updated rows now get the new updatedAt value.
Confusion comes from the instance query methods (ones started with $query()). For example:
somePerson
.$query()
.patch({a: 10})Now it would be intuitive that this would point to the model being updated in the hooks, but then the hook would work different from the non-instance query hooks. The $beforeUpdate example above would not work.
Currently there is no way to get the model being updated in the hooks. What I propose is that we pass in the model being updated in the options object like this:
$beforeUpdate(options, context) {
if (options.old) {
// Do stuff with the old instance.
this.fullName = (this.firstName || options.old.firstName) + ' '
+ (this.lastName || options.old.lastName);
}
this.updatedAt = Date.now();
}This way the hooks would work for with both query types.
Here is a list of how the hooks would work in each case. The new stuff (not currently implemented) is marked with NEW.
$beforeUpdate
- arguments (options, context)
thispoints to the update object.NEWfor instance queries ($query()) the old values are passed in throughoptions.old
$afterUpdate
- arguments (options, context)
thispoints to the update object.NEWfor instance queries ($query()) the old values are passed in throughoptions.old
$beforeValidate
- arguments (jsonSchema, json, options)
- This hook is different form all the other in the way that
thisalways points
to an empty model instance.$beforeValidateis called before the model instance
is populated with fields. This is because the the object is validated before it is created.
The object to be validated is passed in as thejsonargument. - jsonSchema can be modified in this hook and it should be returned from the hook.
NEWfor instance queries ($query()) the old values are passed in throughoptions.old
$afterValidate
- arguments (json, options)
- This hook is different form all the other in the way that
thisalways points
to an empty model instance.$afterValidateis called before the model instance
is populated with fields. This is because the the object is validated before it is created.
The validated object is passed in as thejsonargument. NEWfor instance queries ($query()) the old values are passed in throughoptions.old
$beforeInsert
- arguments (context)
thispoints to the model about to be inserted.
$afterInsert
- arguments (context)
*this points to the model that was inserted.
NEW $beforeDelete
- arguments (context)
thispoints to the model being deleted.- Only called for instance queries started with
$query()!. Hooks are called for model instances.
In aquery().delete().where('id', 'foo')query there is no object to call it for unless we first fetch
the objects to be deleted, but that is not something we should do automatically. You can use
$query().delete()instead.
NEW $afterDelete
- arguments (context)
thispoints to the model being deleted.- Only called for instance queries started with
$query()!. Hooks are called for model instances.
In aquery().delete().where('id', 'foo')query there is no object to call it for unless we first fetch
the objects to be deleted, but that is not something we should do automatically. You can use
$query().delete()instead.
$afterGet
- arguments (context)
thispoints to the fetched model instance.- Called for each fetched model.
What do you think? Please add any comments/changes/points you might think of! @purepear @drew-r @getvega @gerhardsletten