Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
<?php
use yii\base\Behavior;
use yii\db\ActiveRecord;
class SerializedFields extends Behavior
{
/**
* @var ActiveRecord
*/
public $owner;
/**
* @var string[]
*/
public $fields = [];
public function attach($owner)
{
if (!($owner instanceof ActiveRecord)) {
throw new \RuntimeException('Owner must be instance of ' . ActiveRecord::class);
}
parent::attach($owner);
}
public function ownerInit()
{
foreach ($this->fields as $fieldName) {
if (is_array($this->owner->$fieldName)) {
$this->owner->$fieldName = new \ArrayObject($this->owner->$fieldName);
} else {
$this->owner->$fieldName = new \ArrayObject();
}
}
}
public function events()
{
return [
ActiveRecord::EVENT_INIT => 'ownerInit',
ActiveRecord::EVENT_AFTER_FIND => 'unserialize',
ActiveRecord::EVENT_BEFORE_UPDATE => 'serialize',
ActiveRecord::EVENT_AFTER_UPDATE => 'unserialize',
ActiveRecord::EVENT_BEFORE_INSERT => 'serialize',
ActiveRecord::EVENT_AFTER_INSERT => 'unserialize',
];
}
public function prepareAndValidate()
{
foreach ($this->fields as $fieldName) {
if (is_array($this->owner->$fieldName)) {
$this->owner->$fieldName = new \ArrayObject($this->owner->$fieldName);
}
if (!$this->owner->$fieldName instanceof \ArrayObject) {
throw new \RuntimeException("$fieldName must be instance of \\ArrayObject");
}
}
}
public function serialize()
{
$this->prepareAndValidate();
foreach ($this->fields as $fieldName) {
$this->owner->$fieldName = json_encode($this->owner->$fieldName, JSON_UNESCAPED_UNICODE);
}
}
public function unserialize()
{
foreach ($this->fields as $fieldName) {
if (is_string($this->owner->$fieldName)) {
$unserialized = json_decode($this->owner->$fieldName, true);
if (is_array($unserialized)) {
$this->owner->$fieldName = new \ArrayObject($unserialized);
} else {
throw new \RuntimeException("$fieldName not contains serialized array");
}
} else {
throw new \RuntimeException("$fieldName is not serialized string");
}
}
}
}
<?php
class History extends ActiveRecord
{
public function behaviors()
{
return [
[
'class' => SerializedFields::class,
'fields' => ['data'],
],
];
}
public function rules()
{
return [
['data', 'safe'],
];
}
}
// then you can
$history = new History([
'data' => [
'param1' => 'value1',
'param2' => 'value2',
],
]);
// or
$history->data['param1'] = 'value3';
// or even
unset($history->data['param2']);
// or
$history->data = ['param' => 'value'];
// then
$history->save();
// after save your attribute remains untouched
echo $history->data['param'];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment