A filter is a class that can run before/after an action is executed. It can be used to modify execution context or decorate output. In our example, we'll implement a simple access filter that will allow the user to see private content only after accepting the User agreement.
Create a new yii2-app-basic
application using the composer, as described in the official guide at http://www.yiiframework.com/doc-2.0/guide-start-installation.html.
<?php namespace appmodels; use yiiaseModel; class AgreementForm extends Model { public $accept; public function rules() { return [ ['accept', 'required'], ['accept', 'compare', 'compareValue' => 1, 'message' => 'You must agree the rules.'], ]; } public function attributeLabels() { return [ 'accept' => 'I completely accept the rules.' ]; } }
<?php namespace appservices; use Yii; use yiiwebCookie; class AgreementChecker { public function isAllowed() { return Yii::$app->request->cookies->has('agree'); } public function allowAccess() { Yii::$app->response->cookies->add(new Cookie([ 'name' => 'agree', 'value' => 'on', 'expire' => time() + 3600 * 24 * 90, // 90 days ])); } }
filter
class:<?php namespace appfilters; use appservicesAgreementChecker; use Yii; use yiiaseActionFilter; class AgreementFilter extends ActionFilter { public function beforeAction($action) { $checker = new AgreementChecker(); if (!$checker->isAllowed()) { Yii::$app->response->redirect(['/content/agreement'])->send(); return false; } return true; } }
<?php namespace appcontrollers; use appfiltersAgreementFilter; use appmodelsAgreementForm; use appservicesAgreementChecker; use Yii; use yiiwebController; class ContentController extends Controller { public function behaviors() { return [ [ 'class' => AgreementFilter::className(), 'only' => ['index'], ], ]; } public function actionIndex() { return $this->render('index'); } public function actionAgreement() { $model = new AgreementForm(); if ($model->load(Yii::$app->request->post()) && $model->validate()) { $checker = new AgreementChecker(); $checker->allowAccess(); return $this->redirect(['index']); } else { return $this->render('agreement', [ 'model' => $model, ]); } } }
views/content/index.php
view with private content:<?php use yiihelpersHtml; /* @var $this yiiwebView */ $this->title = 'Content'; $this->params['breadcrumbs'][] = $this->title; ?> <div class="site-about"> <h1><?= Html::encode($this->title) ?></h1> <div class="well"> This is our private page. </div> </div>
views/content/agreement.php
view with the form:<?php use yiihelpersHtml; use yiiootstrapActiveForm; /* @var $this yiiwebView */ /* @var $form yiiootstrapActiveForm */ /* @var $model appmodelsAgreementForm */ $this->title = 'User agreement'; $this->params['breadcrumbs'][] = $this->title; ?> <div class="site-login"> <h1><?= Html::encode($this->title) ?></h1> <p>Please agree with our rules:</p> <?php $form = ActiveForm::begin(); ?> <?= $form->field($model, 'accept')->checkbox() ?> <div class="form-group"> <?= Html::submitButton('Accept', ['class' => 'btn btn-success']) ?> <?= Html::a('Cancel', ['/site/index'], ['class' => 'btn btn-danger']) ?> </div> <?php ActiveForm::end(); ?> </div>
views/layouts/main.php
file:echo Nav::widget([ 'options' => ['class' => 'navbar-nav navbar-right'], 'items' => [ ['label' => 'Home', 'url' => ['/site/index']], ['label' => 'Content', 'url' => ['/content/index']], ['label' => 'About', 'url' => ['/site/about']], ... ], ]);
A filter should extend the yiiaseActionFilter
class, which extends yiiaseBehavior
. We can override the beforeAction
or afterAction
method if we want to do post- and pre-filtering.
For example, we can check user access and throw corresponding HTTP-exceptions in a fail case. In this recipe, we redirect the user to the agreement page if the specific cookie value does not exist:
class AgreementFilter extends ActionFilter { public function beforeAction($action) { $checker = new AgreementChecker(); if (!$checker->isAllowed()) { Yii::$app->response->redirect(['/content/agreement'])->send(); return false; } return true; } }
You can attach filters to any controller or module. To specify the list of necessary routes, just use the only
or except
options. For example, we apply our filter only for the index action of the controller:
public function behaviors() { return [ [ 'class' => AgreementFilter::className(), 'only' => ['index'], ], ]; }
For more information about filters, refer to http://www.yiiframework.com/doc-2.0/guide-structure-filters.html.
For build-in cache and access control filters, refer to:
3.144.86.134