Skip to main content

Black Friday 2025! Only until December 1st: coupon FRIDAY25 for 40% off Yearly/Lifetime membership!

Read more here

Tours Filtering and Ordering

Premium
12:29

Now it's time to take care of the second part of the client's description of the Tours List: filtering and ordering.


Task Description From Client

  1. Users can filter (search) the results by priceFrom, priceTo, dateFrom (from that startingDate) and dateTo (until that startingDate).
  2. User can sort the list by price asc and desc. They will always be sorted, after every additional user-provided filter, by startingDate asc.

Filtering Tours: Eloquent when()

First, let's take care of the filter.

Here's our Controller at the moment:

app/Http/Controllers/Api/V1/TourController.php:

public function index(Travel $travel)
{
$tours = $travel->tours()
->orderBy('starting_date')
->paginate();
 
return TourResource::collection($tours);
}

And our goal is to process the endpoint like this:

/api/v1/travels/{slug}/tours?priceFrom=123&priceTo=456&dateFrom=2023-06-01&dateTo=2023-07-01

So, we have four possible parameters:

  • priceFrom
  • priceTo
  • dateFrom
  • dateTo

And all of them are optional, so there may be only one of them passed, or all four.

So, our goal is to build a dynamic query based on different request() parameters.

We will add a Request $request parameter, which will be auto-resolved by Laravel and contain all the GET parameters.

And then, we will use the Eloquent method when(), which has two parameters:

  • a condition
  • and how to transform the query in case of that condition is true
use Illuminate\Http\Request;
 
class TourController extends Controller
{
public function index(Travel $travel, Request $request)
{
$tours = $travel->tours()
->when($request->priceFrom, function ($query) use ($request) {
$query->where('price', '>=', $request->priceFrom * 100);
})
->when($request->priceTo, function ($query) use ($request) {
$query->where('price', '<=', $request->priceTo * 100);
})
->when($request->dateFrom, function ($query) use ($request) {
$query->where('starting_date', '>=', $request->dateFrom);
})
->when($request->dateTo, function ($query) use ($request) {
$query->where('starting_date', '<=', $request->dateTo);
})
->orderBy('starting_date')
->paginate();
 
return TourResource::collection($tours);
}
}

You can read more about the when() method in...

The Full Lesson is Only for Premium Members

Want to access all of our courses? (31 h 16 min)

You also get:

55 courses
Premium tutorials
Access to repositories
Private Discord
Get Premium for $129/year or $29/month

Already a member? Login here

Comments & Discussion

C
campaigncenter ✓ Link copied!

FAILED Tests\Feature\ToursListTest > tour list filters by staring date correctly
Failed asserting that actual size 1 matches expected size 2 at:

$response = $this->get($endpoint.'?dateFrom='.now());
$response->assertJsonCount(2, 'data');

If someone else is having this test failing it is because now() is creating a complete datetime string and not just YYYY-mm-dd and by the time my/your computer gets to return now() one or more seconds have passed and $earlierTour entry no longer gets included. Solution is to use variable like $earlyStartingDate = date('Y-m-d', now()) and use it throughout the test function or modifying $request->dateFrom in TourController.

L
Lokith ✓ Link copied!

We can use now()->format('Y-m-d') instead of $earlyStartingDate = date('Y-m-d', now()). I also encountered this test failure.

SS
Sven Spruijt ✓ Link copied!

Encountered the same problem, solved with now()->toDateString()

H
hrsa ✓ Link copied!

I would encode a sorting parameter by default, for the ease of use :

->when($request->sortBy, function ($query) use ($request) {
$query->orderBy($request->sortBy, $request->sortOrder ?? 'asc');
})

This way the user wouldn't get an error if he forgets to specify sortOrder.

And by the way, if any of you have a problem with getting the validation error messages in Postman - don't forget to modify "Accept" in Headers. It should be application/json, not */*.