Whenever I add additional logic to Eloquent models, I end up having to make it a static
method (i.e. less than ideal) in order to call it from the model's facade. I've tried searching a lot on how to do this the proper way and pretty much all results talk about creating methods that return portions of a Query Builder interface. I'm trying to figure out how to add methods that can return anything and be called using the model's facade.
For example, lets say I have a model called Car
and want to get them all:
$cars = Car::all();
Great, except for now, let's say I want to sort the result into a multidimensional array by make so my result may look like this:
$cars = array(
'Ford' => array(
'F-150' => '...',
'Escape' => '...',
),
'Honda' => array(
'Accord' => '...',
'Civic' => '...',
),
);
Taking that theoretical example, I am tempted to create a method that can be called like:
$cars = Car::getAllSortedByMake();
For a moment, lets forget the terrible method name and the fact that it is tightly coupled to the data structure. If I make a method like this in the model:
public function getAllSortedByMake()
{
// Process and return resulting array
return array('...');
}
And finally call it in my controller, I will get this Exception thrown:
Non-static method Car::getAllSortedByMake() should not be called statically, assuming $this from incompatible context
TL;DR: How can I add custom functionality that makes sense to be in the model without making it a static method and call it using the model's facade?
Edit:
This is a theoretical example. Perhaps a rephrase of the question would make more sense. Why are certain non-static methods such as all()
or which()
available on the facade of an Eloquent model, but not additional methods added into the model? This means that the __call
magic method is being used, but how can I make it recognize my own functions in the model?
Probably a better example over the "sorting" is if I needed to run an calculation or algorithm on a piece of data:
$validSPG = Chemical::isValidSpecificGravity(-1.43);
To me, it makes sense for something like that to be in the model as it is domain specific.
Actually you can extend Eloquent Builder and put custom methods there.
Steps to extend builder :
1.Create custom builder
2.Add this method to your base model :
3.Run query with methods inside your custom builder :
for above code generated mysql query will be :
PS:
First Laurence example is code more suitable for you repository not for model, but also you can't pipe more methods with this approach :
Second Laurence example is event worst.
Many people suggest using scopes for extend laravel builder but that is actually bad solution because scopes are isolated by eloquent builder and you won't get the same query with same commands inside vs outside scope. I proposed PR for change whether scopes should be isolated but Taylor ignored me.
More explanation : For example if you have scopes like this one :
and two eloquent queries :
vs
Generated queries would be :
vs
on first sight queries look the same but there are not. For this simple query maybe it does not matter but for complicated queries it does, so please don't use scopes for extending builder :)
If you look at the Laravel Core - all() is actually a static function
You have two options:
or
Both will allow you to do
for better dynamic code, rather than using Model class name "Car",
just use "static" or "self"