Validation rules on behavior-created attributes - yii2

I have a model with two values that has to be unique together.
Yii2 has a validation rule for this:
[['object_id', 'created_by'], 'unique', 'targetAttribute' => ['object_id', 'created_by']]
The created_by attribute is generated with blameable behavior:
public function behaviors()
{
return [
'blameable' => [
'class' => BlameableBehavior::className(),
'createdByAttribute' => 'created_by',
'updatedByAttribute' => 'updated_by',
],
];
}
The validating is done before the behavior input is stored in the model. (I know this, because if created_by is required in the rules, the model will not save - validation error.)
Is there a good yii2-way to validate a behavior-generated attribute like this?

You can specify the events that the attributes will be created on by using the 'attributes' property of the behavior, so you can amend your model like this:
public function behaviors()
{
return [
'blameable' => [
'class' => BlameableBehavior::className(),
'createdByAttribute' => 'created_by',
'updatedByAttribute' => 'updated_by',
'attributes' => [
ActiveRecord::EVENT_BEFORE_VALIDATE => ['updated_by', 'created_by']
]
],
];
}

Related

How to set a Yii2 module to return JSON response using config/main.php

I am currently using the following lines of code on every controller in the API module in order to return JSON response/data.
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_JSON;
return $behaviors;
}
It works well. But how can i achieve the same using main configuration file?
I tried the following on my frontend/config/main.php
'api' => [
'class' => 'app\modules\api\Module',
'components' => [
'user' => [
'class' => 'yii\web\User',
'identityClass' => 'common\models\User',
'enableSession' => false,
'loginUrl' => null,
],
'response' => [
'class' => \yii\filters\ContentNegotiator::className(),
'formats' => [
'application/json' => \yii\web\Response::FORMAT_JSON,
],
]
],// Module component
],
above configuration still returns XML response only. What is the correct configuration to set all the controllers in the API module to return JSON data.Thanks
Configure your response component as follows:
'response' => [
'format' => yii\web\Response::FORMAT_JSON,
// ...
]
formats is an array containing the available formats. format is the actual output format.
Add this is your config/main-local.php
use yii\web\Response;
$config['bootstrap'][]=
[
'class' => '\yii\filters\ContentNegotiator',
'formats' => [
'text/html' => Response::FORMAT_JSON,
]
];

Yii2 Apply a function to all requests

Is there any way to apply a function to all requests and queries in Yii2?
I want to replace specific characters for all of them.
I am using Yii2 advanced app
Thanks.
This is the config file:
$config = [
'language' => 'en',
'components' => [
'request' => [
'cookieValidationKey' => 'something',
],
'authManager' => [
'class' => 'yii\rbac\DbManager',
'defaultRoles' => ['guest'],
],
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
],
],
];
return $config;
Without extending custom code on each request can be exexuted like so (add this to your application config):
return [
'on beforeRequest' => function () {
if (!Yii::$app->get('user', false)) {
return;
}
$user = User::getCurrent();
if ($user) {
Yii::$app->setTimeZone($user->time_zone);
}
},
'on afterRequest' => function () {
...
},
];
Depending on when you need to execute code (before or after the request) use 'on beforeRequest' or 'on afterRequest' accordingly.
yii2 have a request component. You can extend yii\web\request and define your custom implementation.
[
...
'components' =>
'request' => [
'class' => '\common\MyRequest',
'addGeoLocationForExample' => true,
]
...

having error while implementing TimestampBehavior in yii2

In activerecord class i put this code
use yii\db\ActiveRecord;
use yii\behaviors\TimestampBehavior;
use yii\db\Expression;
public function behaviors()
{
return [
'class' => TimestampBehavior::className(),
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['create_time'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['create_time'],
],
];
}
but it is throwing me an error
Invalid Configuration – yii\base\InvalidConfigException
Object configuration must be an array containing a "class" element.
I dont know where i am doing wrong . i also follow this link
TimestampBehavior is not working in Yii2
but this is also not solving my problem.
Here is the corrected version:
public function behaviors()
{
return [
[
'class' => TimestampBehavior::className(),
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['create_time'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['create_time'],
],
],
];
}
Here is deeper explanation.
The main problem is you forgot to wrap behavior config into its own array, that's why the error is thrown. This config as all others is processed by Yii::createObject() method. So you need array containing at least class element. In case you want default config you can omit this for brevity with just specifying class name, for example:
public function behaviors()
{
return [
TimestampBehavior::className(),
],
];
}
Besides that, you also forgot to close square bracket after attribute.
And finally, you should not specify attributes like that. It was like that at initial stage of this behavior, then it was refactored. Now you only need to specify according attribute names, for example:
public function behaviors()
{
return [
[
'class' => TimestampBehavior::className(),
'createdAtAttribute' => 'created_at', // This is by default, you can omit that
'updatedAtAttribute' => 'updated_at', // This is by default, you can omit that
],
],
];
}
Also I don't think that having the same column for tracking create and update time is a good idea.
Additionally you can specify value used to update these columns:
use yii\db\Expression;
...
'value' => new Expression('NOW()'),
This will involve database to calculate time, if you want to do it on server side via PHP, you can do use closure for example like this:
'value' => function () {
return date('Y-m-d H:i:s');
}
And one more thing even if it's not directly related with this problem - you can set alias for every behavior by setting a key of configuration array. This is called named behavior:
public function behaviors()
{
return [
'timestamp' => [
...
],
],
];
}
This was mentioned by #GAMITG in his answer.
You can try this code.
public function behaviors()
{
return [
'timestamp' => [
'class' => 'yii\behaviors\TimestampBehavior',
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['create_time'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['create_time'],
],
],
];
}

Override default sort for ActiveDataProvider in Yii2

I have a lot of models using created_time. And I want all of the GridViews to show the models sorted by created_time DESC.
Right now I write something like this
$dataProvider = new \yii\data\ActiveDataProvider([
'query' => MyModel::find(),
'sort' => [
'defaultOrder' => [
'created_time' => SORT_DESC
]
],
]);
Instead of writing all of sort configuration I tried ways below but nothing works.
Using container
\Yii::$container->set(\yii\data\ActiveDataProvider::class,
[
'sort' => [
'defaultOrder' => [
'created_time' => SORT_DESC
]
]
]);
Overriding the sort in extended class.
class ActiveDataProvider extends \yii\data\ActiveDataProvider {
public $sort = [
'defaultOrder' => [
'created_time' => SORT_DESC
]
];
}
Overriding before init() in the extended class works, but it won't work if the instantiation tries to override again.
class ActiveDataProvider extends \yii\data\ActiveDataProvider {
public function init() {
$this->sort = [
'defaultOrder' => [
'created_time' => SORT_DESC
]
];
parent::init();
}
}
//but this won't work if I want to use the ascending
$dataProvider = new \app\components\data\ActiveDataProvider([
'query' => MyModel::find(),
'sort' => [
'defaultOrder' => [
'created_time' => SORT_ASC
]
],
]);
To do this for a single GridView, you can add 'defaultOrder' => ['created_time' => SORT_DESC] to the array that is accepted by setSort():
$dataProvider->setSort([
'attributes' => [
'id',
...
],
'defaultOrder' => ['created_time' => SORT_DESC]
]);
You should do this for yii\data\Sort and not for yii\data\ActiveDataProvider. See the documentation to $sort property.
1) With container:
use Yii;
...
Yii::$container->set(\yii\data\Sort::className(),
'defaultOrder' => [
'created_time' => SORT_DESC,
],
]);
2) Overriding class:
class Sort extends yii\data\Sort
{
public $defaultOrder' = [
'created_time' => SORT_DESC,
];
}

TimestampBehavior is not working in Yii2

public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['post'],
],
],
'access' => [
'class' => AccessControl::className(),
'only' => ['create', 'update', 'delete', 'view', 'index'],
'rules' => [
// allow authenticated users
[
'allow' => true,
'roles' => ['#'],
],
// everything else is denied by default
],
],
[
'class' => TimestampBehavior::className(),
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['create_time', 'update_time'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['update_time'],
],
],
];
}
Above code is for my controller behavior function. While creating or updating, 'create_time' and 'update_time' fields are not getting updated by current time. Type of those fields are set to datetime. Please need your help.
You have to declare it in the behaviors method of your model.
To use TimestampBehavior, insert the following code to your ActiveRecord class
public function behaviors()
{
return [
'class' => TimestampBehavior::className(),
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['create_time', 'update_time'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['update_time'],
],
];
}
Try this, it works for me:
use yii\db\Expression;
...
[
'class' => TimestampBehavior::className(),
'updatedAtAttribute' => 'update_time',
'value' => new Expression('NOW()'),
],
You can also use
'value' => date('Y-m-d H:i:s')
if you would like to use the PHP datetime.
I used new Expression('NOW()') to store current timestamp. But It does't store the date based on current timezone. Instead it stores based on server time.
I just used normal php date function to solve this.
eg :
use yii\behaviors\TimestampBehavior;
use yii\db\Expression;
public function behaviors()
{
return [
'timestamp' => [
'class' => 'yii\behaviors\TimestampBehavior',
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
],
'value' => date('Y-m-d H:i:s'),
],
];
}
You must also add the value and use the class Expression
use yii\behaviors\TimestampBehavior;
use yii\db\Expression;
public function behaviors()
{
return [
'timestamp' => [
'class' => 'yii\behaviors\TimestampBehavior',
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
],
'value' => new Expression('NOW()'),
],
];
}