Best Practice/Strategy for Limiting Which Model Attributes Get Passed into Javascript

I’m a relative Yii newbie. I’m developing an application that will be heavily reliant on passing json-encoded model data into Javascript. For example, a list of users might be passed into a variable in the view, like this…

[font="Courier New"]

<script>

var users = <?php echo json_encode($users);?>;

</script>

[/font]

…then updated every few minutes with an Ajax call.

The problem is that many models contain attributes that should not necessarily be exposed. For example, my app uses Twitter to authenticate users, and my user model looks something like this…

[font="Courier New"]

user

[indent]user_id

screen_name

name

profile_image_url

twitter_user_id_str

oauth_token

oauth_token_secret

[/indent]

[/font]

I obviously would not want oauth_token and oauth_token_secret to be included in what I pass to Javascript.

Right now I have a method in my base controller that converts active records to stdClass objects, with the option to exclude attributes…

[font="Courier New"]

/**

  • @param CActiveRecord $model

  • @return stdClass

*/

protected function modelToStdClass($model, $exclude = array()){

[indent]$o = new stdClass();

foreach($model->attributeNames() as $n){

[indent]if (! in_array($n, $exclude)){

[indent]$o->{$n} = $model->{$n};[/indent]

}[/indent]

}

return $o;[/indent]

}

[/font]

I’m not happy with this, because it isn’t foolproof (forgetting to exclude) and doesn’t take into account situations where a user model is attached to another model (e.g. message->author.)

I suspect that the best way forward would be to hive off the sensitive attributes – those that I don’t want to expose – into a separate related model that only gets retrieved when necessary. Is that the best way, or is there a better one? Thanks!

Hi and welcome to the forums,

you could "abuse" scenarios for this. E.g. you could define a scenario public:


array('user_id, screen_name', 'safe', 'on'=>'public')

Then if you add a little helper like this, you can obtain the public attributes in one go:





    /**

     * @param string $scenario name

     * @return array name/value pairs of attributes in given scenario

     */

    public function getScenarioAttributes($scenario)

    {

        $this->scenario=$scenario;

        return $this->getAttributes($this->getSafeAttributeNames());

    }



It could be handy to put this helper in an intermediate ActiveRecord class (extends CActiveRecord), from which all your other concrete AR models extend from.

Thanks! That’s a cleaner, more adaptable solution than creating separate models. Follow up question:

Does


array('user_id, screen_name', 'safe', 'on'=>'public')

go in rules()? I see the similarity with the


array('foo, bar', 'safe', 'on'=>'search')

code that gets placed in rules() by the model generator, but for the newbie the fact that rules() contains things other than data validation is somewhat confusing.

Thanks again.

–Chris

Yeah, well, that’s why i said “abuse” above: Yes it has to go into rules(). And you didn’t sound too much like a newbie to me, so i thought you’re ready to take the next step ;)