luyadev/luya

View on GitHub
core/behaviors/JsonBehavior.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php

namespace luya\behaviors;

use luya\helpers\Json;
use yii\base\Behavior;
use yii\db\ActiveRecord;

/**
 * Json Behavior.
 *
 * Therefore the JsonBehavior default confiugration let you seamless work with expecting array values, array input 
 * and array output. Provides auto encoding for array values after validation in order to store in the database.
 * 
 * An example of how to declared, validate and assign json values (as array):
 * 
 * ```php
 * public function behaviors()
 * {
 *     return [
 *         'json' => [
 *             'class' => JsonBehavior:class,
 *             'attributes' => ['json_field'],
 *         ]
 *     ];
 * }
 * 
 * public function rules()
 * {
 *     return [
 *         [['json_field', 'each', 'rule' => ['safe']]]
 *     ]
 * }
 * ```
 * 
 * The above mode assumes an array is passed to the model:
 * 
 * ```php
 * $model->json_field = ['foo' => 'bar'];
 * ```
 *
 * @author Basil Suter <basil@nadar.io>
 * @since 1.0.9
 */
class JsonBehavior extends Behavior
{
    public $attributes = [];

    /**
     * @var boolean If enabled, the data will be encoded before validating, this means the validation rule should be `string` otherwise the validation must be `array`.
     * This might also differ based on how the data is passed to the model. Data passed to the model will be encoded from string to array (if not already).
     * @since 1.2.0 
     */
    public $encodeBeforeValidate = false;

    /**
     * @var boolean If enabled the data will be encoded from json to array after populating the active record data.
     * @since 1.2.0
     */
    public $decodeAfterFind = true;
    
    /**
     * @var array An Array with all events which should be attached. Based on {{$encodeBeforeValidate}} and {{$decodeAfterFind}} the array will automatically receive certain events. The main goal for this property is
     * to have the option to attach events by yourself. For example it could be useful to decode values on ngrest find.
     * 
     * ```php
     * [
     *     'class' => JsonBehavior::class,
     *     'events' => [NgRestModel::EVENT_AFTER_NGREST_FIND => 'decodeAttributes']
     *     'attributes' => ['my_super_json_attribute'],
     * ]
     * ```
     * @since 2.1.0
     */
    public $events = [];

    /**
     * @inheritdoc
     */
    public function events()
    {
        if ($this->encodeBeforeValidate) {
            $this->events[ActiveRecord::EVENT_BEFORE_VALIDATE] = 'encodeAttributes';
        } else {
            $this->events[ActiveRecord::EVENT_AFTER_VALIDATE] = 'encodeAttributes';
        }

        if ($this->decodeAfterFind) {
            $this->events[ActiveRecord::EVENT_AFTER_FIND] = 'decodeAttributes';
        }

        return $this->events;
    }
    
    /**
     * Encode all attributes from json to php.
     */
    public function encodeAttributes()
    {
        foreach ($this->attributes as $name) {

            $value = $this->owner->{$name};
            
            if (is_array($value)) {
                $this->owner->{$name} = $this->jsonEncode($value);
            }
        }
    }
    
    /**
     * Decode all attributes from php to json.
     */
    public function decodeAttributes()
    {
        foreach ($this->attributes as $name) {
            $value = $this->owner->{$name};
            
            $this->owner->{$name} = $this->jsonDecode($value);
        }
    }
    /**
     * Encodes the given value into a JSON string.
     *
     * @param mixed $value This is commonly an array.
     * @return string
     */
    public function jsonEncode($value)
    {
        // if value is already a json, skip encoding and return result
        // ensure data is not doulble encoded or throws exception
        if (Json::isJson($value)) {
            return $value;
        }

        return Json::encode($value);
    }
    
    /**
     * Decodes the given JSON string into a PHP data structure.
     *
     * @param string $value
     * @return array
     */
    public function jsonDecode($value)
    {
        // data is already passed by array
        if (is_array($value)) {
            return $value;
        }

        return Json::decode($value);
    }
}