Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ajsmith_codes's avatar

Combine query with collection.

I have created filters to search based on the input. For example, if the request has 'number', it uses the Number class, which implements Filter.

The second thing I have is a search where the response is returned through a JsonResource. I'm using this in a Vue component.

I want to combine the two. Where inside of the filter do I implement the JsonResource?


namespace App\OrderSearch;

use App\Models\Order\Order;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;

class OrderSearch
{

    public static function apply(Request $filters)
    {
        $query = static::applyDecoratorsFromRequest($filters, (new Order())->newQuery());

        return static::getResults($query);
    }

    private static function applyDecoratorsFromRequest(Request $request, Builder $query)
    {
        foreach ($request->all() as $filterName => $value) {

            $decorator = static::createFilterDecorator($filterName);

            if (static::isValidDecorator($decorator)) {
                $query = $decorator::apply($query, $value);
            }

        }
        return $query;
    }

    private static function createFilterDecorator($name)
    {
        return __NAMESPACE__ . '\Filters\' . ucfirst($name);
    }

    private static function isValidDecorator($decorator)
    {
        return class_exists($decorator);
    }

    private static function getResults(Builder $query)
    {

        return $query->get();
    }
}
namespace App\Http\Controllers\Order;

use App\Http\Controllers\Controller;
use App\Http\Resources\OrderResource;
use App\Models\Order\Order;
use App\OrderSearch\OrderSearch;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;

class SearchController extends Controller
{

    protected $order;

    public function __construct(Order $order)
    {
        $this->order = $order;
    }

    public function filter(Request $request)
    {


        return OrderSearch::apply($request);
        
    }
}
namespace App\OrderSearch\Filters;

use Illuminate\Database\Eloquent\Builder;


interface Filter
{


    /**
     * @param Builder $builder
     * @param $value
     * @return mixed
     */
    public static function apply(Builder $builder, $value);

}
namespace App\OrderSearch\Filters;

use Illuminate\Database\Eloquent\Builder;

class Number implements Filter
{
    public static function apply(Builder $builder, $value)
    {
        return $builder->where('number', 'LIKE', '%' . $value . '%');
    }

}
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Carbon;

class OrderResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param \Illuminate\Http\Request $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'number' => $this->number,
            'requested_ship_date' => Carbon::parse($this->requested_ship_date)->format('m-d-Y'),
            'status' => $this->status,
        ];
    }
}

0 likes
7 replies
tykus's avatar
tykus
Best Answer
Level 104

Don't implement the JsonResource inside the OrderSearch / Filter instances; just return the Resource (collection) as the Controller response:

public function filter(Request $request)
{
    return OrderResource::collection(OrderSearch::apply($request));
}
ajsmith_codes's avatar

I knew there had to be an easy answer, but apparently, I'm not yet experienced enough to figure it out. Thanks so much!

ajsmith_codes's avatar

@tykus How would I paginate the results? I have tried these changes with no luck:

 return $builder->where('number', 'LIKE', '%' . $value . '%')->orderBy('number')->paginate(5);

Also tried this:

    private static function getResults(Builder $query)
    {

        return $query->get();
    }
tykus's avatar

You could decide to change things up and return a Builder instance from OrderSearch class; then you can decide how you want the results in the Controller action (whether you paginate or get). Failing that, you could do something about all of those static methods in OrderSearch, and fluently call another method on the OrderSearch class that sets a piece of state what determines if getResults does a get or paginate method call to complete the query.

ajsmith_codes's avatar

When you say "return a builder instance from OrderSearch", what would that look like?

tykus's avatar
public static function apply(Request $filters)
{
	return static::applyDecoratorsFromRequest($filters, (new Order())->newQuery());
}

Then you calling controller action will decide how to complete the query, e.g.:

public function filter(Request $request)
{
    	return OrderResource::collection(
		OrderSearch::apply($request)->paginate() // or `get` or `first`
	);
}

Please or to participate in this conversation.