{"__v":10,"_id":"55097adead1f0523008ecbd9","category":{"__v":16,"_id":"550974cc368a56170041475c","project":"55070e814bb83b2500ec9404","version":"550974cb368a561700414757","pages":["55097a674c7c3f2300aabf07","55097a8a4c7c3f2300aabf09","55097a92ad1f0523008ecbd4","55097a9faa9bd525001a065c","55097aa92dd6a11900e6e7b4","55097ab2ad1f0523008ecbd6","55097ac74c7c3f2300aabf0b","55097ace2dd6a11900e6e7b7","55097ad5dd77250d007369fa","55097adead1f0523008ecbd9","55097ae72dd6a11900e6e7b9","55097aefdd77250d007369fc","55097af8dd77250d00736a05","55097aff4c7c3f2300aabf0d","55097b07aa9bd525001a0660","55097b11dd77250d00736a07"],"reference":false,"createdAt":"2015-03-18T11:08:27.090Z","from_sync":false,"order":4,"slug":"database-interaction-through-models","title":"Database Interaction Through Models"},"project":"55070e814bb83b2500ec9404","user":"55070d24d30b3f190011b941","version":{"__v":1,"_id":"550974cb368a561700414757","forked_from":"55070e814bb83b2500ec9407","project":"55070e814bb83b2500ec9404","createdAt":"2015-03-18T12:51:23.709Z","releaseDate":"2015-03-18T12:51:23.709Z","categories":["550974cc368a561700414758","550974cc368a561700414759","550974cc368a56170041475a","550974cc368a56170041475b","550974cc368a56170041475c","550974cc368a56170041475d","550974cc368a56170041475e"],"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"1.4.0","version":"1.4"},"updates":["555fec325884610d00bba241"],"createdAt":"2015-03-18T13:17:18.850Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"auth":"required","params":[],"url":""},"order":11,"body":"Callbacks in Wheels allow you to have code executed before and/or after certain operations on an object. This requires some further explanation, so let's go straight to an example of a real-world application: the e-commerce checkout.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"A Real-World Example of Using Callbacks\"\n}\n[/block]\nLet's look at a possible scenario for what happens when a visitor to your imaginary e-commerce website submits their credit card details to finalize an order:\n\n* You create a new `order` object using the [new()](doc:new) method based on the incoming form parameters.\n* You call the [save()](doc:save) method on the order object, which will cause Wheels to first validate the object and then store it in the database if it passes validation.\n* The next day, you call the [update(](doc:update) method on the object because the user decided to change the shipping method for the order.\n* Another day passes, and you call the [delete()](doc:delete) method on the object because the visitor called in to cancel the order.\n\nLet's say you want to have the following things executed somewhere in the code:\n\n* Stripping out dashes from the credit card number to make it as easy as possible for the user to make a purchase.\n* Calculating shipping cost based on the country the package will be sent to.\n\nIt's tempting to put this code right in the controller, isn't it? But if you think ahead a little, you'll realize that you might build an administrative interface for orders and maybe an express checkout as well at some point in the future. You don't want to duplicate all your logic in all these places, do you?\n\nObject callbacks to the rescue! By using object callbacks to implement this sort of logic in your model, you keep it out of your controllers and ensure your code stays DRY (Don't Repeat Yourself).\n\nPart of the `Order.cfc` model file:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<cfcomponent extends=\\\"Model\\\">\\n    <cffunction name=\\\"init\\\">\\n        <cfset beforeValidationOnCreate(\\\"fixCreditCard\\\")>\\n        <cfset afterValidation(\\\"calculateShippingCost\\\")>\\n    </cffunction>\\n\\n    <cffunction name=\\\"fixCreditCard\\\">\\n        Code for stripping out dashes in credit card numbers goes here...\\n    </cffunction>\\n\\n    <cffunction name=\\\"calculateShippingCost\\\">\\n        Code for calculating shipping cost goes here...\\n    </cffunction>\\n\\n</cfcomponent>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nThe above code registers 2 methods to be run at specific points in the life cycle of all objects in your application.\n\n## Use Proper Naming\n\nWhen naming your callbacks you might be tempted to try and keep things (too) simple by doing something like `afterValidation(\"afterValidation\")`.\n\nDo **not** do this.\n\nIf you do, CFWheels will fail silently and you might be left wondering why nothing is happening. (What is happening is that you, if there is a corresponding method named `afterValidation`, unintentionally overrode an internal CFWheels method.)\n\nIt's best to name the methods so they describe what task they actually perform, such as `calculateShippingCost` or `fixCreditCard` as shown in the example above.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Registering and Controlling Callbacks\"\n}\n[/block]\nThe following 16 functions can be used to register callbacks.\n\n* [afterNew()](doc:afternew)  or [afterFind()](doc:afterfind) \n* [afterInitialization()](doc:afterinitialization)\n* [beforeValidation()](doc:beforevalidation)\n* [beforeValidationOnCreate()](doc:beforevalidationoncreate) or [beforeValidationOnUpdate()](doc:beforevalidationonupdate)\n* [afterValidation()](doc:aftervalidation)\n* [afterValidationOnCreate()](doc:aftervalidationoncreate) or [afterValidationOnUpdate()](doc:aftervalidationonupdate)\n* [beforeSave()](doc:beforesave) \n* [beforeCreate()](doc:beforecreate) or [beforeUpdate()](doc:beforeupdate)\n* [afterCreate()](doc:aftercreate) or [afterUpdate()](doc:afterupdate)\n* [afterSave()](doc:aftersave)\n* [beforeDelete()](doc:beforedelete) \n* [afterDelete()](doc:afterdelete)\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Callback Life Cycle\"\n}\n[/block]\nAs you can see above, there are a few places (5, to be exact) where one callback or the other will be executed, but not both.\n\nThe very first possible callback that can take place in an object's life cycle is either [afterNew()](doc:afternew) or [afterFind](doc:afterfind). The [afterNew()](doc:afternew) callback methods are triggered when you create the object yourself for the very first time, for example, when using the [new()](doc:new) method. [afterFind](doc:afterfind) is triggered when the object is created as a result of fetching a record from the database, for example, when using [findByKey()](doc:findbykey). (There is some special behavior for this callback type that we'll explain in detail later on in this chapter).\n\nThe remaining callbacks get executed depending on whether or not we're running a \"create,\" \"update,\" or \"delete\" operation.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Breaking a Callback Chain\"\n}\n[/block]\nIf you want to completely break the callback chain for an object, you can do so by returning `false` from your callback method. (Otherwise, always return `true` or nothing at all.) As an example of breaking the callback chain, let's say you have called the [save()](doc:save) method on a new object and the method you've registered with the [beforeCreate()](doc:beforecreate) callback returns `false`. As a result, because the method you've registered with the [beforeCreate()](doc:beforecreate) callback will exit the callback chain early by returning `false`, no record will be inserted in the database.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Order of Callbacks\"\n}\n[/block]\nSometimes you need to run more than one method at a specific point in the object's life cycle. You can do this by passing in a list of method names like this:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<cfset beforeSave(\\\"checkSomething,checkSomethingElse\\\")>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nWhen an object is saved in your application, these two callbacks will be executed in the order that you registered them. The `checkSomething` method will be executed first, and unless it returns `false`, the `checkSomethingElse` method will be executed directly afterward.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Special Case #1: findAll() and the afterFind() Callback\"\n}\n[/block]\nWhen you read about the [afterFind()](doc:afterfind) callback above, you may have thought that it must surely only work for [findOne()](doc:findone)/ [findByKey()](doc:findbykey) calls but not for [findAll()](doc:findall) because those calls return query result sets by default, not objects.\n\nBelieve it or not, but callbacks are even triggered on [findAll()](doc:findall)! You do need to write your callback code differently though because there will be no `this` scope in the query object. Instead of modifying properties in the `this` scope like you normally would, the properties are passed to the callback method via the `arguments` struct.\n\nDoes that sound complicated? This example should clear it up a little. Let's show some code to display how you can handle setting a `fullName` property on a hypothetical `artist` model.\n\n**NOTE:** Because all [afterFind()](doc:afterfind) callbacks run when fetching records from the database, it's a good idea to check to make sure that the columns used in the method's logic exist before performing any operations. You mostly encounter this issue when using the `select` argument on a finder to limit the number of columns returned. But no worries! You can use `StructKeyExists()` and perform a simple check to make sure that the columns exists in the `arguments` scope.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<cfcomponent extends=\\\"Model\\\" output=\\\"false\\\">\\n\\n    <cffunction name=\\\"init\\\">\\n        <cfset afterFind(\\\"setFullName\\\")>\\n    </cffunction>\\n\\n    <cffunction name=\\\"setFullName\\\">\\n        <cfset arguments.fullName = \\\"\\\">\\n        <cfif\\n            StructKeyExists(arguments, \\\"firstName\\\")\\n            and StructKeyExists(arguments, \\\"lastName\\\")\\n        >\\n            <cfset arguments.fullName = Trim(\\n                arguments.firstName & \\\" \\\" & arguments.lastName\\n            )>\\n        </cfif>\\n        <cfreturn arguments>\\n    </cffunction>\\n\\n</cfcomponent>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nIn our example model, an artist's name can consist of both a first name and a last name (\"John Mayer\") or just the band / last name (\"Abba.\"). The `setFullName()` method handles the logic to concatenate the names.\n\nAlways remember to return the `arguments` struct, otherwise Wheels won't be able to tell that you actually wanted to make any changes to the query.\n\nNote that callbacks set on included models are not executed. Look at this example:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"fooBars = model(\\\"foo\\\").findAll(include=\\\"bars\\\");\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nThat will cause callback to be executed on the `Foo` model but not the `Bar` model.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Special Case # 2: Callbacks and the updateAll() and deleteAll() Methods\"\n}\n[/block]\nPlease note that if you use the [updateAll()](doc:updateall) or the [deleteAll()](doc:deleteall) methods in Wheels, they will not instantiate objects by default, and thus any callbacks will be skipped. This is good for performance reasons because if you update 1,000 records at once, you probably don't want to run the callbacks on each object. Especially not if they involve database calls.\n\nHowever, if you want to execute all callbacks in those methods as well, all you have to do is pass in `instantiate=true` to the [updateAll()](doc:updateall)/ [deleteAll()](doc:deleteall) methods.","excerpt":"Write code that runs every time a given object is created, updated, or deleted.","slug":"object-callbacks","type":"basic","title":"Object Callbacks"}

Object Callbacks

Write code that runs every time a given object is created, updated, or deleted.

Callbacks in Wheels allow you to have code executed before and/or after certain operations on an object. This requires some further explanation, so let's go straight to an example of a real-world application: the e-commerce checkout. [block:api-header] { "type": "basic", "title": "A Real-World Example of Using Callbacks" } [/block] Let's look at a possible scenario for what happens when a visitor to your imaginary e-commerce website submits their credit card details to finalize an order: * You create a new `order` object using the [new()](doc:new) method based on the incoming form parameters. * You call the [save()](doc:save) method on the order object, which will cause Wheels to first validate the object and then store it in the database if it passes validation. * The next day, you call the [update(](doc:update) method on the object because the user decided to change the shipping method for the order. * Another day passes, and you call the [delete()](doc:delete) method on the object because the visitor called in to cancel the order. Let's say you want to have the following things executed somewhere in the code: * Stripping out dashes from the credit card number to make it as easy as possible for the user to make a purchase. * Calculating shipping cost based on the country the package will be sent to. It's tempting to put this code right in the controller, isn't it? But if you think ahead a little, you'll realize that you might build an administrative interface for orders and maybe an express checkout as well at some point in the future. You don't want to duplicate all your logic in all these places, do you? Object callbacks to the rescue! By using object callbacks to implement this sort of logic in your model, you keep it out of your controllers and ensure your code stays DRY (Don't Repeat Yourself). Part of the `Order.cfc` model file: [block:code] { "codes": [ { "code": "<cfcomponent extends=\"Model\">\n <cffunction name=\"init\">\n <cfset beforeValidationOnCreate(\"fixCreditCard\")>\n <cfset afterValidation(\"calculateShippingCost\")>\n </cffunction>\n\n <cffunction name=\"fixCreditCard\">\n Code for stripping out dashes in credit card numbers goes here...\n </cffunction>\n\n <cffunction name=\"calculateShippingCost\">\n Code for calculating shipping cost goes here...\n </cffunction>\n\n</cfcomponent>", "language": "text" } ] } [/block] The above code registers 2 methods to be run at specific points in the life cycle of all objects in your application. ## Use Proper Naming When naming your callbacks you might be tempted to try and keep things (too) simple by doing something like `afterValidation("afterValidation")`. Do **not** do this. If you do, CFWheels will fail silently and you might be left wondering why nothing is happening. (What is happening is that you, if there is a corresponding method named `afterValidation`, unintentionally overrode an internal CFWheels method.) It's best to name the methods so they describe what task they actually perform, such as `calculateShippingCost` or `fixCreditCard` as shown in the example above. [block:api-header] { "type": "basic", "title": "Registering and Controlling Callbacks" } [/block] The following 16 functions can be used to register callbacks. * [afterNew()](doc:afternew) or [afterFind()](doc:afterfind) * [afterInitialization()](doc:afterinitialization) * [beforeValidation()](doc:beforevalidation) * [beforeValidationOnCreate()](doc:beforevalidationoncreate) or [beforeValidationOnUpdate()](doc:beforevalidationonupdate) * [afterValidation()](doc:aftervalidation) * [afterValidationOnCreate()](doc:aftervalidationoncreate) or [afterValidationOnUpdate()](doc:aftervalidationonupdate) * [beforeSave()](doc:beforesave) * [beforeCreate()](doc:beforecreate) or [beforeUpdate()](doc:beforeupdate) * [afterCreate()](doc:aftercreate) or [afterUpdate()](doc:afterupdate) * [afterSave()](doc:aftersave) * [beforeDelete()](doc:beforedelete) * [afterDelete()](doc:afterdelete) [block:api-header] { "type": "basic", "title": "Callback Life Cycle" } [/block] As you can see above, there are a few places (5, to be exact) where one callback or the other will be executed, but not both. The very first possible callback that can take place in an object's life cycle is either [afterNew()](doc:afternew) or [afterFind](doc:afterfind). The [afterNew()](doc:afternew) callback methods are triggered when you create the object yourself for the very first time, for example, when using the [new()](doc:new) method. [afterFind](doc:afterfind) is triggered when the object is created as a result of fetching a record from the database, for example, when using [findByKey()](doc:findbykey). (There is some special behavior for this callback type that we'll explain in detail later on in this chapter). The remaining callbacks get executed depending on whether or not we're running a "create," "update," or "delete" operation. [block:api-header] { "type": "basic", "title": "Breaking a Callback Chain" } [/block] If you want to completely break the callback chain for an object, you can do so by returning `false` from your callback method. (Otherwise, always return `true` or nothing at all.) As an example of breaking the callback chain, let's say you have called the [save()](doc:save) method on a new object and the method you've registered with the [beforeCreate()](doc:beforecreate) callback returns `false`. As a result, because the method you've registered with the [beforeCreate()](doc:beforecreate) callback will exit the callback chain early by returning `false`, no record will be inserted in the database. [block:api-header] { "type": "basic", "title": "Order of Callbacks" } [/block] Sometimes you need to run more than one method at a specific point in the object's life cycle. You can do this by passing in a list of method names like this: [block:code] { "codes": [ { "code": "<cfset beforeSave(\"checkSomething,checkSomethingElse\")>", "language": "text" } ] } [/block] When an object is saved in your application, these two callbacks will be executed in the order that you registered them. The `checkSomething` method will be executed first, and unless it returns `false`, the `checkSomethingElse` method will be executed directly afterward. [block:api-header] { "type": "basic", "title": "Special Case #1: findAll() and the afterFind() Callback" } [/block] When you read about the [afterFind()](doc:afterfind) callback above, you may have thought that it must surely only work for [findOne()](doc:findone)/ [findByKey()](doc:findbykey) calls but not for [findAll()](doc:findall) because those calls return query result sets by default, not objects. Believe it or not, but callbacks are even triggered on [findAll()](doc:findall)! You do need to write your callback code differently though because there will be no `this` scope in the query object. Instead of modifying properties in the `this` scope like you normally would, the properties are passed to the callback method via the `arguments` struct. Does that sound complicated? This example should clear it up a little. Let's show some code to display how you can handle setting a `fullName` property on a hypothetical `artist` model. **NOTE:** Because all [afterFind()](doc:afterfind) callbacks run when fetching records from the database, it's a good idea to check to make sure that the columns used in the method's logic exist before performing any operations. You mostly encounter this issue when using the `select` argument on a finder to limit the number of columns returned. But no worries! You can use `StructKeyExists()` and perform a simple check to make sure that the columns exists in the `arguments` scope. [block:code] { "codes": [ { "code": "<cfcomponent extends=\"Model\" output=\"false\">\n\n <cffunction name=\"init\">\n <cfset afterFind(\"setFullName\")>\n </cffunction>\n\n <cffunction name=\"setFullName\">\n <cfset arguments.fullName = \"\">\n <cfif\n StructKeyExists(arguments, \"firstName\")\n and StructKeyExists(arguments, \"lastName\")\n >\n <cfset arguments.fullName = Trim(\n arguments.firstName & \" \" & arguments.lastName\n )>\n </cfif>\n <cfreturn arguments>\n </cffunction>\n\n</cfcomponent>", "language": "text" } ] } [/block] In our example model, an artist's name can consist of both a first name and a last name ("John Mayer") or just the band / last name ("Abba."). The `setFullName()` method handles the logic to concatenate the names. Always remember to return the `arguments` struct, otherwise Wheels won't be able to tell that you actually wanted to make any changes to the query. Note that callbacks set on included models are not executed. Look at this example: [block:code] { "codes": [ { "code": "fooBars = model(\"foo\").findAll(include=\"bars\");", "language": "text" } ] } [/block] That will cause callback to be executed on the `Foo` model but not the `Bar` model. [block:api-header] { "type": "basic", "title": "Special Case # 2: Callbacks and the updateAll() and deleteAll() Methods" } [/block] Please note that if you use the [updateAll()](doc:updateall) or the [deleteAll()](doc:deleteall) methods in Wheels, they will not instantiate objects by default, and thus any callbacks will be skipped. This is good for performance reasons because if you update 1,000 records at once, you probably don't want to run the callbacks on each object. Especially not if they involve database calls. However, if you want to execute all callbacks in those methods as well, all you have to do is pass in `instantiate=true` to the [updateAll()](doc:updateall)/ [deleteAll()](doc:deleteall) methods.