Lifecycle callbacks are functions that are called before or after certain model methods. For example, you might use lifecycle callbacks to automatically compute the value of a fullName attribute before creating or updating a User record.
Sails exposes a handful of lifecycle callbacks by default:
The afterCreate lifecycle callback will only be run on queries that have the fetch meta flag set to true. For more information on using the meta flags, see Waterline Queries.
- beforeCreate: fn(recordToCreate, proceed)
- afterCreate: fn(newlyCreatedRecord, proceed)
beforeCreateis also run on bulk inserts of data when you call.createEach(). However,afterCreateis not.
The afterUpdate lifecycle callback will only be run on .update() queries that have the fetch meta flag set to true. For more information on using the meta flags, see Waterline Queries.
- beforeUpdate: fn(valuesToSet, proceed)
- afterUpdate: fn(updatedRecord, proceed)
The afterDestroy lifecycle callback will only be run on .destroy() queries that have the fetch meta flag set to true. For more information on using the meta flags, see Waterline Queries.
- beforeDestroy: fn(criteria, proceed)
- afterDestroy: fn(destroyedRecord, proceed)
If you want to hash a password before saving in the database, you might use the beforeCreate lifecycle callback.
// User.js
module.exports = {
attributes: {
username: {
type: 'string',
required: true
},
password: {
type: 'string',
minLength: 6,
required: true
}
},
beforeCreate: function (valuesToSet, proceed) {
// Hash password
sails.helpers.passwords.hashPassword(valuesToSet.password).exec((err, hashedPassword)=>{
if (err) { return proceed(err); }
valuesToSet.password = hashedPassword;
return proceed();
});//_∏_
}
};If you want to delete child objects when deleting its parent, you can use the beforeDestroy lifecycle callback. By using beforeDestory lifecycle, it will delete the child objects before the parent is deleted. You can stimulate a cascade on delete with this. Be careful when using this, as you may lose sensitive data! In this example we will have 2 Models. Invoice and Item. Invoice to Item corresponds to One-to-many association relation. See One-to-many associations.
// Invoice.js
module.exports = {
attributes: {
client_name: { type: 'string', required: true },
client_email: { type: 'string', required: false },
project_name: { type: 'string', required: true },
project_description: { type: 'string', required: false },
invoice_status: { type: 'string', defaultsTo: 'unpaid' },
due_date: { type: 'string', required: false },
total_price: { type: 'number', required: true},
items: {
collection: 'item',
via: 'invoice'
}
},
async beforeDestroy(criteria, proceed) {
try {
// destroy items related to parent
// we can access the deleted parent object using
// criteria object
await Item.destroy({invoice: criteria.where.id});
} catch (error) {
return proceed(error);
}
return proceed();
}
};
// Item.js
module.exports = {
attributes: {
name: { type: 'string', required: true },
price: { type: 'number', columnType: 'float', required: false },
invoice: {
model: 'invoice'
}
},
};
// Delete Invoice Action
deleteInvoice: async(req, res) => {
let invoice;
try {
//invoice_id is parameter send by client
invoice = await Invoice.destroy({id: req.param('invoice_id')});
} catch (error) {
console.log(error);
return false;
}
return res.status(202).json({message: 'invoice deleted'});
},