Вопрос Про Фильтры

всем привет

подскажите, народ — как фильтр может предотвратить выполнение следующих за ним фильтров?

текст мануала =




Действие может иметь множество фильтров. 

Фильтры запускаются в том порядке, в котором они указаны в списке фильтров, 

при этом фильтр может предотвратить выполнение действия и 

следующих за ним фильтров.

CHttp::Exception() например, или что то подобное. Если есть вопросы по реализации могу расписать.

хм-ммм-ммммм…

треба пояснения:

  1. CHttp::Exception() — что будет происходить при генерации исключения

  2. что-то подобное — это что?

Например реализация фильтра доступа для Rights:




class RightsFilter extends CFilter

.......

protected function preFilter($filterChain){

.....

if( $allow===false )

	{

	$controller->accessDenied();

	return false;

	}

}

....

//controller

public function accessDenied($message=null){

.....

throw new CHttpException(403, $message);

}



итого получаем фильтр контроля доступа, если доступ не разрешен показываем еррор 403.

Также реализованы и стандартные фильтры. Их если не ошибаюсь 3 (postOnly,ajaxOnly,accessControl).

Вот например ajaxOnly:




public function filterAjaxOnly($filterChain)

	{

		if(Yii::app()->getRequest()->getIsAjaxRequest())

			$filterChain->run();

		else

			throw new CHttpException(400,Yii::t('yii','Your request is invalid.'));

	}



Надеюсь это прояснит ситуацию, так как в вики фильтры как-то скудно описаны, но на самом деле очень мощная штука.

to ineersa, спасибо

да уж, не простое это дело

мне надо организовать цепочку проверок и хочется сделать это с помощью фильтров

разумеется, стоит вопрос оптимизации - чтобы пробег по проверкам был минимальный в зависимости от выполнения условия

и без переадресации с целью предотвращения выполнения следующего фильтра

воПщем - пошел обедать и думать

схематичный вид проверок =

Допишите в виде методов в контроллере. Приатачьте к фильтрам. Собственно




$filterChain->run();



запускает цепочку (но он нужен для запуска и текущего фильтра в частности). Организуйте эти фильтры в виде




public function filterMyfilter($filterChain)

if($some_filter_pass)

$filterChain->run();

else {

throw new CHttpException($code,$message); // ну а зачем фильтр без ошибки))

return false;

}

return true;

//добавим в фильтр к контроллерам

public function filters()

	{

	return array(//указывайте фильтры в нужной вам последовательности

        'myfilter',...

        );

}



Не переживайте, если 1 фильтр завалится дальше по цепочке мы не можем пойти никак (при данном исполнении).

тут у меня еще один вопрос назрел = внутри вот этого метода public function filters() может быть такая конструкция:


if( условие )

{

 return array(

  'postOnly + edit, create',

  array(

   'application.filters.PerformanceFilter - edit, create',

   'unit'=>'second',

  ),

 );

}

else

{

 return array(

  'postOnly',

  array(

   'application.filters.PerformanceFilter - update',

   'unit'=>'my_arg',

  ),

 );

}

Может.

и это замечательно! ;D

в директории protected/components есть родительский класс Controller.php, от которого наследуются контроллеры из директории protected/controllers

если использовать в этом родительском классе вызов public function filters


public function filters()

{

  return array( 

    array(

      'application.filters.RequiredFilter',

    ),                   

  );

}

фильтры будут применятся ко всем методам дочерних классов?

Не совсем так. Если вы пошли этим путем в контроллере в компонентах сделайте:




public function filterRequired($filterChain)

	{

		$filter = new RequiredFilter;

		//можете передать то что вам нужно $filter->_required=$this->required

		$filter->filter($filterChain);

	}



Далее тут же в компонентах сделайте файл RequiredFilter:




class RequiredFilter extends CFilter

{

	protected $_required = array();//то что вы передали например

        

        protected function preFilter($filterChain)

	{

          //делайте тут то что вам нужно

          // можете делать в postFilter

        }

}



В контроллерах в папке protected/controllers




class ZController extends MyControllerWithFilterController

{

     public function filters()

{

  return array( 

    array(

      'required',//ваш фильтр

    ),                   

  );

}

}




все почти замечательно, НО

пардон, здесь что = filterRequired или RequiredFilter ?

Просто required. Метод в контроллере обязан начинаться с filter, изза этого он и идет в контроллере как filterRequired.

ну то есть это публичный метод filterRequired из родительского контроллера расположенного в директории components?

а с чем связана такая цепочка: создать фильтр, в родительском классе создать его экземпляр и вызвать (ведь $filter->filter($filterChain) это аналог $filterChain->run()?) и уже после этого в дочернем классе получить доступ к методу в родительском классе?

или я все не правильно понял?

  1. Да это тот самый метод (также как например actionIndex начинается с action).

  2. Ну собственно у вас ведь может быть не 1 фильтр, и не 2. В разных контроллерах возможно придется использовать разные фильтры(ну или разные последовательности). Про $filter->filter($filterChain), это практически тоже, но не совсем :rolleyes:. Вот что нам говорят исходники:




public function filter($filterChain)

	{

		if($this->preFilter($filterChain))

		{

			$filterChain->run();

			$this->postFilter($filterChain);

		}

	}



То есть так мы делаем триггер фильтров которые мы уже переписали (независимо это пост или пре фильтры). В данном контексте это правильнее.

Ну это мое понимание работы с фильтрами, возможно у вас оно немного отличается. Делайте так как удобно.

П.С. Хотелось бы услышать мнение автора.

to ineersa, большой респект и спасибо за подробное разъяснение!