تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Introducing Laravel Macros0:00

If you've worked with Laravel for any sufficient period of time, you've likely come across this concept of macros. Maybe you visit a class like Laravel's collection class, and you'll see a trait, use Macroable. And in fact, if we were to search the Laravel vendor directory for Macroable, you can see a list of all the different classes that reference it. So we can see the RequestGuard, the SessionGuard, the CacheRepository, working with commands, ConsoleEvents, the FactoryBuilder, countless things. You'll see this goes on and on. Which means, littered throughout the entire Laravel codebase is this concept of macros. So let me give you an example.

Creating a Collection Macro0:36

Which means, littered throughout the entire Laravel codebase is this concept of macros. So let me give you an example. If we look at that Macroable trait, you're going to see a method here called, well, macro. Register a custom macro. Think of this as a way to dynamically extend the surface area of a given class. So if we're working with a Collection, let's pull that in, and because we've learned that class is Macroable, I can say, OK, register a new macro. I'm going to do this on the fly. And again, this means we're extending the API, we're extending the surface area. So we know there's methods like first and last, but maybe you want firstNth.

And again, this means we're extending the API, we're extending the surface area. So we know there's methods like first and last, but maybe you want firstNth. And that means give me the first x items from this collection. All right, so we'll pass a closure here. Now here's what's neat. In this closure, the this keyword is properly being bound to the collection instance. It's not normally what you would expect, right? But behind the scenes, Laravel sets this up for you. If in the old days, maybe you used jQuery, you would have found a similar thing where they hijack the this keyword to make it more useful to you.

If in the old days, maybe you used jQuery, you would have found a similar thing where they hijack the this keyword to make it more useful to you. So that means if I were to die and dump this, we would get a collection instance. Let's give it a shot. Let's collect some items, one, two, three, and then we'll call this new method firstNth. All right, let's give it a run. Sure enough, we get our collection. OK, so now, at this point, we need to accept how many we want to take. And then we could use something as simple as Array.slice.

OK, so now, at this point, we need to accept how many we want to take. And then we could use something as simple as Array.slice. So on the EliminateSupportCollection class, if you want to grab the array of underlying items, it's actually, where is it? It's a property called items. So let's say, using that array, I want to slice it and start at the beginning, and I want to take however many items is passed in. Let's return that. OK, so now, if we want to grab the first two items, we'll die and dump this, come back, give it a refresh, now we have an array of the very first two items.

Making Macros Chainable2:25

OK, so now, if we want to grab the first two items, we'll dd this, come back, give it a refresh, now we have an array of the very first two items. And if we add four, five, of course, you're still going to get one and two. Or we can take the first three or four. However, this is somewhat inconsistent. Often when working with Collection methods, you want them to be chainable, and you also want to return a new Collection. So we can do that by saying new static. And remember, in this context, static is going to refer to Collection. OK, so if we give that a refresh, now we have a Collection of the first nth items.

Registering in Service Providers2:53

And remember, in this context, static is going to refer to collection. OK, so if we give that a refresh, now we have a collection of the first nth items. All right, so if I want maybe the first two, now this is working exactly like we hoped. It's really simple, right? And this is a basic example of using a macro. Now, you might be wondering where should I put things like this. Well, you can put it here if you want. I'm not going to beat you up over that. But maybe more common would be to place it within either a dedicated service provider or the standard AppServiceProvider that ships with the new Laravel project.

But maybe more common would be to place it within either a dedicated ServiceProvider or the standard AppServiceProvider that ships with the new Laravel project. So you could place it right here. And one more time, I need to import these. And remember, for things like this, place it within the boot method. I find newcomers often get confused about what goes in register versus boot. Register is the beginning process where you want to register something into the service container. In our example, we want to use something that presumably has already been registered and configured.

In our example, we want to use something that presumably has already been registered and configured. So the boot method is what we want. Hope that makes sense. When booting up Laravel, it will filter through and call the register method for all of the packages you have pulled in or any service providers you've declared. And then once all of those have been registered, it'll do another pass through and call the boot method here once everything has been registered and is ready to work with. Okay. So now we've gotten it out of here.

Adding a Filesystem Macro4:12

Okay. So now we've gotten it out of here. But if I come back and give it a refresh, I'm still going to get the exact same thing. So good job. You made your first macro here. Now like I said, almost everything, so much of Laravel is macroable. So off the top of my head, let's go to the FileSystem class here. And once again, we see macroable. So if you're not familiar, this is the underlying class for Laravel's local storage. So if you want to put to a file or get a file or delete the file, all of that functionality

So if you're not familiar, this is the underlying class for Laravel's local storage. So if you want to put to a file or get a file or delete the file, all of that functionality is available. So one thing that might be useful is like a file make method. So I think there's file_puts, and this would create a new file if it doesn't already exist with the given contents. But maybe you want the equivalent of just create this file. I'm not putting anything in there. Just create the file. Well, if that or something similar, use your imagination, is not available, if you want,

Just create the file. Well, if that or something similar, use your imagination, is not available, if you want, you can add it on the fly. Let's give it a try. We'll go back to our AppServiceProvider. And now we're going to say fileSystem. Let's import that. And we're going to register a new macro called make. Now if I switch back, there is this put method. So it's kind of just an alias for that.

Now if I switch back, there is this put method. So it's kind of just an alias for that. So that means I could say this put to the given path. So we'll need to accept that, and then we'll just add an empty string there. And we'll return that. All right, there's another macro. Very simple, but you get the idea. So now, of course, I can have a FileSystem instance if I need to, or if I use the File facade, it's still going to delegate to that FileSystem class, of course. So now I can say File::make.

How Macroable Works5:44

facade, it's still going to delegate to that FileSystem class, of course. So now I can say file make. And how about within the current directory, we want a new .php file, something like that. All right, let's come back, give this a refresh. And now if we scroll back, you'll see that we have a new .php file within the current directory. So now let's finish up by doing a quick review of how this even works behind the scenes. Once again, I'm going to go back to my routes/web.php file, and let's create a new class here. Anything, it doesn't matter.

here. Anything, it doesn't matter. I want it to be macroable, so I'll say use macroable, and we'll pull that in. And we'll say our one method here, we'll return one. We're not working with anything specific here. We just want to show that we can extend to this. This is all we need here. So now I could say anything macro, and we have one here, so we'll just add another one. And we'll return two. Let's give it a shot.

And we'll return two. Let's give it a shot. So let's return a new instance of anything, and then we will call our new method on here. All right, come back, give this a refresh, and we do get two. So what does this mean? Well, you won't reach for it often, but in the situations, especially when you are distributing a package where you can't anticipate the functionality everyone will require, in those cases in particular, this can be incredibly useful. It gives them the ability to use your class, but also extend it precisely to their needs if it makes sense for their projects.

It gives them the ability to use your class, but also extend it precisely to their needs if it makes sense for their projects. And don't forget, in this case, this function is being bound to the current instance of anything. So that means here you could say return this one and two, you know, if you want to piggyback there. Okay, so now that you understand all of the functionality, yeah, let's finish up by reviewing the Macroable trait. If we take a look at the macro method, the first thing we see is we accept a name and the macro. So this would be the method name that you want to call, and this would be a handler,

the macro. So this would be the method name that you want to call, and this would be a handler, basically, a closure that will trigger the functionality. So when you call this method, all we're doing behind the scenes is storing it on the object, and you see it right up here. So that means for however many macros have been registered within a package service provider or your own service provider, it doesn't matter, we'll keep a static reference to all of those macros. Then, as you can imagine, when you call a method on your class, well, if it doesn't exist natively, so to speak, then we're going to check to see, well, is there a macro registered

Then, as you can imagine, when you call a method on your class, well, if it doesn't exist natively, so to speak, then we're going to check to see, well, is there a macro registered for it? And if so, we'll trigger that instead. All right, let's give it a shot. If we scroll down, here's when we call a static method, and then here's a standard method. So let's take a look. Now, remember earlier when we had anything class and we used macroable? As part of that, we've registered this magic handler here. So call, if you're not familiar, will be triggered if you try to call a method on your object

As part of that, we've registered this magic handler here. So __call, if you're not familiar, will be triggered if you try to call a method on your object that doesn't exist. So for example, if this method is not defined anywhere up here, then we will hit this method. So we are calling a method that is not defined. As arguments, it's going to accept the method name. In this case, doesNotExist, and then parameters would be anything you pass through here. All right, so the first thing we do is, well, we know there is no method defined on the class. So let's see, well, was there a macro defined in one of those service providers?

class. So let's see, well, was there a macro defined in one of those service providers? And if we scroll up, we can see we're just looking. Let's look in that macros array for one with this name, and let me know, is it set? Does it exist? All right. Well, if it doesn't exist, that means there's no method on this class that matches the method you called, and there's no macro that exists. So we say, sorry, bad method, call exception. We have no idea what you want us to do here.

So we say, sorry, bad method, call exception. We have no idea what you want us to do here. Nothing to call. Now, if there is a macro, then let's grab the handler. That would be the closure, the anonymous function that we pass when registering a new macro. If I switch back to AppServiceProvider, this would be the macro. All right. So now that we have that stored, we're going to do a quick check. Is it a closure or a function? If so, okay.

Is it a closure or a function? If so, okay. Well, let's take that function, and we're going to bind it to the current object. So this is a key thing to understand if you're trying to figure out, well, how within that closure was this referring to the object instance? How do you even get that to work? It's basic functionality provided out of the box with php and the Closure class. So we're saying that closure, I want to bind the $this keyword to this. And in this case, it's whatever object is macroable. So the Collection class in our example, or the FileSystem class in that other example.

And in this case, it's whatever object is macroable. So the Collection class in our example, or the FileSystem class in that other example. And that's it. That's all there is to it. So this will return the new closure. We're going to call that function, passing through the parameters, which will be destructured into function arguments. Otherwise, if it's not a closure, then we'll trigger it the normal way. Maybe for an invocable class or something like that. But yeah, that's all that's going on here.

Maybe for an invocable class or something like that. But yeah, that's all that's going on here. It's not that complicated. But when you think about it, it's an incredibly powerful thing. You'll find that Laravel makes use of this everywhere, even for first-party packages. This is a nice way for maybe something like Cachier or Laravel Scout to hook into the main API you're used to, but stack on some extra functionality that probably shouldn't be there by default. But if you're pulling in this new package, then it does make sense for it to be there. For example, I'll show you real quick, and then we're done for the day.

Macros in Laravel Scout11:04

But if you're pulling in this new package, then it does make sense for it to be there. For example, I'll show you real quick, and then we're done for the day. Let's require Laravel Scout. This adds a search API for Laravel. So if you want to hook into Algolia and have a really clean API for querying against it, Laravel Scout can be used for it. All right. So we have found the package. Let's take a look at that, Scout, and let's see, is there one in the service provider? We just have to find out where they register it.

Let's take a look at that, scout, and let's see, is there one in the service provider? We just have to find out where they register it. Let's do searchable. Here we go. Register searchable macros. Okay. So if we take a look at that, there we go. So it's taking the base collection, and that's what we were working with as well, Illuminate\Support\Collection, and it registers a macro called searchable. This is your way of saying, okay, the items in this collection should be searchable and

Support Collection, and it registers a macro called searchable. This is your way of saying, okay, the items in this Collection should be searchable and available on Algolia, or unsearchable. Let's go to searchable scope, see if there's one as well. Here we're adding a macro onto the Eloquent Builder. So if you want to perform an Eloquent query and then call searchable. So for example, Post where condition, whatever is returned from that set, we want those to be searchable. Well, again, something like that doesn't make sense out of the box. But if you pull in Laravel Scout, suddenly it does make sense.

Well, again, something like that doesn't make sense out of the box. But if you pull in Laravel Scout, suddenly it does make sense. So here's where it adds a macro, and then once again, the opposite. Finally, if we take a look at the ScoutBuilder class itself, it too is macroable if you need it. So now you've taken the red pill and you see how deep the rabbit hole goes. So yeah, don't be too crazy with this. Be thoughtful about it. But nonetheless, this is a very powerful, but surprisingly simple feature that ships with Laravel out of the box.

But nonetheless, this is a very powerful, but surprisingly simple feature that ships with Laravel out of the box.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟