
В WEB-приложении мы оперируем массивами и объектами, которые необходимо конвертировать в формат JSON перед сохранением в БД, так же как и конвертировать обратно при извлечении данных. В данной заметке пример поведения, которое позволит не отвлекаться на такие вещи, достаточно его установить и указать поля которые участвуют в конвертации. После этого поведение будет отслеживать обращения к базе данных и конвертировать данные "на лету".
Проще всего установить в виде расширения: https://github.com/klisl/yii2-json-behavior
Или создать отдельный файл, например в папке common/behaviors:
<?php namespace common\behaviors; use yii\base\Behavior; use yii\base\Event; use yii\db\ActiveRecord; use yii\helpers\Json; /** * Поведение для автоматической конвертации свойств объектов ActiveRecord в формат JSON * @property string $property Свойство содержащее объект или массив до конвертации в формат JSON * @property string $jsonField Поле таблицы для хранения данных в формате JSON */ class JsonBehavior extends Behavior { public $property; public $jsonField; /** * Список событий на которые зарегистрировано выполнение указанных методов * @return array */ public function events(): array { return [ ActiveRecord::EVENT_AFTER_FIND => 'onAfterFind', ActiveRecord::EVENT_BEFORE_INSERT => 'onBeforeSave', ActiveRecord::EVENT_BEFORE_UPDATE => 'onBeforeSave', ]; } public function onAfterFind(Event $event): void { /** @var ActiveRecord $model */ $model = $event->sender; $jsonField = $this->getJsonField($model); $model->{$this->property} = Json::decode($model->getAttribute($jsonField)); } public function onBeforeSave(Event $event): void { /** @var ActiveRecord $model */ $model = $event->sender; $jsonField = $this->getJsonField($model); $model->setAttribute($jsonField, Json::encode($model->{$this->property})); } protected function getJsonField(ActiveRecord $model): string { $jsonField = $this->jsonField ?? $this->property; if (!$model->hasAttribute($jsonField)){ throw new \DomainException("Field $jsonField with type JSON does not exist in the table " . $model::tableName()); } return $jsonField; } }
Для подключения данного класса необходимо переопределить метод behaviors в классе нужной модели(сущности):
public function behaviors(): array { return [ [ 'class' => JsonBehavior::class, 'property' => 'meta', 'jsonField' => 'meta_json' ] ]; }
В свойстве property необходимо указать название свойства в котором содержится массив или объект до конвертации в формат JSON. Для примера используется свойство "meta".
Свойство jsonField используется в случае, когда в таблице базы данных поле для хранения данных в формат JSON имеет другое название. Если названия совпадают, его можно не указывать.
И, конечно нужно указать пространство имен для класса в зависимости от места где вы разместите данное поведение.
Используются события ActiveRecord для отслеживания обращений к базе данных: перед сохранением данные конвертируются в JSON, а сразу после извлечения - обратно.