Yii 1.1: ejsonbehavior

Converts Model Attributes and its Relations to a JSON string
22 followers

One fellow programmer at the forum was asking how to convert the ActiveRecord model and its relations to a JSON String. He said that other frameworks do that and did some research and found nothing on this subject.

This is why I created this small behavior, to do exactly that.

Requirements

Was developed using Yii 1.1.4.

Usage

Download and unzip its contents to your application's protected/behaviors folder -the zipped package has the behaviors folder within in case you haven't done that.

//
// Once EJsonBehavior is placed in the folder
// We need to configure the behavior() function
// in your model
 
public function behaviors() {
   return array(
     'EJsonBehavior'=>array(
    'class'=>'application.behaviors.EJsonBehavior'
      ),
    );
}

Then, to convert your AR object to a JSON string is, as said, 'like in other frameworks', one line of code:

echo $model->toJSON();

Thoughts

I think this could be a good starting point to create not only Yii extensions on the server side but also on the client side in form of jQuery plugins that take JSON Yii Objects as part of their configuration steps.

Just like ExtJS javascript library does with all of its objects.

Resources

Total 9 comments

#18093 report it
hemc at 2014/09/06 10:04am
This doesn't work for array of objects, like findAll, findAllByAttributes

Hi,

I have edited it to work it for array of objects.

Behaviour function will return the array for a object and we can create the array for each return and then encode it.

public function toJSON($rels = false) {
        $this->owner = $this->getOwner();
        if (is_subclass_of($this->owner, 'CActiveRecord')) {
            $attributes = $this->owner->getAttributes();
            $this->relations = $this->getRelated($rels);
            $jsonDataSource = array('attributes' => $attributes, 'relations' => $this->relations);
            return $jsonDataSource;
        }
        return false;
    }

and in controller

$models=News::model()->findAllByAttributes(array('status'=>1));
 
 
        $result=array();
        foreach ($models as $key=>$model) {
          $result[$key]= $model->toJSON();
 
        }
 
        echo CJSON::encode($result);
#15293 report it
Max100 at 2013/10/24 08:13am
Small problem

Hi

Inside my model I've got a function to get some query:

public function getSomeFields($id){
$post = $this->with('fkUsers')->findAll('fk_user_id',array($id));
return $post->toJSON(); //ERROR HERE
}

As you see in my code I've got a problem, it doesn't work.

Cheers!

#13250 report it
glyph at 2013/05/17 03:34pm
Added relations argument to toJSON method to prevent request from hanging on relation with large data

I had to update this to limit the relations if you pass the $rels argument to the toJSON method.

class EJsonBehavior extends CBehavior{
 
    private $owner;
    private $relations;
 
    // $rels should be array of relation names to get
    public function toJSON($rels=false) {
        $this->owner = $this->getOwner();
        if (is_subclass_of($this->owner,'CActiveRecord')){
            $attributes = $this->owner->getAttributes();
            $this->relations    = $this->getRelated($rels);
            $jsonDataSource = array('jsonDataSource'=>array('attributes'=>$attributes,'relations'=>$this->relations));
            return CJSON::encode($jsonDataSource);
        }
        return false;
    }
    private function getRelated($rels=false)
    {   
        $related = array();
        $obj = null;
        $md=$this->owner->getMetaData();
        foreach($md->relations as $name=>$relation) {
            if ($rels) {
                if (in_array($name,$rels)) $obj = $this->owner->getRelated($name);
            } else {
                $obj = $this->owner->getRelated($name);
            }
            $related[$name] = $obj instanceof CActiveRecord ? $obj->getAttributes() : $obj;
        }
        return $related;
    }
}

and you can now call it like so:

$rels=array();
$rels[]="relNameOne";
$rels[]="relNameTwo";
$json=$model->toJSON($rels);

If the author wants to roll this into an update, it will be backwards compatible and should prevent some requests from hanging. I imagine this will be a common problem in production sites.

#9693 report it
Anil Konsal at 2012/09/03 08:58pm
Well Done

Well done guys!

I would like the Yii development team to amend their core CJSON::encode() function to function like this. Also, Bruno's method is more feasible and easy to parse.

Thank you guys.

#6009 report it
wisp at 2011/12/05 03:38pm
Json encode

It seems to go only one relation deep :(

-edit- nevermind I created my own one http://www.yiiframework.com/extension/morray/

thanks for getting me started :)

#5848 report it
Taker at 2011/11/20 07:55am
Cool!

I just need this, thanks a lot!

#2069 report it
Antonio Ramirez at 2010/11/07 03:04pm
brunotavares

Your approach is very good brunotavares.

I've done it with 'jsonDataSource' and 'relations' objets, so to create a naming convention (sort of) just in case there are fellow programmers who wish to create jQuery client plugins with this. As by just including its properties would be much harder than knowing in advance that has a 'jsonDataSource' and 'relations' objects within.

It could be cool to push data in JSON format from the server and CGridView to be totally updated by an AJAX call (imagine this JSON object with other CGridView properties -i.e. columns, headers, and CGridView to be able to update its view)

Nevertheless, great contribution!

#2067 report it
sebas at 2010/11/07 12:26pm
Nice idea!

I like this!

nice work

#2066 report it
Bruno Tavares at 2010/11/07 09:57am
Contribution

Thank you very much for this behavior! I have an addition on how the JSON is generated. I have 2 models, Sale and SaleProducts. Doing $sale->toJSON(); the result is:

{"jsonDataSource":{"attributes":{ ... },"relations":{"products":[...]}}}

So I changed your behaviour to this:

public function toJSON(){
    $this->owner = $this->getOwner();
 
    if (is_subclass_of($this->owner,'CActiveRecord')){
 
        $attributes     = $this->owner->getAttributes();
        $this->relations= $this->getRelated();
 
        foreach($this->relations as $key => $value)
        {
            $attributes[$key] = $value; 
        }
 
        return CJSON::encode($attributes);
    }
 
    return false;
}

And now the result is:

{"id":"1","date":"2010-11-07 11:43:25","clientname":"Test","paymentmethod":"V","deliverydate":null,"totalvalue":"160","products":[{"id":"1","saleID":"1","productcode":"00002","observation":"teste","amount":"10","price":"10","totalvalue":"100"},{"id":"2","saleID":"1","productcode":"00006","observation":"testrefadsgfdsgdsgs","amount":"5","price":"12","totalvalue":"60"}]}

I don't have jsonDataSource, attributes, or relations anymore. I have a plain JSON with attributes and the relations named.

I guess is better doing json.id than json.jsonDataSource.attributes.id, or json.products than json.relations.products.

Cheers!

Leave a comment

Please to leave your comment.

Create extension