菜鸟常忽略的基础 - 覆盖过滤器

  • 作者:KK

  • 发表日期:2016.10.11


带key的写法不影响运行

过滤器可以有多个,所以控制器的behaviors方法可以返回多个过滤器:

public function behaviors()
{
    return [
        [
            'class' => '过滤器1的class',
        ],
        [
            'class' => '过滤器2的class',
        ],
    ];
}

返回一个大数组,大数组里的元素都是数组,这些数组每个都是一个过滤器。

这经常会给菜鸟带来一种错觉:过滤器就是这样写的了!?数组里面包含几个小数组就是多少个过滤器**

对!然而其实可以这样写的:

public function behaviors()
{
    return [
        [
            'class' => '过滤器1的class',
        ],
        'name2' => [ // 这行是重点,加了个 name2 的key
		
            'class' => '过滤器2的class',
        ],
    ];
}

其中第2个过滤器是带有key的,叫“name2”,那这样会不会报错呢?不会哦!告诉你底层怎么大概怎么执行过滤器的:

foreach($controller->behaviors() as $filterConfig){
	$filter = Yii::createObject($filterConfig);
	if($filter->run() == false){
		return false;
	}
}

return true;

大概是这样的意思啦,所以,有没有key人家都在foreach遍历,不会造成影响


然而带key是有好处的

虽然给过滤器定义key对底层的foreach处理毫无影响,但我认为这也是Yii留给我们的一个扩展余地,试想有这样的情况:

  1. 项目有app\lib\BaseController这个自定义的控制器基类

  2. 项目90%的控制器都继承了BaseController

  3. BaseController定义了一个通用的登陆验证过滤器,代码如下:

    public function behaviors(){
    	return [
    		[
    			'class' => AccessControl::className(),
    			'only' => ['index', 'login', 'register'],
    			'rules' => [					
    				//登陆用户
    				[
    					'allow' => true,
    					'roles' => ['@'],
    				],
    			],
    		],
    	];
    }
    
  4. UserController想只开放index,login,register三个方法,但其它方法还是要登陆后才能访问

    此时如果User控制器这样写就会显得与Base控制器有点重叠冗余,而且并不优雅:

    public function behaviors(){
    	return [
    		[
    			'class' => AccessControl::className(),
    			'only' => ['index', 'login', 'register'],
    			'rules' => [					
    				//登陆用户
    				[
    					'allow' => true,
    					'roles' => ['@'],
    				],
    					
    				//游客
    				[
    					'allow' => true,
    					'actions' => ['index', 'login', 'register'],
    					'roles' => ['?'],
    				],
    			],
    		],
    	];
    }
    

    其实有这样一个折衷的办法:

    public function behaviors(){
    	$behaviors = parent::behaviors();
    	$behaviors[0]['rules'][] = [
    		'allow' => true,
    		'actions' => ['index', 'login', 'register'],
    		'roles' => ['?'],
    	];
    	return $behaviors;
    }
    

    看似优雅不少,不用重复定义已经定义过的东西,只需要追加新的规则需求。那如果Base控制器有多个过滤器呢?你要保证第0个元素就是登陆认证的过滤器吗?


其实如果Base控制器是这样的就好了,给过滤器定义一个key:

public function behaviors(){
	return [
		'access' => [过滤器配置]
	];
}

这个key叫access或叫name1、nameX什么都可以,就是自己起的名字,反正底层是foreach不管你,但User控制器可以这样来合成配置:

public function behaviors(){
	return \yii\helpers\ArrayHelper(parent::behaviors(), [
		'access' => [
			'rules' => [
				[
					'allow' => true,
					'actions' => ['index', 'login', 'register'],
					'roles' => ['?'],
				],
			]
		],
	]);
}

由于parent::behaviors返回的数组中带access,所以被ArrayHelper合并掉了,合并时由于里面是索引数组,所以就追加了元素到原有的rules里

所以这里有一个不显眼的细节不知你有没有注意到,官方演示过滤器的章节里,就给登陆认证过滤器增加了access这个key