Задать вопрос

Yii2 транзакции и события/поведения модели?

Добрый день!

Не нашел информации про организацию логики взаимодействия событий/поведений при сохранении их сущностей и обработки возникающих событий.

Для примера:
Есть сущность User с событием afterUpdate, при возникновении которого происходит какая-то логика(например логирование изменения).
Есть, связанная с User, сущность UserImage, которая перед сохранением создает файл в файловой системе.

Примерный код:
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(),
        ];
    }
}


Пример использования:
$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();


Соответственно что происходит:
1. Сохраняется сущность User
2. Поведение UserBehavior отправляет лог в сервис, не связанный с нашей транзакцией.
3. Сохраняется сущность UserImage
4. При работе поведения UserImageBehavior выбрасывается исключение
5. Сущности User и UserImage откатываются.

И вот вопрос. По факту в системе ничего не произошло, но лог будет врать, что события были.
Кто как решает подобные проблемы?

Я понимаю, что событие лога можно вынести на отдельное событие, которое триггерить после всех произошедших действий по сохранению. Но в уже написанном проекте сложно взять и пройтись всем таким местам.

Как один из вариантов с минимумом переработок вижу yii2/queue с очередью в db, нужно только убедиться что оно попадает под транзакции. Но такой вариант очень костыльно выглядит.

Заранее спасибо :)
  • Вопрос задан
  • 1921 просмотр
Подписаться 2 Оценить 1 комментарий
Пригласить эксперта
Ответы на вопрос 1
@yiiworld
Если у вас журнал(лог) уходит в сервис у которого нету сторнирующей функции, то можно реализовать сторнирующую функцию используя врeменный журнал в таблице той же БД, чтобы транзакционная связь была. Объединять очереди логов по transactionUniqueID. Потом по rollback чистим записи, а по commit отправляем лог в сервис и потом чистим записи. Такая себе отложенная очередь выполняемая только по событию after_commit.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы