Introducing Module Providers0:00
Welcome back. On the last lesson, we spoke about how to split your application and also how to register the modules folder within the custom namespace. And we did this right here on our composer.json file. On this lesson, we're going to talk about how to set up our service providers, which are basically the heart of each module. It is within the service providers where we're going to tell Laravel where to load migrations from, where to load configuration files from. It's also where we're going to bind our services to the Laravel container. Remember that Laravel provides convention over configuration,
Creating OrderServiceProvider0:32
It's also where we're going to bind our services to the Laravel container. Remember that Laravel provides convention over configuration, and in our case, we're doing the opposite. We're configuring it in a custom way, so we need to tell Laravel where to load things from. Let's start with the order module. So we can go here, and I'm going to create a new class. First, I'm going to create a directory called providers. And I created it on the wrong folder, so let's move this. Now, I'm going to create a class. We're going to call it OrderServiceProvider. Within this OrderServiceProvider, we want to extend the BaseServiceProvider class. Now, keep in mind, this is exactly what a standard service provider would be.
Within this OrderServiceProvider, we want to extend the BaseServiceProvider class. Now, keep in mind, this is exactly what a standard service provider would be. So if we were to generate one using MakeProvider, so for example, OrderServiceProvider, and we go to it, so we have this one. It's pretty much the same thing, but obviously, we want to delete this one. So let's get rid of it and go back to our old one. Each service provider has two methods. It has the register method, and it also has the boot method. The difference between them is they're called at different times.
Register vs Boot Timing1:36
and it also has the boot method. The difference between them is they're called at different times. So if we go back to config, app.php, and you scroll down a little bit, you're going to see all of the service providers. Laravel now offers the default providers method, so you don't actually see them, but if you go to this method, and you go to defaultProviders, you're going to see all of them. The difference between the boot and the register method is really when they're invoked. Laravel is going to iterate through this array of providers, or, well, this array of providers, and as it goes through each provider, it is going to call the register method.
or, well, this array of providers, and as it goes through each provider, it is going to call the register method. Once it has gone through all of the providers, then it is going to loop again and call the boot method. The basic difference is on the boot method, you're going to have access to everything that should be bound to the container. You're going to have access to all the service that Laravel registers. In our case, we don't really want the register method. We want to wait for the framework to have booted. This returns void, or, well, it doesn't return anything. Let's start with loading migrations from a specific place. As you know, we usually load migrations from database/migrations. In our case, we want to move
Loading Module Migrations2:40
Let's start with loading migrations from a specific place. As you know, we usually load migrations from database/migrations. In our case, we want to move those into our module. We can call the loadMigrationsFrom method. There we go. It accepts one argument, which is the path to the migrations. If we look at our module, or inside the providers folder, let's create a new one called database. Let's add a new one called migrations. The reason I created a database folder is we're going to talk about factors later. We're also going to store them inside the database folder, at least for now. Remember, this structure
folder is we're going to talk about factors later. We're also going to store them inside the database folder, at least for now. Remember, this structure is not definitive. This is not a one-size-fits-all thing. Your structure may vary. I'm going to present examples, and we're going to talk about this later. For now, let's go with this structure. Now I have the migrations folder. I can tell Laravel to load our migrations from the current directory. We're going to go back one directory, database, migrations. Now Laravel knows that it should load migrations from this path. Let's test this. The first thing I'm going to do is I'm going to comment this line, and then I'm going to move the order
from this path. Let's test this. The first thing I'm going to do is I'm going to comment this line, and then I'm going to move the order migrations to this path. Now I am going to rerun all of the migrations. As you can see, it did not create any of the order tables. Now I'm going to uncomment this, and rerun the command. Still nothing. The reason is we haven't registered the service provider. We need to tell Laravel to load the service provider. Let's go back to our config/app.php, and we're going to load our very own service providers. So OrderServiceProvider, there we go.
Merging Module Config4:16
back to our config/app.php, and we're going to load our very own service providers. So OrderServiceProvider, there we go. If I rerun this now, we can see that the orders table was created, as well as the order_lines table. That's the first thing. The second thing we want to add is a configuration file. For that, we can use the mergeConfigFrom method. The first argument is the path of the file. For now, let's keep it simple. Let's simply create a file with the order module. We want the current directory. We want to go back one directory, and we're going to call it config.php. The second argument is the key, the name of the config.
We want to go back one directory, and we're going to call it config.php. The second argument is the key, the name of the config. For now, we can call it order. If we go back to order, and we create a file called config. There we go. We want to return an array. For example, let's say we want to return a config that determines the maximum amount of items someone can add to their cart, or rather, make a purchase. So we're going to call it maxItems. Let's say 10. Now, if I open up a php artisan tinker session, and I say config.order, there we go. We get this config. So we're telling Laravel, saying, hey, you can load the following
Registering Module Routes5:20
a tinker session, and I say config.order, there we go. We get this config. So we're telling Laravel, saying, hey, you can load the following config file from this path, and it is going to be called order. Okay, so now we're loading migrations. We're loading the config file, as well. The next thing we want to do is register our routes. To do that, let's create a new file. So we want a class. We're going to call it RouteServiceProvider. If we go to the base RouteServiceProvider within our Laravel application, you're going to notice it's pretty much the same thing. You have a base RouteServiceProvider class, which this one extends, and then you have the boot method,
you're going to notice it's pretty much the same thing. You have a base RouteServiceProvider class, which this one extends, and then you have the boot method, and this is where you register your routes. Let's copy this, because we want the exact same thing. We need to extend RouteServiceProvider, and we can give it an alias. So let's say base RouteServiceProvider, because they share the same name. So we want inside the boot method to paste this. Now, what we're saying is, we want to register routes, we pass a callback, and now we have two options. We can either call the routes here, which I do not recommend, or we can call a file.
a callback, and now we have two options. We can either call the routes here, which I do not recommend, or we can call a file to store those routes. For example, let's start with the web.php routes. First, you have to import the Route facade, as usual. We're saying that we want to use WebMiddleware, and then inside this group function, we can pass the path to a file. For now, we're also going to store it within the root of the module. Let's create another file inside order. It is going to be called routes.php, and then here we have to pass the path, just like we did with the migrations and with the config files. So we can say there, and pass routes.php. Alright, cool. So if we
just like we did with the migrations and with the config files. So we can say there, and pass routes.php. Alright, cool. So if we add a test route right here, let's say we want a getRouteOrderTest, which is going to return hello world. If I go to the terminal and execute route:list, you're going to see nothing. And the reason is, we also have to register the service provider. We have two options here. You can either go to your app.php file and also register it, or you can register from within our OrderServiceProvider. You can say, hey,
.php file and also register it, or you can register from within our OrderServiceProvider. You can say, hey, we call the register method, and then we can pass our RouteServiceProvider, which is this one. If I execute it now, you're going to see the order test route. Now we're telling Laravel, hey, load migrations from this directory as well, merge the following config file, and also register the following provider. Let's say we wanted to add an EventServiceProvider, which we will. We could also call the register method. At this point, we do not want to do that. If you want to bind your own instances to the container,
will. We could also call the register method. At this point, we do not want to do that. If you want to bind your own instances to the container, you can also call the bind method. The service provider is really the core of each module. It's where we're going to tell Laravel where to load things from, it's where we're going to bind our own instances to the container, it's where we're going to map our commands, all of that fun stuff. And ideally, unless it's a very simple module, and you do not need any of this, you want to have at least one provider per module. So what I'm going to do now is I'm going to do the same thing for the other three modules that we have. Or rather, we don't have anything on payment yet,
Replicating Across Modules8:32
per module. So what I'm going to do now is I'm going to do the same thing for the other three modules that we have. Or rather, we don't have anything on payment yet, so I'm going to do it for Product and Shipment. Alright, as you can see, I replicated the same structure we built inside the Order module to the Product and to the Shipment modules. Exact same thing. This is pretty standard. This is really good because now our modules are independent from each other. I can register routes within the Order module and that's not going to affect the Product module. I can create my own bindings within the Order module and it won't affect the other, etc, etc, etc. If I list our routes, you can see that we have orderTasks, we have productTasks, and we have
and it won't affect the other, etc, etc, etc. If I list our routes, you can see that we have order tasks, we have product tasks, and we have shipment tasks. And those are all loaded from different places. Now you might be thinking, this is a lot of extra work. And it is. It is some extra work. But I also want you to remember a few things. First of all, every time that you deviate from the standard structure, you're going to have some extra manual work. Secondly, you're not going to be doing that all the time. Sure, on this course, we're building everything at once, but in real life, you usually work within a module, then you create another one. And once you've done this, you're not doing it again. It's a one-time thing. And thirdly,
you usually work within a module, then you create another one. And once you've done this, you're not doing it again. It's a one-time thing. And thirdly, you don't have to do this for everything. If you have an existing app, maybe you don't need or you don't want to migrate everything to modules. You can build a specific new feature with the modules, or if you have some pieces on your code base that you think would fit well as a module, you can do that. You don't have to migrate everything. You don't have to do everything this way. But I can tell from experience, in real life, this is not a really big hassle. You do it once, you don't do it frequently, it's not a problem whatsoever. As you can see, I also went ahead and I moved all the migrations
You do it once, you don't do it frequently, it's not a problem whatsoever. As you can see, I also went ahead and I moved all the migrations to their respective modules. So we have the orders table here, we have the products table and the cart items table within the Product module, and we have the shipments table within the Shipment module. If we run php artisan migrate:fresh, you can see that everything is migrated as they should because we're telling Laravel where to look for migration files. And with that, we conclude this lesson. This is pretty much the basics for service providers. We're going to come back to them again in the future. They're very powerful. We're going to rely on a lot of them.
This is pretty much the basics for service providers. We're going to come back to them again in the future. They're very powerful. We're going to rely on a lot of them. But this is pretty much the basics. It's how you give life to the modules. Alright, I hope you guys enjoyed this lesson, and I'll see you in the next one. Bye-bye.
