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 Zakir Hyder

This entry was written by .

01. March 2012 by Zakir Hyder
Categories: CakePHP, PHP, Web Development | Tags: , , , , , , , | Comments