Attribute retrieved as string instead of int

Hi,

I am quite new to Yii and PHP, and I’ve just seen a behaviour which I think isn’t normal, that’s why I’ve posted into the “Bug discussion category”.

Also just so you know I’ve spent quite some time (hours) trying to debug this and searching on internet, but couldn’t find anything (unless it’s my english, as it’s not my mother language…)

So in my Yii project, when I retrieve a CActiveRecord from the database, all the attributes of the Model I get are strings even if some columns are defined as int.

Example :




$user = Users::model()->findByPk(1);

var_dump($user->id)



This gives :




string(1) "1"



Is it normal that I get a "string" back from the database where the column was defined as "int" and the CActiveRecord "id" property has been generated by Gii as an Integer ?

I know PHP is dynamically typed but I would have expected Yii to cast the values retrieved from the database to the correct type, or am I missing something ?

I’m asking this because as I was assuming that the types were kept when retrieving models from the database, in my code I was comparing an id which I thought was a number to the number 2 using === and that was returning false…




 if ($user->id === 2) // returns false even if the user id is 2 in the database !



If the behaviour I’m seeing is correct, then what’s the best approach when comparing model’s attributes to some values, using “==” instead of “===”, or still using “===” but forcing the type of the attribute I’m comparing ?

Thanks in advance for your help.

My configuration (sorry I can’t put the versions I’m using as I’m posting from work) :

  • Mac OS Lion

  • Apache

  • Safari

  • MySQL

This is by design and already discussed few times on the forum… problem is that database values can hold bigger numbers than PHP values so a string is used

For your comparison you can use


(int)$user_id === 2

Thanks for your quick reply, but I’m still a little bit confused.

I thought the issue you’re talking about was more about when the Model is generated. For example I’ve read that if you define in MySQL a column as INT UNSIGNED then the corresponding Model attribute type will be “String” (generated by gii), which makes sense as the PHP Integer max value is less than the MySQL INT UNSIGNED max value.

However in my case I’ve defined a column as INT, and the corresponding Model attribute type has been set to “Integer” by Gii, and as far as I know the PHP Integer and MySQL INT types have the same range of values right ?

Also, if all the model attributes retrieved from the database are strings, why does gii then bother creating Integer attributes when generating the Model class if later on all the Models retrieved from the database have their attributes set to strings ?

yes the "unsigned" part is true… what is the declaration of your ID filed in the database ?

Here’s my DB declaration :




CREATE TABLE IF NOT EXISTS tbl__user

(

    id int not null auto_increment,

    email_id int null,

    primary key (id)

)

ENGINE=InnoDB;



And the generated PHP code :




<?php


/**

 * This is the model class for table '{{user}}'.

 *

 * The followings are the available columns in table '{{user}}':

 * @property integer $id

 * @property integer $email_id

 *

 */

class User extends CActiveRecord {

  :

  :



But still when I retrieve a User from the database using User::model()->findByPk(…) it returns an object whose both "id" and "email_id" attributes are set with the "string" type.

I’m interested in this too. I wonder if Yii automatically handles this?

Hasn’t anybody seen that problem before ? Or have I missed something ?

Hi,

I’m interested on this too. For me is a weird behavior. There are a lot of posts in the forum regarding this problem and always the same answers :)

I think yii is a great, nice and well organized framework. You will found other problems on other frameworks so this is one I can live with it (for now).

Maybe using behaviours like http://www.yiiframework.com/extension/i18n-datetime-behavior could help.

I think that if you don’t have performance complains you can implement behaviors or other sort of magic to cast all fields to the right type.

Cheers

Sebastian

How about this for a solution. Extend CActiveRecord and add the following methods:




public function populateRecord($attributes,$callAfterFind=true)

{

	if ($this->useTypeCasting() and is_array($attributes))

		foreach ($attributes as $name => &$value)

			if ($this->hasAttribute($name))

				settype($value, $this->getMetaData()->columns[$name]->type);


	return parent::populateRecord($attributes, $callAfterFind);

}


public function useTypeCasting()

{

	return false;

}



Then use this class as the base class for all your DB models. Override useTypeCasting() to return true in models where you want type casting.

This seems to work fine and will cast TINYINT and SMALLINT to PHP’s integer (presumably MEDIUMINT too, haven’t tested that). However, I’d really like to hear your thoughts on this, since I may be missing something. Is this safe to do and are there any significant performance hits from doing it? Does it cover all scenarios, or might there still be a case when a value will not be typecast? Obviously, when creating a model instance, you may still pass an incorrect type, but that’s your own responsibility.

Actually, you also need to check if value is NULL, so method should look like this:




public function populateRecord($attributes,$callAfterFind=true)

{

        if ($this->useTypeCasting() and is_array($attributes))

                foreach ($attributes as $name => &$value)

                        if ($this->hasAttribute($name) and $value !== null)

                                settype($value, $this->getMetaData()->columns[$name]->type);


        return parent::populateRecord($attributes, $callAfterFind);

}