A
A
Anatoly Sidorov2017-07-29 16:22:57
Yii
Anatoly Sidorov, 2017-07-29 16:22:57

Yii2 transactions and model events/behaviors?

Good afternoon!
I did not find information about the organization of the logic of the interaction of events / behaviors while maintaining their entities and processing emerging events.
For example:
There is a User entity with an afterUpdate event, upon occurrence of which some kind of logic occurs (for example, change logging).
There is a UserImage entity associated with User, which creates a file in the file system before saving.
Sample code:

class UserBehavior extends \yii\base\Behavior
{
    public function events()
    {
        return [
            \yii\db\ActiveRecord::EVENT_AFTER_UPDATE => [$this, 'afterUpdate'],
        ];
    }

    public function afterUpdate(\yii\base\Event $event)
    {
        //логика логирования, например в файл или сторонний сервис
    }
}

class User extends \yii\db\ActiveRecord
{
    public function behaviors()
    {
        return [
            UserBehavior::className(),
        ];
    }
}

class UserImageBehavior extends \yii\base\Behavior
{
    public function events()
    {
        return [
            \yii\db\ActiveRecord::EVENT_BEFORE_INSERT => [$this, 'beforeInsert'],
            \yii\db\ActiveRecord::EVENT_BEFORE_UPDATE => [$this, 'beforeUpdate'],
        ];
    }

    public function beforeInsert(\yii\base\Event $event)
    {
        $this->saveFile($event);
    }

    public function beforeUpdate(\yii\base\Event $event)
    {
        $this->saveFile($event);
    }

    protected function saveFile(\yii\base\Event $event)
    {
        //логика сохранения файл
        //выбрасываем исключение, например не доступна файловая система
        throw new \yii\base\ErrorException('The file system is not available');
    }
}

class UserImage extends \yii\db\ActiveRecord
{
    public function behaviors()
    {
        return [
            UserImageBehavior::className(),
        ];
    }
}

Usage example:
$user = new User();
$userImage = new UserImage();

$user->load($data);
$userImage->load($data);

$transaction = Yii::$app->db->beginTransaction();

try {
    $saved = $user->save();
    $userImage->user_id = $user->primaryKey;

    $saved = $userImage->save() && $saved;
} catch (\Exception $e) {
    $saved = false;
}

$saved ? $transaction->commit() : $transaction->rollBack();

Accordingly, what happens:
1. The User entity
is saved 2. The UserBehavior behavior sends a log to a service that is not associated with our transaction.
3. The UserImage entity is saved
4. An exception is thrown when running the UserImageBehavior behavior
5. The User and UserImage entities are rolled back.
And here's the question. In fact, nothing happened in the system, but the log will lie that there were events.
Who solves such problems?
I understand that the log event can be moved to a separate event, which is triggered after all the saving actions that have occurred. But in an already written project it is difficult to take and walk through all such places.
As one of the options with a minimum of processing, I see yii2 / queue with a queue in the db, you just need to make sure that it falls under transactions. But this option looks very crutch.
Thanks in advance :)

Answer the question

In order to leave comments, you need to log in

1 answer(s)
Y
yiiworld, 2017-12-01
@yiiworld

If your log (log) goes to a service that does not have a reversing function, then you can implement a reversing function using a temporary log in a table of the same database so that there is a transactional relationship. Merge log queues by transactionUniqueID. Then we clean the records by rollback, and by commit we send the log to the service and then we clean the records. Such a delayed queue that is executed only on the after_commit event.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question