Where Method Mystery0:00
If I bring back php, artisan, tinker, we now know what will happen if I do something like Post::find and then give it an ID. But what if we instead begin with a where clause? Well this is sort of different. In your model class, yes there is a find method, but you're not going to see a where method. So where the heck is the where method? That's the subject of this episode. Alright I'm going to switch over to PHPStorm, and just to prove it to you, well we go to our Post model, and we see nothing. And if we go to the parent class, still there is no where method.
Introducing Magic Methods0:32
our Post model, and we see nothing. And if we go to the parent class, still there is no where method. So what's going on here? Why is this the case, but I'm still able to do things like this, where $id is greater than or equal to 160, and give me all of the results? Why is this working when we've just established that there's no where method? Okay well here's the answer. We are using magic methods. So if I scroll all the way to the bottom, and if I move up, you'll see these two magic methods, call and callStatic.
So if I scroll all the way to the bottom, and if I move up, you'll see these two magic methods, call and callStatic. If you're not familiar, these two methods will be triggered automatically if some kind of condition is met. So in translation, well if you try to call a static method that is not defined on a class, well it will look to see if you have defined a callStatic method. And if you have, it'll call that. Let me give you a quick demonstration. I'm going to switch over to this little tool called Code Runner. I often use it when I just want to play around with some kind of php thing, just to make
Demonstrating __callStatic1:34
I'm going to switch over to this little tool called Code Runner. I often use it when I just want to play around with some kind of php thing, just to make sure I understand it well. It gives you an easy way to execute php code. Okay so let's imagine that we have a Foo class, and we have a static method on it. $var, and we're just going to var_dump $var if that does get called. Okay so that means I can say Foo::$var, and if I run this, sure enough, $var is echoed out. But now, if I were to remove that, and we run it again, of course we're going to get a fatal error.
But now, if I were to remove that, and we run it again, of course we're going to get a fatal error. That makes sense. But now, if we create that magic method, static function callStatic, and as arguments, that will receive the method you're trying to call, as well as an array of parameters. So that means method, in this case, would be equal to var, and parameters would be an empty array, but if you pass through some values, they would be inserted here. And I'll show you that in just a minute. But anyways, if I now just say var_dump(called), you'll see that we triggered this now, and we prevented the error.
But anyways, if I now just say var_dump called, you'll see that we triggered this now, and we prevented the error. Let's run it, and there you go. No fatal error, instead we caught it with the __callStatic method. But now, just to drill this in, this method here will only be triggered on the condition that you don't have another static method with this name. So if we try it, var, and we just var_dump, it exists. And now, if we run it, because we already have a method of that name, this will never be touched. Okay, so now that we're all on the same page, we can see that when we do post where, well,
Tracing Post::where Dispatch3:13
be touched. Okay, so now that we're all on the same page, we can see that when we do post where, well, a where method is not defined, so we hit the callStatic method. Now what happens here? Well, we create a new instance, new model, and then we try to call a method on the model. That's all we're doing here. call_user_func_array means we're calling a function or a method, and for all of the arguments that the method accepts, well, we are going to pass that through as an array. So if parameters has three items in it, then each item in that array will be translated to one argument from the method.
So if parameters has three items in it, then each item in that array will be translated to one argument from the method. Okay, so basically what this means is, well, create a new model and then call the method passed here. So if we said Post::where, where is the method? So we're essentially saying this, and we are then extracting the array of parameters into an argument list. All right, so I hope that part makes sense. And real quick, when you pass an array as the first argument here, you're saying I want to call a method on an instance of an object.
And real quick, when you pass an array as the first argument here, you're saying I want to call a method on an instance of an object. So if you didn't pass that here, you would just call a general global function. But in our case, we want to trigger a method. All right, so yeah, we get this part. This all makes sense. But think about it. At this point, we are still trying to call a where method on the model class, and we've already established that there is no where method. So what happens then?
already established that there is no where method. So what happens then? Well, in that case, if I scroll up, we're going to hit another magic method. And this one is just __call. And it's basically the exact same thing. You have call static to catch any static method calls that have not been defined. And then you also have __call for any regular methods that have not been defined. Okay, so now, once again, when we do Post where, we are ultimately hitting this method. So what do we do here?
Okay, so now, once again, when we do post where, we are ultimately hitting this method. So what do we do here? Well, first, we do a quick check. Is the user just trying to increment some value in a column? So for example, you could do post increment views or something. And that'll just add one to whatever the number in that column is. It's pretty easy and very intuitive. So we do a quick check for that. Otherwise, well, we do the same thing as we did before. We get a new builder instance.
Otherwise, well, we do the same thing as we did before. We get a new Builder instance. Just to refresh your memory, if you didn't watch the last video, when you call newQuery, well, ultimately, we set up a new Eloquent builder class, and that object is what gets returned there. So if we return to our call method, we set up our new builder object, and then we call a method on that object. So that means, once again, when you do this, the method variable is now equal to where, which means we're trying to call a where method on the builder object. And then, once again, we pass through an array of parameters that will be translated into
Builder Where Implementation6:16
which means we're trying to call a where method on the builder object. And then, once again, we pass through an array of parameters that will be translated into the arguments that the method accepts. So this means, if we take a look at the builder class, open that up, if we go to the where method here, now we can see what's happening. When I say post where, it first hits a call static magic method, and then it moves up and hits a call magic method, and then finally there, we trigger this method on the builder class. Now, what happens here? Well, we do a quick check.
Now, what happens here? Well, we do a quick check. Is the column actually a closure? Which means, if you didn't know this, you can do things like this, where function query, and then you could say query where foo is bar, or whatever. You may not have known that. You can pass a closure to the where method. Now, the reason why this is possible is because behind the scenes we check for it. Is this column equal to a closure? If so, well in that case, let's get our query, and then trigger the user’s function while.
Is this column equal to a closure? If so, well in that case, let's get our query, and then trigger the user's function while passing through the query. And then we've built up our query, so we just go ahead and add that to the query builder. Okay, so that handles the condition if you pass a closure. But otherwise, what if you don't pass a closure? Well in that case, we just defer to this query, and if you can't remember, that is the query builder object that we talked about in the very last episode. In fact, this class accepts it as a dependency here. So if I go back, we're just calling a where method on the query builder.
In fact, this class accepts it as a dependency here. So if I go back, we're just calling a where method on the query builder. So that's why I wanted to dedicate a full episode to this, because it can be a little confusing at first. It goes through a number of little pipes before it gets to where it needs to be. But of course the benefit to all of that is you get a really nice intuitive API. So this means in translation, when you run Post where, and then you provide your columns, ultimately we are triggering a where method on the query builder class. Now like I noted in the last video, we're going to dedicate sort of a whole lesson to talking about how the query builder works.
Query Builder Where Options8:32
Now like I noted in the last video, we're going to dedicate sort of a whole lesson to talking about how the query builder works. But if you want a quick peek at what's happening with the where method, well right off the bat, if you look at the source, you might have learned something new. If is_array column, which means if you want, you can pass an array to the where method. This allows us to do things like this. Find me any Post where the foo column is equal to that, and the baz column is equal to this. Now if you find anything that matches that criteria, then do whatever I need you to here. You may not have known that you could do that. Instead, maybe you were doing something like this, where foo is bar, and then you just
You may not have known that you could do that. Instead, maybe you were doing something like this, where foo is bar, and then you just stack it again, where baz is biz, and delete. Maybe this is what you're doing currently, but instead, just pass an array. That's a cleaner way to go about that. So you can see right here, we're just filtering through all of the items in that array, and are doing it manually for you. Okay, and then finally, what is this stuff going on here? Well you may know this as well. You can say Post::where('id', 1).
Well you may know this as well. You can say Post where id is equal to 1. Yes, you can do that, and that allows you to do things like not equal to, or greater than, or greater than or equal to. So you can do that, but you also probably know that you can do this as well. Where post_id is 1, and in this case, the equal sign is implied. Well, in order for those things to work, we have to write that functionality of course. So that's exactly what's happening here. If the number of arguments that the user passes is 2, well, they're just passing a column and a value, and they omitted the operator.
If the number of arguments that the user passes is 2, well, they're just passing a column and a value, and they omitted the operator. They just excluded that. So if we want to allow them to do that, and that makes the API just a little bit more easy and intuitive, well, then we just have to reassign a couple things. In that case, the actual value variable, well we should look for that within what was passed through as the operator, because they set the value as the second argument in this case. Otherwise, if they did include the operator, we'll do a quick check to make sure that they passed a valid operator, and if they didn't, we'll throw an exception.
Otherwise, if they did include the operator, we'll do a quick check to make sure that they passed a valid operator, and if they didn't, we'll throw an exception. But then otherwise, we can continue on adding the nested class. And actually there's a number of lines here, and we're sort of getting off task, so I don't want to cover it too much. But if you're curious, just read the comments, and most of this should make basic sense. Here we're checking to see if the User passed a closure to the where method. And then if we scroll down further, we'll see if the actual value, so once again, Post::where('foo', '=', 'bar'), well, it's possible for the value argument to be a closure itself. And that would be for situations where you have some kind of subselect.
where foo equals bar, well, it's possible for the value argument to be a closure itself. And that would be for situations where you have some kind of subselect. So that might be something else that you didn't know you could do in Eloquent. There really is a lot of flexibility here. And then as we finish up, we check to see if value is null. So if we say where foo equals null, well, in that case, they may not realize that there's a whereNull method provided out of the box. So we will just call that for them. And then finally, if we get to this point, as the notes show, we are just dealing with a simple query and can attach the bindings accordingly.
And then finally, if we get to this point, as the notes show, we are just dealing with a simple query and can attach the bindings accordingly. But like I said, we kind of went fast on that part. A full lesson is going to be dedicated to the query builder itself. So mostly for the purposes of this video, we were just curious about what the heck happens when you say Post::where. Because we couldn't find a where method on the model class, we had to do a little digging to figure out how these things work behind the scenes.
