custom validation in yii2 model - yii2

I am trying to add custom rule to form. I have added a custom function in model but it's not working for me.
class BackendUser extends ActiveRecord implements IdentityInterface
{
public function rules()
{
return [
['username','validateUsername','params'=>'username'=>'username']],
];
}
public function validateUsername($attribute, $params)
{
if (preg_match('/[^a-z])/i', $this->$attribute)) {
$this->addError($attribute, 'Username should only contain
alphabets');
}
}}

In PHP there is no construct like you used here (a => b => c, maybe it's a typo) and you don't have to pass any parameters anyway since you don't use them in the validator method. Simple
public function rules()
{
return [
['username','validateUsername'],
];
}
is enough.

There are few typos in your code. Try using $this->{$attribute} in dynamic attributes and also params key should be an array while calling inline validation.
class BackendUser extends ActiveRecord implements IdentityInterface
{
public function rules()
{
return [
['username','validateUsername','params'=>['username'=>'username']],
];
}
public function validateUsername($attribute, $params)
{
if (preg_match('/[^a-z])/i', $this->{$attribute})) {
$this->addError($attribute, 'Username should only contain alphabets');
}
}
}

Related

How to check input data in YII2 for REST API?

How to check input data in YII2 for REST API?
Here's how it's done in a non-REST API:
Controller
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use app\models\Index__GET;
class SiteController extends Controller
{
public function actionIndex($ch_name_url = null)
{
$model = new Index__GET();
$model->ch_name_url = $ch_name_url;
if($model->validate()){
return $this->render('index');
}
}
}
Model
<?php
namespace app\models;
use Yii;
use yii\base\Model;
class Index__GET extends Model
{
public $ch_name_url;
public function rules()
{
return [
['ch_name_url', 'trim'],
['ch_name_url', 'required'],
];
}
}
And now in the controller call $model->validate() for data validation. How to do validation incoming data in the REST API, using yii\rest\Controller and yii\rest\ActiveController?
I try but data validation fails:
I want a GET request to include two required fields.
But if I use /users/123 I will receive data, while I should not receive it, because of the model [['id', 'ch_name_url'], 'required'],.
Me need /users?id=123&ch_name_url=myname
Controller
namespace app\controllers;
use yii\rest\ActiveController;
class IndexController extends ActiveController
{
public $modelClass = 'app\models\Index__GET';
}
Model
<?php
namespace app\models;
use Yii;
use yii\db\ActiveRecord;
class Index__GET extends ActiveRecord
{
public $id;
public $ch_name_url;
public $email;
public static function tableName()
{
return 'user';
}
public function fields()
{
return ['id', 'ch_name_url', 'email'];
}
public function rules()
{
return [
[['id', 'ch_name_url'], 'required'],
];
}
}
Just create a controller extending from \yii\rest\ActiveController, then validate will run automatically. Do something like this:
namespace app\controllers;
use yii\rest\ActiveController;
class IndexController extends ActiveController
{
public $modelClass = 'app\models\Index__GET';
}
$model->validate() is called by default when you call $model->save(), but if you need to validate a model in an action, do it like you did on your question example code.
Just remember that the actions from REST are used a bit different from normal call, where actionIndex usually is not needed.
For more information, follow the original docs: REST Quick Start

Make every functions in all controller execute code some code before execute the functions without add the code in every functions in Yii2

I have a code for checking a session isset or not in Yii2, I added the code inside a function in a controller, this is the code
if(!isset($session['selectedMonth'])){
return $this->redirect(['select-period/month']);
return false;
}
I have over than 50 functions in 10 controllers, I want every function use that code, how do I can make it without put that code in every function one by one?
You could create base controller for your app, and extend all other controllers from it. Then you could add beforeAction() method in this base controller, so it will be used by all controllers that inherit from base controller:
public function beforeAction($action) {
// initialize $session here
if(!isset($session['selectedMonth'])){
Yii::$app->response->redirect(['select-period/month']);
return false;
}
return parent::beforeAction($action);
}
You can create simple behavior that will handle 'before action' event, for example:
use Yii;
use yii\base\Behavior;
use yii\base\Controller;
class RedirectBehavior extends Behavior
{
public function events()
{
return [
Controller::EVENT_BEFORE_ACTION => 'beforeAction',
];
}
public function beforeAction($event)
{
if (Yii::$app->session->has('selectedMonth')){
return;
}
Yii::$app->getResponse()->redirect(['select-period/month'])->send();
}
}
and attach it to your controllers
public function behaviors()
{
return [
'redirect' => [
'class' => RedirectBehavior::className(),
],
];
}

How to pass a variable from AccessFilter to Controller in Yii2

I have an AccessFilter Class
class ProjectAccessControl extends \yii\base\ActionFilter
{
public $a;
/**
* #inheritdoc
*/
public function beforeAction($action)
{
switch ($action->id) {
case 'view':
// code here
break;
}
}
In controller I revoke this AccessFilter in behaviour() method like this:
public function behaviors()
{
return [
'access' => [
'class' => ProjectAccessControl::className(),
]
];
}
Now I want to pass the $a variable from AccessFilter to an any action in Controller. How to do that?
You should simply add a variable in your controller, e.g. :
public $a;
And use this in your filter :
$this->owner->a = $this->a;

Yii2 How to create models dynamically and specify relationship among them?

I am creating tables on the fly. If I created the tables Schools and Classes. How can I create the models for them and specify relationships among them.
I searched but did not get anything on this topic. Any help would be appreciated. Thank you.
The only way I can figure out is to use "global variables" for tables - e.g. in Yii::$app->params['ar_tables'] and redefine them dynamically:
In config:
[
....
'params' => [
'ar_tables' => [
'Parent' => 'parent',
'Child' => 'table2'
]
]
....
]
Parent class:
class Parent extends \yii\db\ActiveRecord
{
public static function tableName()
{
return Yii::$app->params['ar_tables']['Parent'];
}
public function getChildren
{
return self::hasMany(Child::className(), ['parent_id' => 'id']);
}
}
Child class:
class Child extends \yii\db\ActiveRecord
{
public static function tableName()
{
return Yii::$app->params['ar_tables']['Child'];
}
public function getParent
{
return self::hasOne(Parent::className(), ['id' => 'parent_id']);
}
}
After that you can dynamically change Yii::$app->params['ar_tables'] values for getting what you want. I have already tried this. And didn't like :)

Yii2 Events: How to add multiple event handlers using behavior's events method?

Here is my Behavior's class events() method. When I trigger an event second handler i.e. sendMailHanlder gets called and it ignores anotherOne. I believe, the second one overwrites first one. How do I solve this problem so that both event handlers get called?
// UserBehavior.php
public function events()
{
return [
Users::EVENT_NEW_USER => [$this, 'anotherOne'],
Users::EVENT_NEW_USER => [$this, 'sendMailHanlder'],
];
}
// here are two handlers
public function sendMailHanlder($e)
{
echo ";
}
public function anotherOne($e)
{
echo 'another one';
}
One thing to notice is that I'm attaching this behavior to my Users.php model. I tried adding both handlers using model's init() method. That way both handlers got called. Here is my init code.
public function init()
{
$this->on(self::EVENT_NEW_USER, [$this, 'anotherOne']);
$this->on(self::EVENT_NEW_USER, [$this, 'sendMailHanlder']);
}
You can override Behavior::attach() so you can have something like this in your UserBehavior and no need of your events()
// UserBehavior.php
public function attach($owner)
{
parent::attach($owner);
$owner->on(self::EVENT_NEW_USER, [$this, 'anotherOne']);
$owner->on(self::EVENT_NEW_USER, [$this, 'sendMailHanlder']);
}
You can use anonymous function for attaching handler's in events method :
ActiveRecord::EVENT_AFTER_UPDATE => function ($event) {
$this->deleteRemovalRequestFiles();
$this->uploadFiles();
}
You should not use equal event names. Use this instead:
public function events()
{
return [
Users::EVENT_NEW_USER => [$this, 'sendMailHanlder'],
];
}
// Here are two handlers
public function sendMailHanlder($e)
{
echo '';
$this->anotherOne($e);
}
public function anotherOne($e)
{
echo 'another one';
}