Eloquent under the hood: where is the where method?
Sep 3, 2022
When you look through the source code for the Model class, you might be surprised to find that the where
method (along with other methods used to create queries) is nowhere to be found.
How then, can you call where
as if it was a static method defined on the model class? What is this magic?
YourModel::where()
// where is ☝️ this defined? 🤔
Is it magic?
In a way, you could say it's magic. Under the hood, Laravel uses something called magic methods.
From the documentation:
Magic methods are special methods which override PHP's default's action when certain actions are performed on an object.
In this article, I'm going to focus on two of the magic methods offered by PHP: __call
and __callStatic
.
But before that, let's talk about where is the where
method actually defined. Since it doesn't come out of thin air.
Where is the where?
If the where
method is not defined in the Model
class, where is it?
If you keep digging through the Laravel framework code you will find it hiding in the Eloquent query builder class:
namespace Illuminate\Database\Eloquent;
//...
class Builder implements BuilderContract
{
// ...
/**
* Add a basic where clause to the query.
*
* @param \Closure|string|array $column
* @param mixed $operator
* @param mixed $value
* @param string $boolean
* @return $this
*/
public function where($column, $operator = null, $value = null, $boolean = 'and')
{
// ...
}
//...
}
But, how does that function get executed when we statically call where
on a model?
That's where __call
and __callStatic
come in.
The magic methods
These two magic methods are very similar. Their function is almost the same.
When the __call
method is defined in a class, it will be triggered when you try to call a method that doesn't exist on the instance of that class.
That might sound confusing, so here is an example:
class SomeObject
{
public function __call($name, $arguments)
{
echo "$name 🔥 " . implode(', ', $arguments) . "\n";
}
}
$obj = new SomeObject();
$obj->someMethod('is fire'); // prints "someMethod 🔥 is fire
As you can see above, the __call
method receives two parameters.
- The name of the method called (in this case
someMethod
) - An array contained the arguments passed to the method (in this case
["is fire"]
)
The __callStatic
method works exactly the same, except it gets triggered when you try to call an undefined static method.
<?php
class SomeObject
{
public static function __callStatic($name, $arguments)
{
echo "$name 🪄 " . implode(', ', $arguments) . "\n";
}
}
SomeObject::someOtherMethod("is magic"); // prints "someOtherMethod 🪄 is magic"
Back to the model class
In the Model
class, Laravel uses the __callStatic
method, and this gets triggered when we call where
statically in our models:
abstract class Model
{
//...
public static function __callStatic($method, $parameters)
{
return (new static)->$method(...$parameters);
}
//...
}
// That means that this 👇
User::where('is_active', '=', true);
(new User())->where('is_active', '=', true);
// is the same as this ☝️
From there, Laravel creates a new instance of our model class and calls the where
method on it, but this time it's called on the instance, so it's not called as a static method anymore.
That means that the __call
method gets triggered since there is no where
method defined in the Model
class.
abstract class Model
{
//...
public function __call($method, $parameters)
{
// Some other checks..
// newQuery() returns a new eloquent builder
// with this instance as the model 👇
return $this->forwardCallTo($this->newQuery(), $method, $parameters);
}
protected function forwardCallTo($object, $method, $parameters)
{
try {
return $object->{$method}(...$parameters);
} catch (Error|BadMethodCallException $e) {
// Error handling
}
}
//...
}
// That means that this 👇
User::where('is_active', '=', true);
(new User())->newQuery()->where('is_active', '=', true)
// is the same as this ☝️
In that __call
method, Laravel makes a call to the method newQuery()
. This method creates an instance of Eloquent query builder, the same class I mentioned previously where the where
is actually defined (🤯).
And then forwards the call to that instance. So in short, when you call the where
method on your Eloquent model, Laravel makes use of these magic methods to forward that call to the Eloquent query builder class.
Conclusion
Now you know a little more about how Eloquent works under the hood. In my experience, knowing how the framework works under the hood can make debugging an easier task. So next time you use the where
on a model. Remember, you are actually calling where
on the Eloquent builder class.
If you would like to know how other parts of the framework work under the hood or have any other questions, send me a message on Twitter or write me an email at [email protected]. I'm happy to help.
You might also like