In this tutorial, we will see how to implement search and filter functionality in Laravel with Inertia and Vue 3. For this section, we will use the Vue 3 SFCs method <script setup>
. First, you need to install and set up Laravel with Inertia and Vue 3.
Laravel Inetia Vue 3 Search Example
Create user controller.
php artisan make:controller UserController
UserController.php
<?php
namespace App\Http\Controllers;
// use Illuminate\Http\Request;
use Illuminate\Support\Facades\Request;
use Inertia\Inertia;
use App\Models\User;
class UserController extends Controller
{
public function index()
{
return Inertia::render(
'Users/Index',
[
'users' => User::query()
->when(Request::input('search'), function ($query, $search) {
$query->where('name', 'like', '%' . $search . '%')
->OrWhere('email', 'like', '%' . $search . '%');
})->paginate(8)
->withQueryString(),
]
);
}
}
routes/web.php
<?php
use App\Http\Controllers\UserController;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return Inertia::render('Welcome', [
'canLogin' => Route::has('login'),
'canRegister' => Route::has('register'),
'laravelVersion' => Application::VERSION,
'phpVersion' => PHP_VERSION,
]);
});
Route::get('/dashboard', function () {
return Inertia::render('Dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
Route::get('users',[UserController::class,'index'])->name('users');
require __DIR__.'/auth.php';
First you need to setup inertia vue 3 pagination, you can read below article.
Laravel 9 Inertia Vue 3 Pagination Example
Users/Index.vue
<script setup>
import { ref } from "vue";
import BreezeAuthenticatedLayout from "@/Layouts/Authenticated.vue";
import { Head } from "@inertiajs/inertia-vue3";
import { Link } from "@inertiajs/inertia-vue3";
import { Inertia } from "@inertiajs/inertia";
import { watch } from "vue";
import Pagination from "@/Components/Pagination.vue";
const props = defineProps({
users: {
type: Object,
default: () => ({}),
},
});
let search = ref('');
watch(search, (value) => {
Inertia.get(
"/users",
{ search: value },
{
preserveState: true,
}
);
});
</script>
<template>
<Head title="Users" />
<BreezeAuthenticatedLayout>
<template #header>
<h2 class="text-xl font-semibold leading-tight text-gray-800">
Users
</h2>
</template>
<div class="py-12">
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
<div class="overflow-hidden bg-white shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<div class="mb-2">
<input
type="text"
v-model="search"
placeholder="Search..."
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-60 p-2.5 "
/>
</div>
<div
class="relative overflow-x-auto shadow-md sm:rounded-lg"
>
<table
class="w-full text-sm text-left text-gray-500 dark:text-gray-400"
>
<thead
class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400"
>
<tr>
<th scope="col" class="px-6 py-3">#</th>
<th scope="col" class="px-6 py-3">
Name
</th>
<th scope="col" class="px-6 py-3">
Email
</th>
</tr>
</thead>
<tbody>
<tr
v-for="user in users.data"
:key="user.id"
class="bg-white border-b dark:bg-gray-800 dark:border-gray-700"
>
<th
scope="row"
class="px-6 py-4 font-medium text-gray-900 dark:text-white whitespace-nowrap"
>
{{ user.id }}
</th>
<th
scope="row"
class="px-6 py-4 font-medium text-gray-900 dark:text-white whitespace-nowrap"
>
{{ user.name }}
</th>
<td class="px-6 py-4">
{{ user.email }}
</td>
</tr>
</tbody>
</table>
</div>
<Pagination :data="users"/>
</div>
</div>
</div>
</div>
</BreezeAuthenticatedLayout>
</template>
Laravel Inetia Vue 3 Search Filter (recommended)
UserController.php
<?php
namespace App\Http\Controllers;
// use Illuminate\Http\Request;
use Illuminate\Support\Facades\Request;
use Inertia\Inertia;
use App\Models\User;
class UserController extends Controller
{
public function index()
{
return Inertia::render('Users/Index',
[
'users' => User::query()
->when(Request::input('search'),function($query, $search) {
$query->where('name','like','%'.$search.'%')
->OrWhere('email','like','%'.$search.'%');
})->paginate(6)
->withQueryString(),
'filters' => Request::only(['search'])
]);
}
}
Users/Index.vue
<script setup>
import { ref } from "vue";
import BreezeAuthenticatedLayout from "@/Layouts/Authenticated.vue";
import { Head } from "@inertiajs/inertia-vue3";
import { Link } from "@inertiajs/inertia-vue3";
import { Inertia } from "@inertiajs/inertia";
import { watch } from "vue";
import Pagination from "@/Components/Pagination.vue";
const props = defineProps({
users: {
type: Object,
default: () => ({}),
},
filters: {
type: Object,
default: () => ({}),
},
});
// pass filters in search
let search = ref(props.filters.search);
watch(search, (value) => {
Inertia.get(
"/users",
{ search: value },
{
preserveState: true,
replace: true,
}
);
});
</script>
<template>
<Head title="Users" />
<BreezeAuthenticatedLayout>
<template #header>
<h2 class="text-xl font-semibold leading-tight text-gray-800">
Users
</h2>
</template>
<div class="py-12">
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
<div class="overflow-hidden bg-white shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<div class="mb-2">
<input
type="text"
v-model="search"
placeholder="Search..."
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-60 p-2.5"
/>
</div>
<div
class="relative overflow-x-auto shadow-md sm:rounded-lg"
>
<table
class="w-full text-sm text-left text-gray-500 dark:text-gray-400"
>
<thead
class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400"
>
<tr>
<th scope="col" class="px-6 py-3">#</th>
<th scope="col" class="px-6 py-3">
Name
</th>
<th scope="col" class="px-6 py-3">
Email
</th>
</tr>
</thead>
<tbody>
<tr
v-for="user in users.data"
:key="user.id"
class="bg-white border-b dark:bg-gray-800 dark:border-gray-700"
>
<th
scope="row"
class="px-6 py-4 font-medium text-gray-900 dark:text-white whitespace-nowrap"
>
{{ user.id }}
</th>
<th
scope="row"
class="px-6 py-4 font-medium text-gray-900 dark:text-white whitespace-nowrap"
>
{{ user.name }}
</th>
<td class="px-6 py-4">
{{ user.email }}
</td>
</tr>
</tbody>
</table>
</div>
<Pagination :data="users" />
</div>
</div>
</div>
</div>
</BreezeAuthenticatedLayout>
</template>