Difference between #14 and #15 of defaultScope

unchanged
Title
defaultScope
unchanged
Category
How-tos
unchanged
Tags
create skip bypass disable defaultScope composite condition
changed
Content
This example includes a composite condition as well as an empty condition - as
if you bypass or disable defaultScope without using resetScope().

If you want to apply constant filter(s) to all your tables - even to related
tables when using
Relational Query - then currently (Yii 1.1.12) defaultScope() is your best
option.

First, create your own base model which will hold the defaultscope. Lets call it
myBaseModel.

All your other models that should use defaultScope should extend from
myBaseModel. (Note that we can also temporarily "bypass" defaultScope
for these models if needed.)

Models that should never use this partiqular defaultScope can still extend from
CActiveRecord.


Your individual models that should be subjected to defaultScope():

~~~
[php]
class tbl1_model extends myBaseModel
{
	public $modelName 			= __CLASS__;
	public $rstatus_fieldname 	= 'tbl1_rstatus_nr';
	...
}

class tbl2_model extends myBaseModel
{
	public $modelName 			= __CLASS__;
	public $rstatus_fieldname 	= 'tbl2_rstatus_nr';
	...
}
~~~



Your individual models that should NOT be subjected to defaultScope():

~~~
[php]

class tbl32_model extends CActiveRecord
{
	...
}
~~~

I added two variables to each model that extends myBaseModel. Both these
variables are used by
defaultScope(). 
$modelName is used to include the model's class as alias in the defaultScope's
condition.
$rstatus_fieldname is used to filter the records.

Record filtering:
Each table has a field that indicates a record's status:

Record Status = 1 : record is inactive.

Record Status = 2 : record is active.

The defaultScope must be able to access these record status fields and that is
why we store
their names in $rstatus_fieldname. This enables defaultScope to re-use the same
name for all tables.
But in the db, the field's name in tbl1 is tbl1_rstatus_nr.
In tbl2 it is called tbl2_rstatus_nr.

Obviously you could give these fields the same name in all tables and then
reference them directly in defaultScope (without using $rstatus_fieldname), but
in this wiki we regardmy experience is that defaultScope
sometimes have trouble disambiguating the fields if you tunnel through tables
via
Relational Query. So I prefer disambiguating them as being
disambiguated in the db likewith tbl1_rstatus_nr,
tbl2..., tbl3... etc.

Here is the base class with the defaultScope():
 


~~~
[php]
class myBaseModel extends CActiveRecord 
{
	public function defaultScope()
	{
		$condition = $this->testStatus();
		
		return array(
			'alias' => $this->modelName,
			'condition' => $condition,
		);		
		
	}

	public function testStatus()
	{
		/* Default condition: tblX_rstatus_nr = 0
		   (return no records unless further tests in this function are
		    successful)
		   (0 is an invalid option, thus no records are returned) */
		$condition = $this->modelName . "." . $this->rstatus_fieldname
. " = 0";
		
		/*	Bypass defaultScope().
			At the start of code sections in your controller where you
			do not want to apply defaultScope, do this: 
			Yii::app()->user->setState('skipDefaultScope', True);. 

			REMEMBER to unset(Yii::app()->user->skipDefaultScope) at
			the end of these sections, AND before any 'return'
			statement in these sections, AND in
			siteController/actionError so that it is unset after
			unexpected exceptions. */
		if(isset(Yii::app()->user->skipDefaultScope))
		{
			if(Yii::app()->user->skipDefaultScope === TRUE)
			{	//Return all records
				$condition = "";
			}
		}

		/*  If user is GUEST: 
			In some controllers, the user might not be login yet, so
			you can't use Yii::app()->user->skipDefaultScope.
			You therefor have to stipulate these conditions, under
			which defaultScope must	only return active records - or
			whatever you want. */
		elseif(Yii::app()->user->isGuest)
		{
			/* Test if the defaultScope should not be applied to
			the current controller.action.model combination */
			$controller	=	Yii::app()->controller->id;
			$action		=	Yii::app()->controller->action->id;
			if(in_array($controller . '/' . $action . '/' . $this->modelName,
				array(
					'site/login/tbl7_users',
					'site/login/tbl8_user_roles',
				)
			))
			{	//Return all active records.
				$condition = 
				$this->modelName . "." . $this->rstatus_fieldname . " =
2";
			}
		}
		
		/* Logged in Users. */
		else
		{
			if(user may view only active records)
			{	//Return all active records.
				$condition = 
				$this->modelName . "." . $this->rstatus_fieldname . " =
2";
			}
			elseif(user may view active and inactive records)
			{	//Return all active and inactive records.
				$condition = 
				$this->modelName . "." . $this->rstatus_fieldname . "
> 0";
				
				/* You can also build a composite condition:
				$condition =
					$this->modelName . "." . $this->field1 . " <=
8"
					. " AND " .
					$this->modelName . "." . $this->field2 . " = " .
$this->field3
					. " AND " .
					$this->modelName . "." . $this->field4 . " = 1";
*/
			}
		}
		return $condition;
    }	
?>
~~~

Tip: defaultScope() only checks records **read** from the db. If you want
similar control over records
being **written** to db, then do something similar in beforeValidate() or
beforeSave() and beforeDelete().
Write new article