tags:

views:

280

answers:

2

My table structure:

boxes (id, boxname)
boxes_items (id, box_id, item_id)

I was looking at the SQL logs for the "delete box" action, and am slightly horrified.

SELECT COUNT(*) AS count FROM boxes Box WHERE Box.id = 191
SELECT BoxesItem.id FROM boxes_items BoxesItem WHERE BoxesItem.box_id = 191
SELECT COUNT(*) AS count FROM boxes_items BoxesItem WHERE BoxesItem.id = 1685
DELETE FROM boxes_items WHERE boxes_items.id = 1685
SELECT COUNT(*) AS count FROM boxes_items BoxesItem WHERE BoxesItem.id = 1686
DELETE FROM boxes_items WHERE boxes_items.id = 1686

    -- snip 50 more SELECT & DELETE statements --

SELECT COUNT(*) AS count FROM boxes_items BoxesItem WHERE BoxesItem.id = 1733
DELETE FROM boxes_items WHERE boxes_items.id = 1733

DELETE FROM boxes WHERE boxes.id = 191

This is perhaps the least efficient way to delete from these tables that I could conceive of. I mean, it could be replaced with this:

DELETE FROM boxes WHERE id = 191
DELETE FROM boxes_items WHERE box_id = 191

Is there any reason Cake does it this way? If not, do you know of any way that I can streamline the procedure without breaking the core libraries?


Here's the relevant bits of code:

// app/controllers/boxes_controller.php    /////////////

public function delete($id = null) {
    if ($this->Box->del($id)) {
        $this->redirect(array('action'=>'index'));
    }
}

// app/models/box.php    ///////////////////////////////

class Boxes extends AppModel {
    var $hasAndBelongsToMany = array(
        'Item'
    );
}

// app/models/app_model.php    /////////////////////////

class AppModel {
    var $actsAs = array('Containable');
    var $recursive = -1;
}
+2  A: 

Unfortunately that's how Cake does it.

You could override the del() method in your model with something like this rough mockup:

function del($id, $cascade = true) {
    if ($cascade) {
        $this->BoxesItem->deleteAll(array('BoxesItem.box_id' => $id));
    }
    return parent::del($id, false);
}
deceze
the call to parent::del(...) at the end of method. What does it do exactly?
smchacko
@smchacko: It invokes the original `del()` method of the parent class, the class the model is inheriting from and who's `del()` it is overriding. Read about Object Inheritance: http://php.net/manual/en/language.oop5.inheritance.php and the `parent` keyword: http://php.net/keyword.parent
deceze
And why should you call the parent::del() again, if you're trying to avoid the inefficient SQL queries that it generates?
rscherer
the parent will find that it has no Items to delete, so it won't call all the delete statements one by one.
nickf
More precisely, we're calling it with `$cascade` set to false, so it won't even bother with the associated model and just execute one delete for the primary model. Seeing Mathew's answer I'd actually go with his though. My method is still useful if you want to have a lot of control over delete actions.
deceze
+3  A: 

If you have a hasMany relationship, which I am assuming is the case you might want to try setting the "exclusive" flag:

http://book.cakephp.org/view/82/hasMany

exclusive: When exclusive is set to true, recursive model deletion does the delete with a deleteAll() call, instead of deleting each entity separately. This greatly improves performance, but may not be ideal for all circumstances.

Mathew Attlee