CActiveRecord::model() - some confusion...

[s]What exactly is the intended purpose of the static CActiveRecord::model() method?

In the documentation and examples, it’s always used when demonstrating how to build a query… however…




<?php


// find active users:


$users = User::model()->active()->find();


// find inactive users:


$users = User::model()->inactive()->find();




… because the static model() method always returns the same instance, in the second query, you are actually working with the same CDbCriteria instance - which results in a search for users that are active() AND inactive(), i.e. no results.

I thought I might have to call resetScope() before each query to work around this, but that doesn’t work either, since you would be throwing away your defaultScope() criteria.

Is there a defined way to start a new query? I don’t see one.

If the static model() method is going to result in weird buggy edge-cases whenever two queries are executed against the same model during the same request, I don’t want to use it - and if that’s the case, I don’t understand why the documentation and tutorials teach you to do this? It seems dangerous, and wrong.

Any ideas?[/s]

Your calling sequence is correct.

Did you check the generated SQLs?

The 2nd call will only bring in inactive users. It shouldn’t search for active AND inactive users.

The find() method will clean up the accumulated criteria inside.

What I wrote above is of course entirely false.

The problem was a simple typo ("conditon" instead of "condition" in a call to mergeWith() somewhere…)

It turns out, typos in CDbCriteria are forgiven, without warning! It took me over an hour to find this bug!

This is because CDbCriteria does not derive from a base class, and does not implement any magic methods - so as with any bare-bones PHP object, you can set whatever property you want, without error.

If it had been derived from CComponent, this would not be the case, as CComponent will throw exceptions if you attempt to access properties that cannot be resolved.

As there is no practical use for dynamic properties on CDbCriteria (and probably other classes), and I sincerely doubt anybody (ab)uses them that way, I would strongly recommend this hole gets patched.

I realize we don’t want CDbCriteria and other lightweight objects deriving from CComponent for performance reasons, but perhaps they could derive from another, simpler base class?

I would suggest something along the lines of this:




/**

 * A base class for classes with explicitly defined properties.

 */

abstract class CExplicit

{

  public function __get($name)

  {

    throw new CException(Yii::t('yii','Property "{class}.{name}" is not defined.',		

      array('{class}'=>get_class($this), '{property}'=>$name)));

  }


  public function __set($name,$value)

  {

    throw new CException(Yii::t('yii','Property "{class}.{name}" is not defined.',		

      array('{class}'=>get_class($this), '{property}'=>$name)));

  }

}


class CDbCriteria extends CExplicit

{

  ...

}



This should be a very lightweight solution, and effectively puts an end to this annoying (and costly!) silent error.

Thoughts?

(Edit: the __call() magic method was unnecessary)

Also see this post.