Apply Condition To All Update/delete (Activerecord)

Hi all,

We are enforcing a data protection policy on our application where records get marked as "frozen" after a certain amount of time (frozen = 1 in table), meaning that they cannot be modified or deleted.

My first instinct was to use a named scope on all update/delete methods for AR records, but after testing, found that these methods to not respect named scopes (contrary to the changelog for 1.0.6). Here is some example code of what I was trying to do:




public function scopes()

{

    return array(

        'protectPolicy'=>array(

            'condition'=>'frozen=0'

        ),

    );

}






ImportantData::model()->protectPolicy()->updateAll(array('target_column'=>'new_value'));



Alas, all records get updated with this approach :(

I know I can extend ActiveRecord class and override all update*/delete* methods by copying the current code and adding my own lines as so:




public function updateAll($attributes,$condition='',$params=array())

{

    Yii::trace(get_class($this).'.updateAll()','system.db.ar.CActiveRecord');

    $builder=$this->getCommandBuilder();

    $criteria=$builder->createCriteria($condition,$params);

    

    //added by jreznik

    if($this->hasAttribute('frozen'))

    {

        $criteria->mergeWith(array('condition'=>'frozen=0'));

    }

    

    $command=$builder->createUpdateCommand($this->getTableSchema(),$attributes,$criteria);

    return $command->execute();

}



However, I would consider this “invasive” since I can’t call parent::updateAll(); (et al), because the added code needs to be in the middle, and any future updates in the framework to these methods won’t be used in my code since this is copied from the current version.

Is there a better, more elegant way of doing this? I only want this to apply to update*/delete* and NOT select.

See this discussion.

updateAll() ignores scopes, I can only suggest to make myUpdateAll() method that calls updateAll() with "frozen = 0" condition…

Thanks for the response. So the answer is no, there is no way for this to be done without extending AR. Seems to be a contentious issue, clearly there would be some benefit for having a way of doing this. Why does everyone assume that this has to include default scopes as well? Named scopes would be fine because the developer needs to explicitly add them every time. gregmolnar is right when he says that this would not be any more inconsistent than the current behavior of ignoring scopes altogether.

My problem still remains though. Needing to add the criteria to every update/delete call leaves us open to error, so it would be nice to enable this as default behavior in our application.

Looks like the best option is to just create a new method in ActiveRecord to return the CDbCriteria object, and if we need additional criteria we can chain a mergeWith().

Would be cool if when defining scopes, we can also define which operations it applied to. Omitting this would revert to default behavior (yes to select, no to update/delete). It’s elegant AND backwards compatible.