Thursday, June 4, 2009

Auto-Delete Old Data Before Saving A New Record in CakePHP

Thursday, June 4, 2009

I think it happens sometimes, that you need to keep a certain amount of data, and don't want your database table to grow out of limit.

For example, we have a fairly typical Post model and a posts table with more than 100 posts. We only want the latest 100 posts in the table, so we need to delete older posts on every new post.

To accomplish this in Cake way, we need to write a bit of script. So, let's add a trimming method to our Post model (You can also add it to AppModel to make it reusable in other models):

<?php
class Post extends AppModel {

    var $name = 'Post';
 
    function trim($options) {
        $defaults = array('conditions' => null, 'maintain' => null, 'order' => null, 'cascade' => true, 'callbacks' => false, 'notices' => true);
        $options = array_merge($defaults, (array)$options);
        extract($options);
        
        if ($notices) {
            $calledFrom = debug_backtrace();
            if ($calledFrom[1]['function'] != 'beforeSave') {
                trigger_error(
                    __("({$this->alias}::trim()) should only be called by Model::beforeSave()", true),
                    E_USER_WARNING
                );
                return false;
            }
        }
        
        $conditions = (array)$conditions;
        $count = $this->find('count', array_merge(array('recursive' => -1), compact('conditions')));
        
        if ($count <= 0) {
            return false;
        }
        
        if (is_int($maintain) && $maintain > 0) {
            $start = $maintain - 1;
            $last = $count - $start;
            unset($maintain);
            
            if ($start == $last) {
                return false;
            }
        }

        if (isset($start) && isset($last) && $count > $start) {
            if (is_null($order)) {
                $order = array($this->alias . '.' . $this->primaryKey => 'desc');
            }
            $limit = $start . ',' .  $last;
            $ids = Set::extract(
                $this->find('all', array_merge(array(
                    'fields' => "{$this->alias}.{$this->primaryKey}", 'recursive' => -1),
                    compact('conditions', 'order', 'limit')
                )),
                "{n}.{$this->alias}.{$this->primaryKey}"
            );

            $this->deleteAll(array($this->alias . '.' . $this->primaryKey => $ids), $cascade, $callbacks);
        }
    }
}
?>

In theory it's pretty simple.

  1. We count to get total number of recods in our posts table along with conditions (if needed)
  2. We get all the IDs of the records to delete.
  3. And then, we delete them with Model::deleteAll().

Now, let's call it in our Post model beforeSave():

function beforeSave() {
    $this->trim(array(
        'maintain' => 100
    ));
    return true;
}

Here are some options available for the trim() method.

$options has the following possible keys - all of which are optional except for the 'maintain' key:

conditions mixed Conditions to match (no default)
maintain integer The maximum number of records to maintain (no default)
order mixed String or array defining order primary key desc
cascade boolean Set to true to delete records that depend on this record true
callbacks boolean Run callbacks (not being used). false
notices boolean When set to false, E_NOTICES wont't be displayed true

That’s all folks.

5 comments:

Please feel free to post your comment about this article.

 
JamNite ◄Design by Pocket, BlogBulk Blogger Templates