afterSave: A Great Callback Method for Soft Deleting Related Model Data( hasOne, hasMany, belongsTo)
Update: Here is model file to download Download Code
I never like to “Hard Delete” data. Some one will make mistake deleting something. Then they will tell you retrive it. So “Soft Delete” can save you time telling you clients/boss why the data can not be retrieved. BTW “Soft Delete” means you keep a field in table name is_delete/delete. Setting it’s value 1(or any value you may use but using boolean 1 seems logical) means the item is deleted. “Hard Delete” means you delete the row from the table. Poof gone. When i say delete on this post means “Soft Delete”.
Now lest discuss the power of afterSave. Suppose you Relational database for products. You have categories, sub-categories and sub-categories. Their relation looks like this
Category hasMany SubCategories
SubCategory hasMany SubSubCategories
SubSubCategory hasMany Products
Category -> SubCategories -> SubSubCategories -> Products
As CakePHP naming convention your Category, SubCategory, SubSubCategory Model should have these fields
CREATE TABLE `categories ` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(550) NOT NULL, `is_delete` tinyint(1) NOT NULL DEFAULT '0', `created` datetime DEFAULT NULL, `modified` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB ; CREATE TABLE `sub_categories` ( `id` int(11) NOT NULL AUTO_INCREMENT, `category_id` int(11) NOT NULL, `name` varchar(550) NOT NULL, `is_delete` tinyint(1) NOT NULL DEFAULT '0', `created` datetime DEFAULT NULL, `modified` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB; CREATE TABLE `sub_sub_categories` ( `id` int(11) NOT NULL AUTO_INCREMENT, `sub_category_id` int(11) NOT NULL, `name` varchar(550) NOT NULL, `is_delete` tinyint(1) NOT NULL DEFAULT '0', `created` datetime DEFAULT NULL, `modified` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB; CREATE TABLE `products` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` int(11) NOT NULL, `sub_sub_category_id` int(11) NOT NULL, `is_delete` tinyint(1) NOT NULL DEFAULT '0', `created` datetime DEFAULT NULL, `modified` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB;
created, modified will be automatically updated by CakePHP. Its nice little feature. So on to our relation between tables. categories table is connected with sub_categories category_id. Its model will look like this
class Catagory extends AppModel {
var $name = 'Catagory';
var $displayField = 'name';
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $hasMany = array(
'SubCatagory' => array(
'className' => 'SubCatagory',
'foreignKey' => 'catagory_id',
'dependent' => false,
)
);
}
Same with sub_categories, sub_sub_categories and products table.
class SubCatagory extends AppModel {
var $name = 'SubCatagory';
var $displayField = 'name';
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'Catagory' => array(
'className' => 'Catagory',
'foreignKey' => 'catagory_id',
)
);
var $hasMany = array(
'SubSubCatagory' => array(
'className' => 'SubSubCatagory',
'foreignKey' => 'sub_catagory_id',
'dependent' => false,
)
);
}
class SubSubCatagory extends AppModel {
var $name = 'SubSubCatagory';
var $displayField = 'name';
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'SubCatagory' => array(
'className' => 'SubCatagory',
'foreignKey' => 'sub_catagory_id',
)
);
var $hasMany = array(
'Product' => array(
'className' => 'Product',
'foreignKey' => 'sub_sub_catagory_id',
'dependent' => false,
)
);
}
class Product extends AppModel {
var $name = 'Product';
var $displayField = 'name';
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'SubSubCatagory' => array(
'className' => 'SubSubCatagory',
'foreignKey' => 'sub_sub_catagory_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
Now we will delete on category – so the row on the categories table will updated with 1. But all the rows on the sub_categories, sub_sub_categories, products for that category will not be deleted. Now comes CakePHP to rescue. It is actually quite beautiful. Let’s add afterSave() function to Category model -category.php
function afterSave()
{
if($this->data['Catagory']['is_delete']==1)
{
$this->SubCatagory->recursive=0;
$sub_catagories = $this->SubCatagory->find('all', array(
'conditions' => array('SubCatagory.catagory_id'=>$this->data['Catagory']['id'])
));
foreach($sub_catagories as $sub_catagory):
unset($this->data['SubCatagory']);
$this->data=$sub_catagory;
$this->data['SubCatagory']['is_delete']=1;
endforeach;
}
}
We first check for delete. The we found out all the sub_catagories. Then we delete it. But what about the sub_sub_categories and products. First delete the sub_sub_categories.
function afterSave()
{
if($this->data['SubCatagory']['is_delete']==1 )
{
$this->SubSubCatagory->recursive=0;
$sub_sub_catagories = $this->SubSubCatagory->find('all', array(
'conditions' => array('SubCatagory.catagory_id'=>$this->data['Catagory']['id'],'SubSubCatagory.sub_catagory_id'=>$this->data['SubCatagory']['id'])
));
foreach($sub_sub_catagories as $sub_sub_catagory):
unset($this->data['SubSubCatagory']);
$this->data=$sub_sub_catagory;
$this->data['SubSubCatagory']['is_delete']=1;
$this->SubSubCatagory->save($this->data);
endforeach;
}
}
Wow all the sub_sub_catagories are deleted. Now delete all the products
function afterSave($created)
{
if($this->data['SubSubCatagory']['is_delete']==1 or $this->data['SubSubCatagory']['is_hide']==1)
{
$this->Product->recursive=0;
$products = $this->Product->find('all', array(
'conditions' => array('Product.catagory_id'=>$this->data['SubCatagory']['catagory_id'], 'Product.sub_catagory_id'=>$this->data['SubCatagory']['id'], 'Product.sub_sub_catagory_id'=>$this->data['SubSubCatagory']['id'])
));
foreach($products as $product):
unset($this->data['Product']);
$this->data=$product;
$this->data['Product']['is_delete']=1;
$this->Product->save($this->data);
endforeach;
}
}
Now all the records have been deleted. Now you can ask why i did not add the codes on the Category model because then deleting any sub_categories entry will not delete sub_sub_categories and products.
About Jakir Hayder
This entry was written by The NewComer.
01. March 2012 by Jakir Hayder
Categories: CakePHP, PHP, Web Development |
Tags: afterSave, belongsTo, cakephp, Callback Method, hasMany, hasOne, Related Model Data, Soft Delete |