Depdendency Injection
A concept that I'm still learning a lot about in OOP is Dependency Injection. The basic idea is that we need an outside resource in an object and there are a ton of ways of bringing them into it, most of them terrible ideas, and a few more decent ways. But one of the best ways is dependency injection. Let's talk about some of the bad ways to do it so we can understand why Dependency Injection, and what it is. For the following examples, we're going to use a database connection. It's one of the most common dependencies in a web application, and there a lot of ways you might think would be good, but actually bite you in the butt. The worst way in the world is to use 'global.' 'Global' was fine for procedural coding when you knew what order everything was going to execute (or at least pretended to know just because you wrote everything in a linear fashion). But when you jump into an object, and try to import a resource assigned to a variable using 'global', you have no idea what state that item is in. I'm going to assume that you're using PDO because PDO is awesome. So, we'll start amateur hour, and say you have a config.php file sitting in your root or your includes with a $db variables assigned:
// config.php $db = new PDO('host', 'dbname', 'user', 'password');
And then everywhere you want to use that database connection, you pull it in with global:
//User.php class User { global $db; $db->prepare('Select * FROM users'); }
But oops, one of the other developers on your team needs a connection to another database in a different class. And, assuming that this developer has no idea what the best choice is and only has your code as an example:
//Movies.php class Movies { global $db; $db = new PDO('host', 'differentDBName', 'user', 'password'); $db->prepare('Select * FROM movies'); }
So, now at some point in your code, you've got a different definition for $db. And if the User class is uses somewhere after the Movie class, you've got the wrong database in use, and a lot of tears. And you can't define objects in a constant, so don't get any wild ideas (I mean, you can try, but don't expect any magic). As an improvement, you might decide to make a database singleton. And honestly, a lot of people have done this and continue to do this. And if you never write a test for your code (shame on you), you might not see any reprocussions. If you want to use PDO and have a singleton, this would a possible solution. I'm going to assume you've learned the errors of storing your database connection in a variable for this example:
//DB.php class Database extends PDO { private static $db; public static function getInstance() { if (!isset($this->db)) { $this->db = new PDO('host', 'dbname', 'user', 'password'); } return $this->db; } }
Then, where you needed to use it.
//User.php class User { $db = Database::getInstance() $db->prepare('Select * FROM users'); }
Like I said, the problem with this is that it makes testing hard. For one, you have a static method, which makes testing more difficult. But worse, you've enforced state across your application, removing the flexibility to use test data and databases for your tests. You've created one problem in order to solve another. Singletons have been deemed "anti-patterns" for this very reason. This explains it much better that I can. So, we want the flexilibity to define our database object however we please, but we don't want it so flexible that anyone can overwrite it and leave our code in shambles. You might think we could just instantiate the PDO in the object like so:
//User.php class User { $protected $db; function __construct() { $this->db = new PDO('host', 'dbname', 'user', 'password'); } }
This is better, because it allows us to define PDO according to each object's need, but we are still locked in to one database for our tests. By the way, we are making use here of a magic method that is inherit in all PHP objects. __construct() allows you to define what happens when an object is instantiated. It is, as the name suggests, magic. We're getting closer now, but here's one minor shift that you might hate, but gets us to discussing dependency injection:
//User.php class User { $protected $db; function __construct(PDO $db) { $this->db = $db; } }
And then, when we instantiate our object:
//some_code.php $db = new PDO('host', 'dbname', 'user', 'password'); $user = new User($db);
“ Boo! Now we've got to define our database every single time we want to use it, and we might only use it once on the page. This is more code to maintain in more place. This sucks and you suck. ” As hard as it is to hear, dear reader, you are correct. Let me assure you that there is a solution to this called an Inversion of Control (IoC) object. I'm still learning about those, and so I don't think I could do a good job with it. Instead, I'm going to hand it over to Jeffrey Way to explain. Alternatively, you can look into a library like Pimple to provide what's called a Dependency Injection Container (DIC). But hopefully you can at lease see the flexibility that Dependency Injection offers you. Your objects only need to know about a database object of some sort that offers functions with the same names. You can create a mock object to inject that simulates that for your tests, rather than relying on an actual database and potentially corrupting or losing data through your tests. It gives you a little more flexibility in future feature implementation, and keeps your objects performing as few responsibilities as possible. I am positive I did not cover everything here, and that I may have explained some poorly. If so, please shoot me a message and I will clarify where need be.















