Introducing Laravel Contracts0:00
Alright, let's move on to Laravel's various contracts, or think of that as a collection of interfaces that Laravel uses. If I switch over to Chrome, you can view this all on GitHub, at illuminate/slash-contracts. That's the name of the repository. Now, take a look. Basically, the entire makeup of Laravel is defined in all of these various interfaces. And we call them contracts, but really, you can use them interchangeably. Contract is just a nice term to use. It very much describes what they are. They define a general, abstract contract that any concrete implementation must abide by.
It very much describes what they are. They define a general, abstract contract that any concrete implementation must abide by. So, for example, let's think about something like config. Whenever you want to fetch your configuration, well, we have an interface, or a contract, that defines the interactions that would be expected. So, for example, any implementation of this interface needs to conform to this contract. A has method, a get method, a set method, a prepend method, and a push method. So, from that point of view, if it weren't for anything else, an interface still makes for perfect documentation. You can hunt through here and really figure out all of the APIs that Laravel exposes.
an interface still makes for perfect documentation. You can hunt through here and really figure out all of the APIs that Laravel exposes. Why don't we do another one? Maybe something as simple as events, which we've already reviewed in the previous two episodes. All right, so we can see that there is a dispatcher contract. And take a look at the API here. Very familiar to you. There's a listen method. There's a fire method. There's some additional stuff, such as the event that is currently being fired.
There's a fire method. There's some additional stuff, such as the event that is currently being fired. So you can see that if you look through these, in addition to the ones, the methods, that you use all the time, you might realize, oh, I didn't know that I could also call an until method, which allows us to keep firing an event until a truthy response is returned. Okay, so this is pretty cool. And these contracts come straight out of the box. You don't have to do anything. They're just there.
Using the app() Helper2:25
but straight out of the box, those bindings are already set up for you. Which means, if you're working on a project that requires a really good separation of concerns and loose coupling, well, the contracts component will be a godsend to you. And of course, if you want to review the documentation, go to laravel.com/docs/contracts, and you can review the essentials here. Okay, but let's switch over to our project. There's a couple of things I do want to talk about. Why don't we begin with the app function? This is a helper function where you can see, once again, it just is a nice wrapper around app()->make.
This is a helper function where you can see, once again, it just is a nice wrapper around app make. So from the Laravel four days, if you did a lot of this stuff, well, now you can just say app, and then you pass in the name of the identifier, or even the class as a string that you want to resolve. Okay, so if I were to switch back, why don't we use one? Why don't we use that config? So if we take a look at it, we could say Contracts\Config\Repository. Here's the interface that I want to use. Illuminate\Contracts\Config\Repository.
Here's the interface that I want to use. Illuminate Contracts Config Repository. Now take a look at this. If I were to build this up, and let's just dd it. And then if we view this in Chrome, let's see what happens. I already have a server running. So if I switch over to Chrome, there we go. And take a look. All the stuff that we're used to. So for example, maybe we want to dig into database,
All the stuff that we're used to. So for example, maybe we want to dig into database, and we just want to figure out what our default database driver should be. Well, if we switch back, you know that you can do things like this. config get database.default. And if we come back and give that a refresh, there you go. We're using MySQL by default. So in this particular case, we're using the config facade. And that makes all of this interaction really easy. You're not locked to any specific driver or implementation.
And that makes all of this interaction really easy. You're not locked to any specific driver or implementation, but you still keep it as readable and simple as possible, which is hugely important. But it's true for some larger projects, you really want to have very clearly defined interfaces and boundaries. So in those cases, when and only if it makes sense for your project, you can either refer to the concrete class, or the contract, if you need to be as decoupled as possible. I'll show you both.
or the contract, if you need to be as decoupled as possible. I'll show you both. Let's do this time, app, and we'll reference the actual concrete class. But how do we do that? Well, I'm going to show you how to pin this down in just a moment. But at least for now, I happen to know that it's Illuminate\Config\Repository. Here's the concrete implementation of that particular contract. And notice it adheres to this exact API that we're talking about. Now, if you're at all a little blurry with this concept of interfaces, it does take a little time to come to terms with.
Why Interfaces Matter5:16
Now, if you're at all a little blurry with this concept of interfaces, it does take a little time to come to terms with. But just think of it like this. What you see right here is one possible way that we can fetch configuration. And it happens to be the default way in Laravel. And really, you probably would never need to change it. However, for many of these components, you might run into situations where you do need to be a little more flexible. Maybe you have your own custom way that you want to deal with configuration for your project. Well, if Laravel was referencing this specific class everywhere,
Maybe you have your own custom way that you want to deal with configuration for your project. Well, if Laravel was referencing this specific class everywhere, your hands are sort of tied, right? You want to use your own configuration methodology to fetch it. But there's no real way to hook that into the Laravel core. If Laravel is just referencing this everywhere, well, you're going to get this exact implementation of the has method, no matter what you do. But instead, if we use this concept of contracts and interfaces, remember, you can use those interchangeably.
But instead, if we use this concept of contracts and interfaces, remember, you can use those interchangeably. So if we think a little more in terms of these bindings, rather than specific implementations, well, you could create your own class that adheres to this specific contract. And then, as part of your bootstrap process, you only need to hook into Laravel and tell it, hey, whenever you need an implementation of this contract, you were using that default one that Laravel provides. But instead, I want you to use this custom one that I've built.
you were using that default one that Laravel provides. But instead, I want you to use this custom one that I've built. And now, any time in the entire project, when it needs to fetch configuration, it's going to use your implementation, not the other one. That's what interfaces allow for. They give you a lot more flexibility. Another common example is maybe something like session handling. There's so many different ways you could do it. You could use something in memory.
There's so many different ways you could do it. You could use something in memory. You could use a file-based session handler. You could use something like Redis. You could use Beanstalk. So when you think of all of these different... well, we could use the word strategy. All of these different strategies for session handling. And if we just locked ourselves into one approach, well, that's kind of rigid, right?
Now I'm going to use my Redis-based handling. If you focus on decoupling your code for the projects that warrant it, as a quick aside, if you decouple your code in this way, these sorts of changes are the easiest thing in the world. But yeah, like I say all the time, all of this comes down to context. In plenty of situations, it's just not required. Anyways, so we've learned about Illuminate, Config, Repository. That is a concrete class.
Multiple Ways to Resolve8:07
Anyways, so we've learned about Illuminate, Config, Repository. That is a concrete class. And if we were to switch over to Chrome and refresh, there it is. You get the exact same thing. Which means, to fetch the default driver, you could say database and then the default. And there we go. We get the same thing. But now, let's delete this.
We get the same thing. But now, let's delete this. And if we duplicate this, we're now going to reference the contract. Illuminate, Contracts, Config, Repository. And notice the little pattern here. If you ever get confused about where the contract is located, you can just append Contracts right after Illuminate, and it'll be the exact same thing. So if you have Illuminate, Contracts, Config, Repository, then you can find one of the concrete implementations,
So if you have Illuminate, Contracts, Config, Repository, then you can find one of the concrete implementations, most likely, at Illuminate, Config, Repository, as you can see right there. But anyways, let's comment that out. And what you're going to see is we get the exact same thing. So that's pretty cool, right? We're referencing a contract, or really just an abstract key. It's an identifier.
or really just an abstract key. It's an identifier. Now, that's really cool, but one fourth option is to simply use a general keyword, which would be Config. So if we comment that one out and try it again, yeah, once again, we get the exact same thing. So take a look. We've demonstrated four different ways that we can fetch, in this case, our configuration repository.
We've demonstrated four different ways that we can fetch, in this case, our configuration repository. And actually, in fact, if you want to take it to a fifth, well, imagine that you already have an app instance. We don't have one here, but we could just say app like that. And then we could use the array access feature of php, which Laravel's container implements. So in that case, you could say Config, and then get database default.
So in that case, you could say config, and then get database default. Come back, give that a refresh. And yeah, we still get the same thing. And real quick, if you're curious, if we take a look at the Container, it implements ArrayAccess. So that means when you try to access a key on the app object, in this case, it will call an offsetGet.
on the app object, in this case, it will call an offsetGet. And there you can see, well, you're passing in Config. So it's just basically going to do something like this, app make the key, or app make key, or we passed in Config. So that's all that's doing. It just implements array access to make it easy for us to use. Okay, so let's go back to routes.php. The final thing I want to show you in this lesson
Controller Injection Examples10:39
Okay, so let's go back to routes.php. The final thing I want to show you in this lesson is how this works within the context of a controller. And then in the next episode, what I want to do is actually dig into the underlying source code and figure out how the heck does all of this work? How are all of these keys registered so that I can reference a concrete class or an abstract, an interface in this case, or just a general alias?
or an abstract, an interface in this case, or just a general alias? How does all of that work? And how is it all wired up behind the scenes within Laravel's application and container classes? We'll talk about that in the next video. So just to finish up, well, why don't we create a controller and figure out just the basic usage? How do we request an interface and receive it automatically?
and figure out just the basic usage? How do we request an interface and receive it automatically? Well, why don't we say Route::get, and by the way, don't forget, there's also helper functions you can use. So I could say get test, and why don't we just use a dummy controller, maybe that idea of WelcomeController, and we'll call a method on it test, just to try everything out.
and we'll call a method on it test, just to try everything out. Now, I don't think I have a WelcomeController right now, so I will make one, php artisan make:controller WelcomeController, and I just want a plain controller in this case. I don't need any of those method stubs that generally come with it. So if we take a look at this,
that generally come with it. So if we take a look at this, there we go, and we're triggering a method on it called test. Okay, so now, much like we did in our routes.php file, I'll show you how to reference some of this stuff in your controller, and then I'll finish it up with an important warning that I want you to keep in mind.
and then I'll finish it up with an important warning that I want you to keep in mind. So let's go back to our controller, and like we did before, we just want to fetch our default database type. That serves no purpose other than to have something in the configuration for us to fetch, just to prove that it's working. Well, to fetch this,
just to prove that it's working. Well, to fetch this, we could use constructor injection, we could use method injection, we could use the facade, or we could use the config helper function. I'm going to show you all of them. And then further, with these two, you have the option of referencing
with these two, you have the option of referencing the concrete implementation or the contract itself. Okay, let's review all of them, and then we'll call it a day. Within the constructor, well, yeah, we could type in the config repository, and if we import this,
we could type in the config repository, and if we import this, don't forget, you can reference the concrete implementation, or you can reference the abstract, the interface, the contract. And that's what we will do in this case. And we'll call this config, and then finally,
And we'll call this config, and then finally, really quickly, assign it. Like so. So that takes care of the constructor injection option. And in this case, we would just say return $this->config->get('database.default'). And if we come over to Chrome,
get database.default. And if we come over to Chrome, and we visit our test routes, there you go. It works. So that's constructor injection using an interface. And don't forget, in the next episode, if you're curious about, well, how did Laravel know that for this interface,
if you're curious about, well, how did Laravel know that for this interface, it should use some specific implementation? In the next episode, when we go behind the scenes, and once again, peel back the curtain, I'll show you how that works. Okay, but now, what about method injection?
Okay, but now, what about method injection? The only difference there is, well, Laravel 5 supports method injection on controllers, which means you could do this. And everything will still work exactly the same. Let's try it. Refresh, and you still get the same output, which means you could remove all of this.
and you still get the same output, which means you could remove all of this. This is especially a good option in the situations where you really only want to reference your configuration in one single method. If you'll reference it elsewhere in this class, then yeah, you might want to use constructor injection. But otherwise, this is a nice and clean way to go about it. Alright, so you got constructor injection.
this is a nice and clean way to go about it. Alright, so you got constructor injection. You have method injection. Don't forget, you can still reference the facade if you want. In your controllers, I'd say there's absolutely nothing wrong with referencing a facade, regardless of what you might hear around the web. So we return, config,
So we return, config, get, and once again, database.default. So we come back to Chrome, refresh, and we once again get the same thing. And of course, you can use that at the top if you want.
And of course, you can use that at the top if you want. Okay, so we add the Facade, and then finally, if we want to use the helper function, which is what I would recommend in your controllers, by the way, then you can just call config. How clean and convenient is that? So we come back and refresh, and there we go.
Choosing the Simplest Approach15:20
So we come back and refresh, and there we go. We still get the same thing. Now, in your controllers, I would very much recommend that you use this approach. You always have to ask yourself, by using this form of injection, am I really getting any bang for my buck? Or am I just adding little bits of complexity that add up over time? In this case,
Or am I just adding little bits of complexity that add up over time? In this case, even though we're calling a general function, you still have the full flexibility to use any driver or implementation that you want. So this does not link us to any concrete class whatsoever. And that's why this is the option I would recommend to you.
