Proper way to render JSON.

So, I’ve decided to use Yii to build the website and API for a game I’m developing.

However, I want my API to be as "slim" as possible. In the previous iteration of the software I had a home grown MVC system that had a "render mode" which would take the data and, instead of rendering a view with it, would just package it into JSON and dump the output to the client.

Well, I was trying to replicate this ability in Yii, and after a few hours of documentation reading to find the “right way” I’ve come up with a hack version (Alas, most searches for “JSON” and “Yii” seem to lead to a discussion on how you should just use SOAP and be done with it.)

So, what I’ve done is extend CController with “ApiController”. My original intent was to override the “render” functions with JSONified versions… but these functions all expect a view as the first parameter, and that seems silly*. So now I’ve got a single function: renderJSON($data=null, $ttl=30)

renderJSON() dumps the ‘Content-type: application/json’ header, determines cache control settings (-1 TTL means no-cache) and expiry time (positive TTL means “x second”, 0 means “forever”, defaults to 30 seconds), echos json_encode($data), and then dies, and it seems to work. Yay! Only JSON, in the browser, with the right content type. And it looks good…

…but again, I’m trying to do things “right.” If I don’t die() at the end, Yii happily dumps out javascript (console.log output because I’m in debug mode) which I don’t want to happen on API calls. But I worry… does die()ing here have any fatal effects which I can’t forsee at this point in my use of Yii? Is there a better way to say “cease output but finish up whatever you have left to do”?

Use not die(), but




Yii::app()->end()



and all be ok.

How much further have you gotten with your API, did you use REST, (is Yii built for REST), I’m very interested in whether you will be detailing a tutorial, or case-study on your project.

I have a similar scale of project in mind and I’m currently prototyping it in 4 different frameworks, CodeIgniter, Kohana, Django and Yii) but I keep getting frustrated because there is so much I need to do and I only know the very basics of Yii.

Any pointers you can give would be great.

Thanks.

I too use die, there are no problems.

Yii::app()->end will render anyway the log, spoiling the json.

zaccaria

Problems will go later. AutoDMC trying to do things “right.” ;) Right way to end Yii application is using Yii::app()->end(); Where in production it’s only one way. See http://www.yiiframework.com/doc/api/1.1/CApplication#end-detail

Important place here marked bold.

The problem is that onEndRequest cause the log table to be prompted, wich spoil the json or images or any non-html file you are trying to send.

Therefore, using Yii::app()->end() here is not a solution, as the code will not work.

A solution is, for example, to change the configuration of the log router in order to prevent the log to be displayed, but in my opition that is too laborious and there are no reason to avoid using exit.

Cool. Thanks, zaccaria, creocoder. It looks like Yii::app()->end() is the “right way” but basically equivalent of die() for my specific use case. So while my way isn’t “completely right” at least I’m not going to cause databaseageddon by simply killing the output. I’d rather there was a way to simply tell logging “No, thanks” instead of killing the request because there’s a (slight?) possibility I’ll end up needing that hook. But then again my use is a very special case, so, whatever.

(Or, brainstorming, haven’t tried this, but maybe add this to the ApiController…


public function silentEnd() {

    if(Yii::app()->hasEventHandler('onEndRequest')) {

        ob_start();

            Yii::app()->onEndRequest(new CEvent(Yii::app()));

        $output = ob_get_contents();

        ob_end_clean();

        Yii::trace($output, 'ApiController');

    exit();

}

Then I’d have a log, and in case any modules or future code depend on that hook it’ll still be called and silently logged for the ApiController. Have to try this when I get home, just for giggles.)

@Zardon: I haven’t decided if I’m going to write up a case study or anything on my use of Yii, right now I’m simply trying to write up the program. Like I said, I had a home grown system with my own controller, models based on PHPActiveRecord, etc, but was finally lured away to Yii because of Gii and the CRUD generation facilities (I HATE CRUD. Personal web applications have taken back seat to Virtual Console Final Fantasy simply because I didn’t want to write one more object editor screen… and my application will be using TONS of CRUD pages.) (Also the idea of not having to support the framework myself was a boon.)

However, search “Yii JSON view” on Google and you’ll see a zillion posts all over the place saying “I want to use JSON to build a REST API but how to do that in Yii?” with no answers… so hopefully this thread will give somebody a head start.

I’m doing “Kinda REST.” I’ll be using GET and POST off the URL structure like REST but most likely won’t be going the full route of supporting HEAD, DELETE, etc. So maybe I should just call it a “http based JSON api” to keep the purists happy :)

Don’t be afraid of dataBaseGeddon or anything else!!

The only thing triggered on the onEndRequest is the trace, you will break anything with exit.

Anyway than for sharing your silentEnd, it can be useful for save the logs, even if in that case would be better just to change the configuration if the logger:


		Yii::app()->log->routes->itemAt(1)->enabled= false;



@zaccaria Do you mean here web log rendered by the Yii at the end of the page (CWebLogRoute) or some extensions like yii debug toolbar?

For the REST application you can just disable these kind of logging, but you can still use log routes like CFileLogRoute.

I am using such approach and Yii::app()->end() works good both for json or xml data.

For whoever finds this topic later, here is a solution to renderJSON() that also disabled web log routes.

one more simple way by using

echo CJSON::encode($result);

example code:




public function actionXYZ(){

    if (Yii::app()->request->isAjaxRequest && isset($_POST['term'])) {

            $models = Model::model()->searchNames($_POST['term']);

            $result = array();

            foreach($models as $m){

                $result[] = array(

                        'name' => $m->name,

                        'id' => $m->id,

                );




            }

            echo CJSON::encode($result);

        }

}




this is one way…

=========================

where comes the major difference while echoing straight away and using header json ??




header('Content-type: application/json');

echo json_encode($arr);

Yii::app()->end(); 



cheers :)