phplaraveleloquentmodel

How do you move the function definition for the ->when() function in Laravel Eloquent Models?


I'm building a way to organize Items based on different requests:

$items = Item::query()
    ->where("type", $itemType)
    ->when($mode, function (Builder $query, $mode) {
        switch ($mode) {
            case "TopFavorites":
                return $query->orderByDesc('number_favorited');
            case "BestSelling":
                return $query->orderByDesc('number_sold');
            case "RecentlyUpdated":
                return $query->orderByDesc('updated_at');
            case "ForSale":
                return $query->where("onSale", true);
            case "Offsale":
                return $query->where("onSale", false);
        }
    })
    ->get();

I'm wondering if you can move the function in when to an external function in the controller class. I tried doing so however it seems the $query variable is not carried over even if you add it as an argument.

private function whenModeQuery(Builder $query, string $mode) {
    switch ($mode) {
        case "TopFavorites":
            return $query->orderByDesc('number_favorited');
        case "BestSelling":
            return $query->orderByDesc('number_sold');
        case "RecentlyUpdated":
            return $query->orderByDesc('updated_at');
        case "ForSale":
            return $query->where("onSale", true);
        case "Offsale":
            return $query->where("onSale", false);
    }
}
$items = Item::query()
    ->where("type", $itemType)
    ->when($mode, $this->whenModeQuery($query, $mode))
    ->get();

The code above gives me a Undefined variable $query error.


Solution

  • As apokryfos pointed out in the comment, the issue is that you're calling the method immediately instead of passing it as a function reference to when(). The correct way is:

    $items = Item::query()
        ->where("type", $itemType)
        ->when($mode, function (Builder $query, $mode) {
            return $this->whenModeQuery($query, $mode);
        })
        ->get();
    

    Reference: https://laravel.com/docs/12.x/collections#method-when

    Without any additional internal code, it can also be shortened:

    $items = Item::query()
        ->where("type", $itemType)
        ->when($mode, fn (Builder $query) => $this->whenModeQuery($query, $mode))
        ->get();
    

    Reference: How to use arrow functions in PHP?