Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

json and activerecord attribute #3495

Closed
dizews opened this issue May 16, 2014 · 13 comments
Closed

json and activerecord attribute #3495

dizews opened this issue May 16, 2014 · 13 comments

Comments

@dizews
Copy link
Contributor

dizews commented May 16, 2014

Hello.

I have an attribute which contain a json string. I want to get an element of a json as a property of object.

example: $model->position->lng where $model->position is a json string.

It is a good idea to describe this example (or similar) in docs.

@Ragazzo
Copy link
Contributor

Ragazzo commented May 16, 2014

can be resolved by afterFind i think and creating needed object, or not asking and just pointing to add this to docs?

@dizews
Copy link
Contributor Author

dizews commented May 16, 2014

@Ragazzo it is good for find but I want to get an object after load too.

@qiangxue
Copy link
Member

Using afterFind is not a good idea because you would have to worry about what if you modify this attribute.

It's better to add a getter: getPositionObject() which returns the object representation of position. You can develop the getter and/or setter in a way such that it will allow you to modify part of the object.

@dizews
Copy link
Contributor Author

dizews commented May 16, 2014

@qiangxue by your logic I will get bidirectional changes. I can't imagine how to get away from rewrite attribute value (save, asArray...)

@Ragazzo
Copy link
Contributor

Ragazzo commented May 16, 2014

@qiangxue why one should be worry if it is called after find? is not it the case like for before / after methods?

@dizews in your case it should not be AR, it can be simple object, saving will require saving primary model and in beforeSave setting needed attribute according to the created object value.

@lynicidn
Copy link
Contributor

return [
            'load' => [
                'class' => \yii\behaviors\AttributeBehavior::className(),
                'attributes' => [
                    self::EVENT_AFTER_FIND => 'jsonstring',
                    self::EVENT_AFTER_UPDATE => 'jsonstring',
                    self::EVENT_AFTER_INSERT => 'jsonstring',
                    self::EVENT_INIT => 'jsonstring',
                    ...
                    ]
                'value' => function ($event) {
                        if (is_string($this->jsonstring)) {
                            $this->jsonstring = new SubModel(['attributes' => json_decode($this->jsonstring)]);
                        }
                    }
            ],
            'unload' => [
                'class' => \yii\behaviors\AttributeBehavior::className(),
                'attributes' =>[
                    ...
                ],
                'value' => function ($event) {
                        if ($this->jsonstring instanceof Model) {
                            $this->jsonstring = json_encode($this->jsonstring->attributes);
                        }
                    }
            ],
        ];
    }

@lynicidn
Copy link
Contributor

also can validate submodel on before validate and set error on owner as array

@qiangxue
Copy link
Member

@Ragazzo The problem with afterFind() is that it only works if your AR object is populated from DB query. If your AR object is newly created or if the attribute needs to be modified, you may not get the expected object representation of the attribute.

@dizews If you don't want bidirectional changes, just don't implement the setter.

@Ragazzo
Copy link
Contributor

Ragazzo commented May 16, 2014

@qiangxue yes, you right ) also as for bidirectional changes, why one should handle them, is not it the case when you should avoid hitting attribute directly, just use provided object as need?

@mdmunir
Copy link
Contributor

mdmunir commented Jun 7, 2014

How about this approach
You can do like this

class JsonBehavior extends ConverterBehavior
{
    protected function convertToStoredFormat($value)
    {
        return json_encode($value);
    }

    protected function convertFromStoredFormat($value)
    {
        $array = json_decode($value);
        return $this->arrayToObjectRecrusive($array);
    }

    private function arrayToObjectRecrusive($array)
    {
        foreach($array as $key=>$value){
            if(is_array($value)){
                $array[$key] = $this->arrayToObjectRecrusive($value);
            }
        }
        return (object)$array;
    }
}

usage

function behaviors()
{
    return [
          [
                'class'=>JsonBehavior::className(),
                'attributes'=>[
                    'jsonObject' => 'jsonstring' 
                ]
          ],
    ]
}

// in application

echo $model->jsonObject->property;

Is a another reason to put this behavior as official? :D

@dizews
Copy link
Contributor Author

dizews commented Jun 7, 2014

@mdmunir thanks!

@spiritdead
Copy link

@cebe exist a option for add this behavior for yii2 ?

@cebe
Copy link
Member

cebe commented May 1, 2016

@spiritdead add what exactly? there are a lot of different proposals that cover specific cases

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants