{"id":66,"date":"2021-09-04T17:20:49","date_gmt":"2021-09-04T16:20:49","guid":{"rendered":"https:\/\/mrzebra.co.uk\/code\/?p=66"},"modified":"2021-09-04T17:22:16","modified_gmt":"2021-09-04T16:22:16","slug":"laravels-touch-function-is-broken","status":"publish","type":"post","link":"https:\/\/zebra-north.com\/code\/2021\/09\/04\/laravels-touch-function-is-broken\/","title":{"rendered":"Laravel&#8217;s touch() function is broken"},"content":{"rendered":"\n<p>After losing a few hours debugging an obscure and difficult to reproduce issue, I can say that the <code>touch()<\/code> function in Eloquent&#8217;s database model is best avoided.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">What it Should Do<\/h2>\n\n\n\n<p>The operation is simple: <code>Model::touch()<\/code> will update the <code>modified_at<\/code> timestamp on your model.  This might be useful if you want to mark a record as having been changed without actually changing it, for example you might <code>touch()<\/code> a parent record after modifying a child record.<\/p>\n\n\n\n<p>A model can also call <code>touch()<\/code> automatically on another model by setting the <code>$touches<\/code> magic property to an array of model names.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What it Doesn&#8217;t Do<\/h2>\n\n\n\n<p>Calling <code>touch()<\/code> will update the database, which triggers the <code>update()<\/code> callback method on your model.  The problem is that the <code>modified_at<\/code> timestamp has a resolution of one second, calling <code>touch()<\/code> twice in the same second will only call your <code>update()<\/code> callback once.  This will cause a failure if you are expecting the <code>update()<\/code> callback to be triggered each time you call <code>touch()<\/code>.<\/p>\n\n\n\n<p>The code snippet below demonstrates the problem.  You register an <code>update<\/code> event callback in the Model&#8217;s <code>boot()<\/code> function that will perform some action &#8211; perhaps updating another system, audit logging, or anything else.  In your <code>doWork()<\/code> function you trigger the <code>update<\/code> event twice.  Although you trigger the event twice, your event handler may only be called once.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-php\">class Widget extends Model\n{\n    \/**\n     * This is called automatically by\n     * Laravel when the model is created.\n     *\/\n    public function boot()\n    {\n        \/\/ Register an event handler for when the model is\n        \/\/ saved to the database.\n        static::updated(function () {\n            doSomethingImportant();\n        }\n    }\n\n    \/**\n     * Some function...\n     *\/\n    public function doWork()\n    {\n        \/\/ Do some work.\n        \/\/ Trigger the &#039;update&#039; event.\n        $this-&gt;touch(); \/\/ Calls doSomethingImportant().\n\n        \/\/ Do some more work.\n        \/\/ Trigger the &#039;update&#039; event again.\n        $this-&gt;touch(); \/\/ May or may not call doSomethingImportant(),\n                        \/\/ depending on how long the work took.\n    }\n}<\/code><\/pre>\n\n\n\n<p>If more than a second passed between the two calls to <code>$this-&gt;touch()<\/code> then <code>doSomethingImportant()<\/code> will be called twice; if less than a second passed, then it will only be called once because the <code>updated_at<\/code> value will not change due its one second resolution.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Remedy<\/h2>\n\n\n\n<p>Unfortunately there is no fix for this coming in Laravel, so the only solution is to avoid using the <code>touch()<\/code> function and avoid doing anything important in the <code>updated<\/code> event handler.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>After losing a few hours debugging an obscure and difficult to reproduce issue, I can say that the touch() function in Eloquent&#8217;s database model is best avoided.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10],"tags":[11],"class_list":["post-66","post","type-post","status-publish","format-standard","hentry","category-php","tag-laravel"],"_links":{"self":[{"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/posts\/66","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/comments?post=66"}],"version-history":[{"count":2,"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/posts\/66\/revisions"}],"predecessor-version":[{"id":73,"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/posts\/66\/revisions\/73"}],"wp:attachment":[{"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/media?parent=66"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/categories?post=66"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/tags?post=66"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}