Miguel Santana
Miguel Santana

Reputation: 247

PHP PDO to create an object of a class with fetchObject

I am trying to create a "User" object inside my class "User" based on the properties I have stored inside my database, which I use PDO to connect to.

I've search some pages on Google and found that most people use the fetchObject method for that. Right now, I am trying to use this to create an object of this current class but I keep getting the error:

Fatal error: Uncaught ArgumentCountError: Too few arguments to function User::__construct(), 0 passed and exactly 11 expected in ...

Everyone keeps using this one the web but I can't seem to find their constructors, because they may not have any attributes to receive.

My current code is this:

public static function createUserBySQL($id)
    {
        $stmt = BD::getConnection()->prepare("SELECT * FROM user WHERE username = ?");
        $stmt->execute([$id]);
        $user = $stmt->fetchObject(__CLASS__);
        var_dump($user);
        return $user;
    }

This may be easy to solve but it's my first time doing OOP in PHP with PDO, so I don't know yet all of the tips and tricks to workaround that easily ;)

Thanks,
mikeysantana

QUICK EDIT: All of the attributes from "User" instance are private and I wanted to keep it like that to maintain the code logic.

Upvotes: 0

Views: 2049

Answers (3)

Tales
Tales

Reputation: 412

After some tries, I actually managed to get this working without setting default values in constructors or any workaround. The key is in the PDO::FETCH_PROPS_LATE* and the arguments array at the end.

The solution

$ctor_args = array(1, 'a placeholder')// pass an array of placeholder values.

$result = $stmt->fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, "Post", $ctor_args); 

Working example

class Post
{
    private $id;
    private $content;

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

    public function getId(){
        return $this->id;
    }
}
$result = $stmt->fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, "Post", [0,0]); 

print_r($result[0]->getId());
// 1

print_r($result);

// Array
// (
//     [0] => Post Object
//         (
//             [id:Post:private] => 1
//             [content:Post:private] => value from the db
//         )

//     [1] => Post Object
//         (
//             [id:Post:private] => 2
//             [content:Post:private] => it is working!
//         )
// )

Warning:

*When PDO::FETCH_PROPS_LATE is used with PDO::FETCH_CLASS, the constructor of the class is called before the properties are assigned from the respective column values.

So keep in mind the constructor will be called with the placeholder values, not the data from the database, so this solution is (absolutely) not recommended if you manipulate the input values right away inside the constructor. But works fine for just initializing the Class properties.

The problem with it

class Count{
        private $number5;

        function __construct($number)
            {
                $this->number5 = $number;

                print_r(10 / $this->number5); // should be 10/5, so print 2
            }
        }        
$result = $stmt->fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, "Count", [0]); 

// Warning: Division by zero in C:\xampp\htdocs\app\Model\Post.php on line 15 INF
// Warning: Division by zero in C:\xampp\htdocs\app\Model\Post.php on line 15 INF

Yes, it calls the constructor twice.

Upvotes: 1

Miguel Santana
Miguel Santana

Reputation: 247

Thanks everyone for your help.

To help future readers of this question, here is my solution:

I have kept the method "createUserBySQL" above as it is, I've only changed my constructor.

The constructor now has all of it's attributes in the signature optional, like this:

function __construct($name = null, $surname = null ...)

Inside the constructor I did a validation to see if all arguments were null. Since I had 11 of them, doing an if statement for all would be quite annoying, so I've used the method func_get_args() that returns an array with all arguments passed to the constructor.

The final constructor would look something like this:

function __construct($firstname = null, $surname = null, ...)
    {
        if (func_get_args() != null){
            $this->firstname = $firstname;
            $this->surname = $surname;
            ...
        }
    }

Upvotes: 0

Barmar
Barmar

Reputation: 781503

You need to make the parameters to your constructor optional. The documentation says:

When an object is fetched, its properties are assigned from respective column values, and afterwards its constructor is invoked.

When the constructor is invoked, no arguments are supplied, which is why you're getting that error.

The constructor needs to check whether the arguments were explicitly provided, and only fill in the properties when they are. There are some examples of how to do this in the comments section of the above documentation.

Upvotes: 2

Related Questions