CJSON::encode() for an AR object does not include its related objects?

For example, a Order AR object has many OrderItem objects, the code below generates json string containing values of Order object only but not its OrderItem objects. Any easy way to generate a json string containing values of OrderItem objects also, with a proper structure? I know some frameworks can do this with one line of code, but do not know how to do this with Yii.


$model = Order::model()->with('orderItems')->findByPk($orderId);

$json = CJSON::encode($model);

I guess this is a very common situation, but I still cannot find a good solution except constructing an array manually and then encoding it.

i just ran into this problem as wel… i was trying to save the CSort, CPaginationand and the CDbCriteria object into an cookie os i can restore the state of my CGridView. But when i encode the object for storing the data into a cookie and i decode them it’s not a CSort, CPaginationof or CDbCriteria object anymore…




        //save page, order, filter

        $par['pagination'] = $dp->getPagination();

        $par['criteria'] = $dp->getCriteria();

        $par['sort'] = $dp->getSort();


        $cookie = new CHttpCookie('city-grid', json_encode($par));

        $cookie->expire = time()+60*60*24*180;


        Yii::app()->request->cookies['city-grid'] = $cookie;


        if(!$_GET) {

            $cookie = Yii::app()->request->cookies['city-grid'];


            $cookie = json_decode($cookie->value);


            $dp->setPagination($cookie->pagination);

            $dp->setCriteria($cookie->criteria);

            $dp->setSort($cookie->sort);

        }



Try this


$model = (array)Order::model()->with('orderItems')->findByPk($orderId);

$json = CJSON::encode($model);

found a solution: you can use seriallize and unseriallize

seriallize() does not generate json-formatted strings

The resulted json with your code contains DB schema and other infomation of the AR objects, which generates more traffic and makes parsing with javascript more difficult.

Have you tried using DAO instead of AR? I believe this is the cleanest solution than parsing AR objects into JSON.

Even though is not one line of code could help to achieve your expected results.

Hey buddy,

I have developed this http://www.yiiframework.com/extension/ejsonbehavior/, it will convert your AR and related models to a JSON String.

Tell me if was helpful.

Many thanks for your great help. I am looking for something like this! I’ll modified your code to fit my need (for example, if a related object is not eagerly loaded, do not include in the retulted JSON. And, the resulted JSON is in the structure such that, we can access [font=“Courier New”]order.orderItems[0].unitPrice[/font] in JavaScript, like we can do similar things in PHP with [font=“Courier New”]$orderItems = $order->orderItems; echo $orderItems[0]->unitPrice;[/font]

BTW, examples of "some frameworks" that I mentioned are:

Doctrine, where you can do [font="Courier New"]json_encode($model->toArray());[/font]

An extension of CodeIgniter, where you can do [font="Courier New"]$model->to_json();[/font]

Very glad it helped…

It will be nice that you could post the changes you made to the code so we can share and maybe making it better for others to use.

If you want to get the related objects to display in your JSON output, you can do this:

  1. Open CJSON.php in the framework library.

  2. modify the section of code that looks like this:




                        case 'object':

                                if ($var instanceof Traversable)

                                {

                                        $vars = array();

                                        foreach ($var as $k=>$v)

                                                $vars[$k] = $v;

                                }

                                else

                                        $vars = get_object_vars($var);

                                return '{' .

                                           join(',', array_map(array('CJSON', 'nameValue'),

                                                                                   array_keys($vars),

                                                                                   array_values($vars)))

                                           . '}';



  1. to this:



                        case 'object':

				if ($var instanceof Traversable)

				{

					$vars = array();

					foreach ($var as $k=>$v)

						$vars[$k] = $v;

				}

				else

					$vars = get_object_vars($var);

					

				// related

				foreach ($var->relations() as $key => $related)

				{

					if ($var->hasRelated($key))

					{

						$vars[$key] = $var->$key;

					}

				}

				

				return '{' .

					   join(',', array_map(array('CJSON', 'nameValue'),

										   array_keys($vars),

										   array_values($vars)))

					   . '}';



Let me know if this is what you are looking for. :)

I don’t think is not right to modify the library, at the most you can extend from the CJSON object and override…

If you do that, you make CJSON not able to work with other type of objects. Imagine you wish to encode other objects that do not have relations() method.

Yeah, you are right Antonio. I was thinking about this later and realized that this is true. The way that CJSON works is it exposes the properties of the object to the get_object_vars() method and encodes them into JSON. Do you think there is a way to expose related objects as properties once they are retrieved through eager loading?

Okay so here is my solution that I think is a good solution. This solution extends the CActiveRecord and overrides the getIterator() method. In each class that you extend CActiveRecord, place this block of code in your class:




        /**

	 * @return CMapIterator the iterator for the foreach statement

	 */

	public function getIterator()

	{

		$attributes=$this->getAttributes();

		$relations = array();

		

		foreach ($this->relations() as $key => $related)

		{

			if ($this->hasRelated($key))

			{

				$relations[$key] = $this->$key;

			}

		}

		

		$all = array_merge($attributes, $relations);

		

		return new CMapIterator($all);

	}




I think this may be a good solution that is really quick and it exposes the related fields as if they were properties of the object. What do you think of this Antonio? Would you change anything?

@Antonio Ramirez , Thank you for such a great class.

I have updated it to handle array of objects and added a comment for same on extensions page.