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

soleh's avatar
Level 1

Excel filter functionality clone using Laravel 11

Hello Laracasts Community,

I'm working on a project using Laravel 11 with Inertia and React.js on the front end. I have a Project model with the following fillable properties:

$fillable = ['name', 'description', 'due_date', 'priority',  'created_by', 'updated_by'];

I have an index route that displays all projects in an HTML table. I want to implement Excel-like column filter functionality. Such that, when a user clicks on any column header, a filter menu should appear, displaying all unique values in that column as check boxes. The check boxes for values currently present in the data should be checked, while others should be unchecked. The user should be able to apply the filter to show only the selected values.

How can I achieve this efficiently, avoiding performance issues? Any guidance or examples would be greatly appreciated.

Thank you!

0 likes
3 replies
soleh's avatar
Level 1

@martinbean Here is what i have so far, I created a post route "project/get-filter-values", with payload {key and context}, where key is the name of the column which we want the unique values for, context is basically an object with keys as column names and value and the currently selected values by the user, I am using this to apply filters from other columns as well, an example value context will be as follows

{
  "context": {
    "status": {
      "items": [
        "completed",
        "in_progress"
      ]
    }
  }
}

on the controller i run following query

$payload = $request->validate([
            'key'=>'required'
        ]);
        
        $query = Project::query();
        $respone;
        if($request['context']){
            // Loop through the filter items context and apply filters
            foreach ($request['context'] as $column => $filter) {
                if ($column !== $payload['key'] && isset($filter['items'])) {
                    $query->whereNotIn($column, $filter['items']);
                }
            }
            $response = $query->select($payload['key'])->distinct()->get();
        }
        else{
            $response = Project::select($payload['key'])->distinct()->get();
        }
        return response()->json($response);

I created another post route as below

Route::post('project', [ProjectController::class, 'index'])->name('project.filter');

Which basically calls the index method of the controller, on the controller i have following logic

public function index(Request $request)
    {
        
        $query = Project::query();
        $respone = [];
        $page = 1;
        if($request->page) $page = $request->page;
        if($request['context']){
            foreach ($request['context'] as $column => $filter) {
                if (isset($filter['items'])) {
                    $query->whereNotIn($column, $filter['items']);
                }
            }
            $response = $query->paginate(10, ['*'], 'page', $page)->onEachSide(1);
        }
        else{
            $response = $query->paginate(100, ['*'], 'page', $page)->onEachSide(1);
        }

       	return inertia('Project/Index', [
            "projects" => ProjectResource::collection($response),
            "context" => $request['context']
        ]);

    }

i am using whereNotIn assuming that initially, every project is displayed hence all checkboxes in the filter menu are checked, when user de-selects few values, it means user wants to see all project but the one deselected.

On the react side, i maintain this context object as a react state and pass it to the request.

However i have following doubts:

  1. What if user deselects all the values and just selects one value, in that case the whereNotIn will receive a big array and query might fail.
  2. is this an efficient way? wanted to get ideas from the community
  3. How to make it persistant, such that if user refreshes page, previously applied filter should reapply automatically
  4. How to make the pagination work, because right now, if click second page, it makes a get request hence all filters are lost

Please or to participate in this conversation.