Cactivedataprovider(S) And Named Scopes

Hi

I want to use namedscopes into CActiveDataProviders like that:


$dataProvider = new CActiveDataProvider(MyModel::model()->scopeA());

and in another widget


$dataProvider2 = new CActiveDataProvider(MyModel::model()->scopeB());

The problem is in the second provider $dataProvider2 has models that are applied both scopeA and scopeB !!

The solution is using MyModel::model()->resetScope(); before the second CActiveDataProvider

but is strange to be kept the first scope. each MyModel::model()->scopeA() should be a seperated task (or not ?)

am I missing something ?

Notes:

If I use find(All e.t.c) instead CActiveDataProvider everything works correctly


MyModel::model()->scopeA()->findAll()

MyModel::model()->scopeB()->findAll()

So what is the problem ??

According to this

https://code.google.com/p/yii/issues/detail?id=2619

The problem could be solved using resetScopes as I mentioned or using new instances like that


$a = new MyModel();

$dataProvider = new CActiveDataProvider($a->scopeA());

$b = new MyModel();

$dataProvider2 = new CActiveDataProvider($b->scopeB());

I don’t know exactly why CActiveRecord is designed in this way but the above code seems to works

model is intended to be used in pipeline so every subsequent criteria modification (scopes) are combined. Query function (find, findAll, delete, etc) resets this criteria and you can start from begining. So typical usage is:




MyModel::model()->scopeA()->scopeB()->with(...)->findAll();



also ::model() function is kind-of singleton pattern so every call to that function returns same instance.

if you want to use model in two dataproviders you need to store filters and scopes in criteria objects like:




$model1 = MyModel::model()->scopeA();

$criteria1 = new CDbCriteria();

$model1->applyScopes( $criteria1 );

$dataProvider = new CActiveDataProvider($model1, array( 'criteria'=>$criteria1 ) );

...

...

$model2 = MyModel::model()->scopeB();

$criteria2 = new CDbCriteria();

$model2->applyScopes( $criteria2 );

$dataProvider = new CActiveDataProvider($model2, array( 'criteria'=>$criteria2 ) );



Hi redguy

Good explanation, I suppose it could achieved the same thing using one instance. Using applyScopes, resetScopes called internally so …


$model1 = MyModel::model()->scopeA();

$criteria1 = new CDbCriteria();

$model1->applyScopes( $criteria1 );

$dataProvider = new CActiveDataProvider($model1, array( 'criteria'=>$criteria1 ) );

...

...

$model1 = MyModel::model()->scopeB();

$criteria2 = new CDbCriteria();

$model1->applyScopes( $criteria2 );

$dataProvider = new CActiveDataProvider($model1, array( 'criteria'=>$criteria2 ) );

… has the desired results. But is there any difference in the results with the below code ?


$a = new MyModel();

$dataProvider = new CActiveDataProvider($a->scopeA());

$b = new MyModel();

$dataProvider2 = new CActiveDataProvider($b->scopeB());

::model() function creates class instance without scenario: new MyModel(null) which has impact on initialisation in constructor:




	public function __construct($scenario='insert')

	{

		if($scenario===null) // internally used by populateRecord() and model()

			return;


		$this->setScenario($scenario);

		$this->setIsNewRecord(true);

		$this->_attributes=$this->getMetaData()->attributeDefaults;


		$this->init();


		$this->attachBehaviors($this->behaviors());

		$this->afterConstruct();

	}



so it may give different results (but does not have to :) ). Creating model without any param is same as new MyModel(‘insert’) You could however create your models passing null and get very same results:


$a = new MyModel(null);

$dataProvider = new CActiveDataProvider($a->scopeA());

$b = new MyModel(null);

$dataProvider2 = new CActiveDataProvider($b->scopeB());