How to use PDO with dependency injection?

794 Views Asked by At

I'm having a hard time understanding how to use dependency injection. I've read over a lot of questions/answers here but I can't picture it with the code I'm using.

Model.php

abstract class Model {

    protected static function getDB() {
        static $db = null;

        if ($db === null) {
            $db = new PDO('mysql:host=host;dbname=dbname;charset=utf8', 'dbuser', 'password');
        }

        return $db;
    }
}

The model.php just contains that function, which I want to move away from setting and calling it statically.

User.php

class User extends Model {

    /*
    * Selects all of the user information
    */
    public function getUser($id){

        $db = static::getDB();

        $sth = $db->prepare('SELECT * FROM user WHERE id = :id');
        $sth->bindValue(':id', $id, PDO::PARAM_INT);
        $sth->execute();

        return $sth->fetch();
    }

    /*
    * Selects all of the user posts
    */
    public function getUserPosts($id){
        $db = static::getDB();

        $sth = $db->prepare('SELECT * FROM user_posts WHERE user_id = :id');
        $sth->bindValue(':id', $id, PDO::PARAM_INT);
        $sth->execute();

        return $sth->fetch();
    }
}

In the user.php I'm extending the model class, but I have set the $db = static::getDB(); in every single function.

I know that dependency injection is pretty much just passing the methods/variables to an object but I'm not even sure I'm doing this right.

Updated with further thoughts:

I'm thinking it would be better to create a private variable, and in the constructor we'd just call the getDB() like so:

class User extends Model {

    protected $db;

    public function __construct(){
        $this->db = getDB();
    }

    /*
    * example usage
    */
    public function getUser($id){
        $sth = $this->db->prepare('SELECT * FROM user WHERE id = :id');
        $sth->bindValue(':id', $id, PDO::PARAM_INT);
        $sth->execute();

        return $sth->fetch();
    }
}

But would it still count as dependency injection as I'm not calling the class directly in the function constructor?

SECOND UPDATE: After reading multiple guides, and this page which wound up making a lot more sense, this is what I came up with.

model.php

abstract class Model {
    protected $db = null;

    public function __construct(){
        if($this->db === null){
            try {
                $this->db = new PDO('mysql:host=' . Config::DB_HOST . ';dbname=' . Config::DB_NAME . '; charset=utf8', Config::DB_USER, Config::DB_PASSWORD);
                $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            } catch (PDOException $e) {
                echo 'Connection failed: ' . $e->getMessage();
            }
        }
        return $this->db;
    }
}

User.php

class User extends Model {

    protected $db;

    public function __construct(Model $db){
        $this->db = $db;
    }

    /*
    * example usage
    */
    public function getUser($id){
        $sth = $this->db->prepare('SELECT * FROM user WHERE id = :id');
        $sth->bindValue(':id', $id, PDO::PARAM_INT);
        $sth->execute();

        return $sth->fetch();
    }
}

How does that look?

1

There are 1 best solutions below

5
On BEST ANSWER

I think that you are not using Dependency Injection because you are not actually supplying any dependencies into your Model, you are generating them on the constructor.

In order to supply the dependency you should pass it as an argument to the constructor:

public function __construct($db){
    $this->db = $db;
}

This way you decouple the creation of the connection from your Class and you can use the benefits of Dependency Injection, like passing a Mock object for testing instead of the actual thing.