{"id":15,"date":"2017-11-22T15:41:39","date_gmt":"2017-11-22T17:41:39","guid":{"rendered":"http:\/\/www.fabianocouto.com.br\/blog\/?p=15"},"modified":"2017-11-23T13:29:10","modified_gmt":"2017-11-23T15:29:10","slug":"laravel-exclusao-em-cascata-usando-soft-delete-cascade-delete","status":"publish","type":"post","link":"https:\/\/www.fabianocouto.com.br\/blog\/laravel-exclusao-em-cascata-usando-soft-delete-cascade-delete\/","title":{"rendered":"Laravel: Exclus\u00e3o em cascata usando soft delete (Cascade Delete)"},"content":{"rendered":"<p>Um dos recursos mais interessantes do <strong>Laravel<\/strong> \u00e9 a possibilidade de implementa\u00e7\u00e3o de &#8220;<strong>soft delete<\/strong>&#8221; com extrema facilidade nos sistemas, s\u00f3 que com isso surgem alguns problemas.<\/p>\n<p>Para manter a integridade dos relacionamentos na base de dados, a utiliza\u00e7\u00e3o de &#8220;foreign keys&#8221; n\u00e3o \u00e9 suficiente quando se usa &#8220;sof delete&#8221;, e seria necess\u00e1rio a cria\u00e7\u00e3o de in\u00fameras &#8220;triggers&#8221; para executar as a\u00e7\u00f5es em cascata originadas pela &#8220;exclus\u00e3o&#8221; de um registro, &#8220;exclus\u00e3o&#8221; entre aspas porque usando &#8220;soft delete&#8221; nenhum registro \u00e9 exclu\u00eddo fisicamente. Mas mesmo implementando o &#8220;soft delete&#8221; por padr\u00e3o no sistema, haver\u00e3o casos de tabelas que n\u00e3o ser\u00e1 necess\u00e1rio ficar mantendo um hist\u00f3rico de registros, e que uma exclus\u00e3o f\u00edsica seria recomendado para n\u00e3o ficar sobrecarregando o banco de dados com registros desnecess\u00e1rios, principalmente se uma rotina de backups \u00e9 executada.<\/p>\n<p>Foi ent\u00e3o que ap\u00f3s pesquisar bastante, e ver inumeras sugest\u00f5es, como reescrever fun\u00e7\u00f5es nativas do framework e tantas outras absurdas, que resolvi criar minha pr\u00f3pria solu\u00e7\u00e3o.<\/p>\n<p>Vamos l\u00e1.<\/p>\n<p>Todos os modelos em um projeto padr\u00e3o utilizando Laravel estendem do ORM <strong>Eloquent<\/strong>, ent\u00e3o a primeira coisa \u00e9 criar um modelo base, de onde todos os outros modelos do sistema passar\u00e3o a estender.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\">&lt;?php\r\n\r\nnamespace App;\r\n\r\nuse Eloquent;\r\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\r\n\r\n\/**\r\n * Class Model\r\n * @package App\r\n *\/\r\nclass Model extends Eloquent\r\n{\r\n    use SoftDeletes;\r\n\r\n    \/**\r\n     * Enable Table Timestamps\r\n     *\r\n     * @var bool\r\n     *\/\r\n    public $timestamps = true;\r\n\r\n    \/**\r\n     * Execute Actions On Boot\r\n     *\r\n     * @return void\r\n     *\/\r\n    public static function boot()\r\n    {\r\n    \tparent::boot();\r\n\r\n    \tstatic::deleting(function($model) {\r\n\r\n            \/\/ CASCADE SOFT DELETE\r\n\r\n            if (isset($model-&gt;cascadeDelete)) {\r\n\r\n                collect($model-&gt;cascadeDelete)-&gt;each(function ($class, $method) use ($model) {\r\n\r\n                    if (method_exists($model, $method)) {\r\n\r\n                        collect($model-&gt;$method($class)-&gt;get())-&gt;each(function ($model) {\r\n                            $model-&gt;delete();\r\n                        });\r\n\r\n                        $model-&gt;$method($class)-&gt;delete();\r\n                    }\r\n                });\r\n            }\r\n\r\n            \/\/ CASCADE PERMANENTLY DELETE\r\n\r\n            if (isset($model-&gt;cascadeForceDelete)) {\r\n\r\n                collect($model-&gt;cascadeForceDelete)-&gt;each(function ($class, $method) use ($model) {\r\n\r\n                    if (method_exists($model, $method)) {\r\n\r\n                        collect($model-&gt;$method($class)-&gt;get())-&gt;each(function ($model) {\r\n                            $model-&gt;forceDelete();\r\n                        });\r\n\r\n                        $model-&gt;$method($class)-&gt;forceDelete();\r\n                    }\r\n                });\r\n            }\r\n        });\r\n    }\r\n}\r\n\r\n?&gt;<\/pre>\n<p>Agora, todo modelo que estender de &#8220;Model&#8221;, vai usar &#8220;soft delete&#8221; por padr\u00e3o, e para que uma exclus\u00e3o em cascata ocorra, ou seja, todos os relacionamentos que por ventura devam ser exclu\u00eddos tamb\u00e9m sejam, voc\u00ea precisa definir a lista de m\u00e9todos e classes que devem ser exclu\u00eddos.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\">&lt;?php\r\n\r\nnamespace App;\r\n\r\nuse App\\Model;\r\n\r\n\/**\r\n * Class Example\r\n * @package App\r\n *\/\r\nclass Example extends Model\r\n{\r\n    \/**\r\n     * Table name\r\n     *\r\n     * @var string\r\n     *\/\r\n    public $table = 'example';\r\n\r\n    \/**\r\n     * Relations that should be soft deleted\r\n     *\r\n     * @var array\r\n     *\/\r\n    public $cascadeDelete = [\r\n        'hasMany' =&gt; \\App\\Relation::class,\r\n    ];\r\n\r\n    \/**\r\n     * Relations that should be permanently deleted\r\n     *\r\n     * @var array\r\n     *\/\r\n    public $cascadeForceDelete = [\r\n        'hasMany' =&gt; \\App\\Relation::class,\r\n    ];\r\n\r\n    \/**\r\n     * @return \\Illuminate\\Database\\Eloquent\\Relations\\HasMany\r\n     **\/\r\n    public function Relations()\r\n    {\r\n        return $this-&gt;hasMany(\\App\\Relation::class);\r\n    }\r\n}\r\n\r\n?&gt;<\/pre>\n<p>Essa solu\u00e7\u00e3o tem funcionado muito bem at\u00e9 ent\u00e3o, e tem sido adotada como padr\u00e3o em todos os sistemas que desenvolvo usando Laravel.<\/p>\n<p>Espero ter ajudado.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Um dos recursos mais interessantes do Laravel \u00e9 a possibilidade de implementa\u00e7\u00e3o de &#8220;soft delete&#8221; com extrema facilidade nos sistemas, s\u00f3 que com isso surgem alguns problemas.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1,2],"tags":[5,3,9],"_links":{"self":[{"href":"https:\/\/www.fabianocouto.com.br\/blog\/wp-json\/wp\/v2\/posts\/15"}],"collection":[{"href":"https:\/\/www.fabianocouto.com.br\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.fabianocouto.com.br\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.fabianocouto.com.br\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fabianocouto.com.br\/blog\/wp-json\/wp\/v2\/comments?post=15"}],"version-history":[{"count":8,"href":"https:\/\/www.fabianocouto.com.br\/blog\/wp-json\/wp\/v2\/posts\/15\/revisions"}],"predecessor-version":[{"id":43,"href":"https:\/\/www.fabianocouto.com.br\/blog\/wp-json\/wp\/v2\/posts\/15\/revisions\/43"}],"wp:attachment":[{"href":"https:\/\/www.fabianocouto.com.br\/blog\/wp-json\/wp\/v2\/media?parent=15"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fabianocouto.com.br\/blog\/wp-json\/wp\/v2\/categories?post=15"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fabianocouto.com.br\/blog\/wp-json\/wp\/v2\/tags?post=15"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}