tags:

views:

130

answers:

4

Lets say I have a class like this:

Class User {
  var $id
  var $name;
}

And I run a query using PDO in php like so:

$stm = $db->prepare('select * from users where id = :id');
$r = $stm->execute(array(':id' => $id));
$user = $r->fetchObject('User');

If I vardump my user object it has all kinds of other fields in it that I have not defined in the User class. Obviously I could make my query specific so that it only gives me back the fields I need/want. But if I don't want to do that is there any way to make this work the way I want it to?

I like the idea of fetchObject, because it's one line of code to create this object and set member variables for me. I just don't want it to set variables I haven't defined in my class.

EDIT:

Well it seems like karim79 is right and the fetch or fetchObject won't work the way I want it to. I've added the following bit of code after I do the fetch to get the desired results.

$valid_vars = get_class_vars('User');
foreach (get_object_vars($user) as $key => $value) {
    if (!array_key_exists($key, $valid_vars)) {
        unset($user->$key);
    }
}

Obviously not the most elegant solution :/ I'm going to extend the PDOStatement class and add my own method fetchIntoObject or something like that and automatically do these unsets. Hopefully shouldn't be to much overhead, but I want to be able to easily fetch into an object with 1 line of code :)

SUPER EDIT:

Thanks to mamaar's comment I went back to the documentation again. I found what the problem is. http://us.php.net/manual/en/pdo.constants.php and scroll down to PDO::FETCH_CLASS and it explains that the magic method __set() is used if properties don't exist in the class. I overwrote the method in my target class and tada, works. Again, not the most elegant solution. But now I understand the WHY, and that's important to me :D

A: 

I don't think that's possible. fetchObject will create an instance of the classname specified as fetchObject's $class_name parameter (which defaults to stdClass). It will not check for existing classes with the same name and create an instance, assigning values only to member variables which match column names in the result. I would suggest relying on something more boring, like this:

$user = new User($result['id'], $result['name']);

Which would of course mean giving your User class a constructor:

Class User {
  var $id
  var $name;

  public function __construct($id, $name)
  {
     $this->id = $id;
     $this->name = $name;
  }

}
karim79
Right. I wanted to avoid solutions like this since the appeal of the fetchObject was that it could do that with 1 line of code. I may just extend PDOStatement and add my own method fetchIntoObject or something that does what I want it to do. Hopefully the performance will be ok.
threendib
A: 

As alternative, you can of course just fetch an array and simply typecast this yourself:

$user = (User) $r->fetch();

Btw, I've not seen this behaviour. Maybe you have PDO::FETCH_LAZY activated, that might create extra data. You could test it with ->fetchObject("stdClass"), else the reason resides with your User class, or its Parent?

mario
Unfortunately the typecasting didn't work, just threw errors. You can type cast ('object') but not specific classes like (User), (Bar), etc...As for the FETCH_LAZY, I have tried various ways using the fetch, fetchObject, fetchInto and always comes up the same. It fills in the member variables and then tacks on the extra fields the db gave it onto the end so in my example instead of having 2 vars (id, name) my test object would have 3 (id, name, descr).
threendib
A: 

You could probably use the PDOStatement->fetch method with PDO::FETCH_CLASS or PDO::FETCH_INTO as the $fetch_style parameter

Edit: So I've tried myself, and got it to work with PDOStatement->setFetchMode

class User
{
    public $id;
    public $name;
}

$db = new PDO('mysql:host=127.0.0.1;dbname=test', 'username', 'password');

$stmt = $db->prepare("select * from users where id=:userId");
$stmt->setFetchMode(PDO::FETCH_CLASS, 'User');
$stmt->execute(array(':userId' => 1));

$user = $stmt->fetch();
var_dump($user);
mamaar
I've tried FETCH_INTO and FETCH_CLASS with the fetch() method and still get the same result.
threendib
I got it to work using PDOStatement::setFetchMode, my answer is edited with a working example.
mamaar
Thanks for the update. I tried your exact code (changed db info obviously) and it's still giving me the same problem. Your comment made me go look at the documentation again, and I found what the problem is. http://us.php.net/manual/en/pdo.constants.php and scroll down to PDO::FETCH_CLASS and it explains that the magic method __set() is used if properties don't exist in the class. I overwrote the method in my target class and tada, works.
threendib