Lessons: 18Length: 2.3 hours

Next lesson playing in 5 seconds

Cancel
  • Overview
  • Transcript

5.5 Dependency Injection

In order to achieve the holy grail of decoupling, you cannot go without dependency injection. Dependency injection seems to be clouded in mystery, but it doesn’t have to be. In this video, I’ll help you create two examples of dependency injection.

5.5 Dependency Injection

Hello, and welcome back to this last video in PHP OOP fundamentals. Now this video will be entirely about dependency injection. Now you've probably heard about it. It's surrounded with a lot of magic. But the thing is, in the end, it's just a clever way to decouple your code. We'll just dive right in with an example. Say I have a repository of addresses, and I would like to display a single address together with a Google Maps that belongs to that address inside of my web application. Okay, let's start, and see if we can make this work. We'll first open up the Acme folder and inside of our app folder, we'll create a class. Now that class will simply be called Address. Now this class will hold the properties for a single address. So let's just start by creating those properties. They'll be a protected property called Street. And let's copy that once. We're going to make this very basic. We'll also have a protected property called City. Now I'll just create a single public function, so that will be a public function called Find. All I will do is pass an ID, and then in here I'll just fetch an address, like so. And I'll use that address to fill the properties. And then I'll just return the entire object, so I'll just return this. Now I'm not trying to build an ORM here. This is certainly not the best way to go about it. This is just to demonstrate dependency injection which we'll get to later on. Okay, now all our addresses are stored inside of a repository. Now that repository could be anything. Let's first create an interface that will define the methods we can use to fetch and store addresses. So inside of our Repositories folder, I'll create a new interface, and I'll call that AddressRepositoryInterface. There you go. Now like we said before, this interface will purely function as a contract. So it will tell all the classes that implement this interface hey, don't forget you must define this and that method. Okay? So let's just keep it simple. We'll create a single public function, call it Find, and for now it will accept an ID as a parameter, okay? Back to our Repositories folder. Now I'll create a new class. And that class will be a repository, and it will implement AddressRepositoryInterface. It will be very simple. It'll just contain a PHP array of addresses. So, let's see, we'll call it AddressArrayRepository, like so. And then we'll make sure it implements the AddressRepositoryInterface. Okay, now first things first, we'll need a protected property called, I don't know, Addresses. And then, of course, in order to stick to our contract, we need to implement a Find method. And like in the interface, it should take an ID as a parameter. Now we'll also create a constructor, as well as a protected method, which we'll call getAddresses, and that will just return an array. That array will contain each and every address that we have in our repository. Now I'm not going to make you watch me type out all these addresses, instead I'll just paste them in, like so. Okay, well that's just a simple array containing an array with indexes of street and city, and that's three times. So inside of a construction, we'll just set this addresses and make that equal to the result of this getAddresses. So as soon as we instantiate our class, our protected property is set to the result of this method here. Okay, now all we need to for now is just find a way to return a single address. We'll do from our Find method. First we'll run a simple if statement, and say, if is set this addresses, and then we'll pass a key of ID. Well, if it sets, then, for now, let's just return it. So we'll do return this addresses ID. But then, if it's not set, we'll just return an empty array. And now we need to find a way to make this AddressArrayRepository available in our Address class. Well of course we could do something like this, we could create a protected property called addressRepository, like so. And then we need to make sure that that will contain an instant of AddressArrayRepository. So maybe we could create a constructor here and do something like this, address repository equals new \Acme\App\Repositories\AddressArrayReposi- tory. And then, inside of our Find method, we could do something like this. Now, this is a bad way to do it, but we'll see how we can improve on this later on. Okay? So bear with me. We could maybe set a result variable and make that equal to this addressRepository, Find, and then we can just pass in the ID. And now what we could do here is set our properties. And maybe because we have protected properties here, it would be a good idea to use our accessible trait that we created earlier on. So we'll use \Acme\App\Traits\Accessible, and let's just protect ourselves against mass assignment. We'll take the fillable array and add a new item, and that will be equal to, I don't know, street. And of course that needs to go between quotation marks. And also we want the city property to be fillable as well. And the same goes for the accessible property. We want streets to be publicly accessible, and city as well. So, if we have a result here, we could simply loop through that result, and say that for each result as key value, we'll just set this key to a value of value. So either a street or a city is returned here, then that will set our street and city property here. Now, before we start improving on this code, let's see if it actually works. We'll open up index.php and create a variable called addressmodel. And we'll make that equal to a new instance of Acme\App\Address. Then we'll take that address model and fire up the Find method, and let's just fetch the second address here. We store that in a variable called address, and then all we need to do is do a var dump of that same address variable and see if it actually returns something. And we are getting 1 Euston Road, London. Let's see if that's actually the second address in our repository. Yep. It is. Okay, so here we have it. We're creating a new address model. That's this class here. It contains a street and a city which are empty at the moment that we instantiated. And what we do is we take this address model and fire up the Find method which is over here. Now this Find method in its turn takes the addressRepository, which is equal to the addressArrayRepository that we created over here. And it finds the address using the passed id, it sets street and city, and it just returns itself. Now this code works like a charm, but there are a couple of things wrong with it. The first thing is this very line here. You see by creating a new AddressArrayRepository inside of our address model, we are tightly coupling this very model here to the AddressArrayRepository. In other words, we are letting the address model know which repository to use. And think about it, should it know about that? Well no, because we created an interface. It only needs to know that it has access to a Find method, which is defined in this interface here. So in the future we should be able to swap this out for an address database repository or an address JSON repository, or what have you. And that is of no concern to the address model, so no, we should not instantiate this addressRepository here. But then how can we fix this? Well, what if we passed the addressRepository to the constructor? Would that help? Okay, well let's just cut this here, and see if we can make this work. In our constructor, we now have access to an address repository, so let's just set that. We'll make this addressRepository equal to addressRepository. And now we can pass in any implementation of the addressRepository interface, whether it be an address array repository or an address JSON repository. Doesn't really matter. So let's make sure we do have an addressRepository to actually pass to this model. We'll go back to index.php and here we'll set up a variable called addressRepository, and we'll make that equal to a new address array repository. And now we'll just take this variable and pass it right here. And this is dependency injection. Instead of instantiating external classes inside of a class you simply pass them in to the constructor. But without the class needing to know which implementation to use, okay? And now for the exciting part, will this still work? Well, let's try and find a different address here. I'll just fire up the browser again, and yes, there we have it. Address 1, which is Capitol Hill in Washington. Okay, cool. Now can we take this a step further? Well, I think we can. You see, with dependency injection, it's possible to type hint the address repository that we are receiving here in this constructor. All we need to do is add addressArrayRepository before our variable. Now as long as we're using an instance of addressArrayRepository, we're okay. See? But if we were to pass a different kind of class, say maybe a postJSON repository that we created a couple of lessons ago, then that would throw an error. You see PHP knows that the argument passed to the constructor should be an instance of Acme App Repositories Address Array Repository. Okay, let's just change this back. But now we have a different problem. Because, what would happen if we were to create another implementation of our address repository interface? Well, let's see what would happen. Let's just create a new class. We'll call it AddressJsonRepository, like so, and that will implement the interface AddressRepositoryInterface. Now let's quickly add our Find method here. So we'll go down here and do Find, and clean that up and there you have it. Now I'll just copy this class name here, and then instead of instantiating an AddressArrayRepository, we will instantiate an AddressJsonRepository. Now watch what happens. We're still getting an error, because, see what we're expecting is AddressArrayRepository, while instead, we are getting AddressJsonRepository. So again, tightly coupling. Let's have a look why. Inside of our address model constructor, we're looking for an AddressArrayRepository. Again, this is an implementation of our interface. So yes, we removed the coupling here. But we added it again here. So no win. Luckily, we don't have to type hint for the implementation of the interface. We can just type hint for the interface itself. We'll just remove this and type hint addressRepositoryInterface. And as you can see, my IDE added a use statement here. We don't need this anymore, like so. So now instead of saying to this class okay, you need a certain implementation of the interface, you are just saying okay, you can take any implementation of that interface. And now, this will always work. Whether we use a AddressJsonRepository or using an AddressArrayRepository. See? And now we have successfully decoupled this repository from the address model. Could we maybe improve on this a little bit more? Well, I think we could take this bit of code here and just move it to our repository. So, what if in a Find method, we were to actually pass an instance of the Address class itself? So we can work with that. Let's do that. We'll just add this and clean this up and we can lose the result here. So all we'll do is find an address, set the street and city properties in the address model, and then just return the address model entirely. Okay. But now our find method accepts two parameters. First of all, we'll check the repository interface and say okay. For the second parameter, I would like to accept an Acme App Address type of variable. And let's just call that address. I'll just copy that. And just make sure I implement it in the same way in our actual repository implementations like so. Okay now we are working with our array repository for now. Here let's take that address and make sure we set those properties. And that's simple enough because all we need to do is do a little foreach statement here. Just take the address which is stored in here and then loop through each key inside that address so that would be street and city. And then we'll just take the address variable that we passed just a minute ago. And we'll set a property with a name of key to a value of value. And then we can just remove this and return the address like so. And this is dependency injection the other way around. We are passing the address for which we want the properties to both fields then we do fill these properties. And we just return that object here. Now what good has it done us to move this functionality from the Address class to the AddressArrayRepository class? Well again, that has to do with knowledge. If we structure our code like this, then the address model doesn't need to know how its properties are filled. You see the array repository could fill it this way but maybe the AddressJsonRepository would need an entirely different way to fill those properties. And now all that we need to do is go inside of our index file and spice up our presentation. Well, the first thing we'll do is echo an iFrame with a Google map. To do that, I'll just go over to the browser to Google maps, click the link here, and copy the iframe code. We'll just paste that inside of our index.php and there's a lot of things that we can just throw away here. Let's see what we can do. Okay, we can throw away everything after the iframe. And then there's a whole lot of shaking going on right here that we can lose as well. Let's see. We don't need this, we don't need that. We need to the q, right. Ok, now, this will contain the address. I'll just create a little room for a variable to be placed inside of. Here, we'll just take our address and we probably first need the street. And then, maybe a space and then the address city like so. And let's just make sure to cut that, paste it back inside of rawurlencode, like so. Okay, and I think that should give us a Google map, and it does. Okay, very good, maybe we'll set the language to English here. Okay, now the second thing I would like to do is echo an h1 tag And then maybe inside that tag, we'll do address city and I'll just copy that once and the street address will be inside of a paragraph tag right underneath. Let's reload that again. Oh, seems like I was a bit too hasty. Yes, I've got the wrong variable name. That should fix it. There you go, 1 Capitol Hill, Washington. Let's see if we can do the same for address number two. We'll just save that out and reload. Yep, that works fine. And then maybe address number three, okay, and that seems to work like a charm. Well, I think that's all for dependency injection. So that wraps up the PHP object-oriented programming course. I really do hope you enjoyed it and learned something new and useful as well. Of course this is just an introduction, there's a whole lot of ground that you still need to cover yourself. If after this course you feel like you would like to expand your horizons and learn a little more about OOP, Then I would really recommend you follow the course Object-Oriented Design in PHP by Matthew Machuga here on Tuts+. It deals with all kinds of Object Oriented Design patterns and also a very good thing for you to. To look into is the SOLID design principles. Learning about SOLID will quickly get you to that next level in object oriented programming. Well, that's all for now. Thank you very much for watching. I'll see you soon.

Back to the top