I www.jamesbarnsley.com was tasked to find a way to do recursive deletion in Yii. As I asked other developers and looked around the internet the usual answers all came up, use a plugin or use cascade delete at the DB level, but I did not want to do either of these.
So I came up with my own solution which I found to be simple and effective. It all starts off by adding a function to your global model, if you do not have one of these then create one. Create a model that extends active record and then make sure your other models extend the global model. This creates a nice place to put global model functions which can be used by any model in your application.
Add this function to the global model ...
public function deleteRecursive($relations = array())
{
foreach($relations as $relation)
{
if(is_array($this->$relation))
{
foreach($this->$relation as $relation_item)
{
$relation_item->deleteRecursive();
}
}
else
{
$this->$relation->deleteRecursive();
}
}
$this->delete();
}
Now setup the rest of your models so you can recursively delete them by adding this function to your models, make sure you replace the "tags" and "comments" etc, to the names of the relations you want to delete, the relations must be setup using the active record relations etc. As stated earlier if you set those "child models" (tags, comments) up correctly as well, they will also run their recursive function and on and on it goes deleting all child relations of those models as well ...
public function deleteRecursive()
{
parent::deleteRecursive(array("tags", "comments"));
}
If you come across a model that has no child relations, then it will still delete the model with no child relations because deleteRecursive() is defined in the parent. Basically it will call the function with no relations, so it will just delete the model with no child relations and nothing else.
That should handle recursive deletion in your Yii app, give it a try.
There are other solutions
There is one drawback for this method: if a child row cannot be deleted, the process won't halt and may crash when the parent is to be deleted. Instead a creating a new inheritance layer in AR, I'd rather overload the
beforeDelete()
method. As an added bonus, this event method is called automatically, so every AR deletion will then be recursive.And don't forget that, if your DB engine supports it, the easiest solution is to define "ON DELETE CASCADE" actions on your foreign keys.
another thing...
...is that you use deleteByPk instead of $this->delete() so you totally ignore all beforeDelete and afterDelete handlers of related records... this way your solution has no advantages over cascading deletions in database which in fact is much safer as Francois already noticed.
Changed deleteByPK to delete
I have changed deleteByPK to delete.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.