Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attach behavior globally for all models in application config (without inheritance)

Tags:

php

yii2

behavior

I'm dealing with a problem for Yii2 on adding TimestampBehavior to run from main config. The reason is that i have to use it in frontend and backend on most of my models.

To use it in a model is simple :

public function behaviors()
{
    return [
        [
            'class' => TimestampBehavior::className(),
            'createdAtAttribute' => 'created_at',
            'updatedAtAttribute' => 'updated_at',
            'value' => function(){ return date('Y-m-d H:i:s'); } ,
        ],
    ];
}

But if i'm trying to add the behavior in main.php nothing happens. I was thinking at :

'as timestamp'=>[
    'class'=> \yii\behaviors\TimestampBehavior::className(),
    'createdAtAttribute' => 'created_at',
    'updatedAtAttribute' => 'updated_at',
    'value' => function(){ return date('Y-m-d H:i:s'); } ,
],

But it doesn't work. Any ideas?

like image 286
cku2014 Avatar asked Jan 27 '15 13:01

cku2014


2 Answers

I know its a bit old question but in case one is in same problem, I will share what I have found as clean way to do it. I just define a behavior in a trait. I then just use that trait in models and that is it. It adds only one line in model, yet its cleaner and makes code maintenance a bit breeze.

Here is sample trait

<?php

namespace common\traits;

use Yii;
use yii\behaviors\BlameableBehavior;
use yii\behaviors\TimestampBehavior;

trait Signature 
{

    public function behaviors()
    {
        return [
            [
                'class' => BlameableBehavior::className(),
                'createdByAttribute' => 'created_by',
                'updatedByAttribute' => 'updated_by',
            ],
            [
                'class' => TimestampBehavior::className(),
                'createdAtAttribute' => 'created_at',
                'updatedAtAttribute' => 'updated_at',
                'value' =>function(){
                    return time();
                },
            ],
        ];
    }
}

And model code

class Category extends \yii\db\ActiveRecord
{
    use \common\traits\Signature;
    // Model code here
}
like image 164
Stefano Mtangoo Avatar answered Oct 31 '22 22:10

Stefano Mtangoo


In case you don't want to use inheritance I can suggest the following method.

The basic idea behind it is using events of ActiveRecord:

use yii\base\Event;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;

...

$events = [ActiveRecord::EVENT_BEFORE_INSERT, ActiveRecord::EVENT_BEFORE_UPDATE];

foreach ($events as $eventName) {
    Event::on(ActiveRecord::className(), $eventName, function ($event) {
        $model = $event->sender;

        if ($model->hasAttribute('created_at') && $model->hasAttribute('updated_at')) {
            $model->attachBehavior('timestamp', [
                'class' => TimestampBehavior::className(),
                'value' => function () {
                    return date('Y-m-d H:i:s');
                },
            ]);
        }
    });
}

This code will dynamically attach TimestampBehavior to all models which are inherited from yii\db\ActiveRecord before saving it to database.

You can also omit createdAtAttribute and updatedAtAttribute because they already have these names by default (since it's most common).

As you can see behavior is attached only when both created_at and updated_at attributes exist, no need to create extended behavior for that.

To avoid inheritance and copy / paste this code should run on every application bootstrap.

You can add this to entry script (before application run) right away and it will work, but it's not good practice to place it here, also these files are generated automatically and in git ignored files list.

So you need to create just one separate component containing that logic and include it in the config. No need to extend classes etc.

Let's say it's called common\components\EventBootstrap. It must implement BootstrapInterface in order to work properly.

namespace common\components;

// Other namespaces from previous code

use yii\base\BootstrapInterface;

class EventBootstrap implements BootstrapInterface
{
    public function bootstrap($app)
    {
        // Put the code above here  
    }
}

Then you need to include it in config in bootstrap section:

return [
    'bootstrap' => [
        'common\components\EventBootstrap',
    ],
];

Official documentation:

  • Event::on()
  • BootstrapInterface

Additional notes: I also tried to specify it through the application config only, but with no success.

I didn't find a way to specify ActiveRecord there.

You can see this question, but behavior there is attached to the whole application which is possible through config.

like image 24
arogachev Avatar answered Oct 31 '22 23:10

arogachev