Simple search in Laravel 9 app using Alpine.js

In this tutorial we’ll build simple search / filter capability using Alpine.js in a Laravel 9 app, so that when you start typing in the search input field, the data presented in the data table is filtered in accordance with the input.

Laravel 9.x requires a minimum PHP version of 8.0. This tutorial assumes that you are using Linux, MacOS or WSL on Windows and Node.js, Composer and PHP 8 along with required modules i.e bcmatch, sqlite, mbstring, xml, zip, gd, mcrypt are properly installed. Also make sure that SQLite is installed as we’ll be using SQLite to keep things simple and quick; you may use MySQL or PostgreSQL as you like, however make sure the corresponding PHP module is installed too.

Using Composer, enter following command in terminal to install Laravel 9;

composer create-project laravel/laravel app 9.*

Now go inside newly created app directory by executing;

cd app

A simple way to add Alpine.js in Laravel 9 app is by installing default Breeze starter kit which will incorporate basic authentication to your Laravel app along with adding Alpine.js as dependency.

So, using Composer, add default Breeze package by executing following command;

composer require laravel/breeze:* --dev

Now install Breeze package by executing;

php artisan breeze:install

The above command will also install required node modules and compile the UI assets for the first time.

Next, setup database and Laravel environment file. Here, I’m configuring SQLite database for simplicity purposes.

Being in parent directory (app), execute following command in order to create new SQLite database file;

touch database/database.sqlite

Then find .env config file in the parent directory, open it and amend DB_CONNECTION line as follows;

DB_CONNECTION = sqlite

You can remove DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD from .env when using SQLite database.

Save and close .env file. Now enter following command in order to define database structure:

php artisan migrate

In order to populate database table quickly, let’s enter following command to invoke tinker;

php artisan tinker

And enter following command to create 100 users;

User::factory(100)->create()

The above command will utilize database\factories\UserFactory.php to generate fake data for app testing.

Note down any of the generated email addresses for logging in later. Now type ‘quit’ and press Enter to exit tinker.

Now, create a UserController by entering the following command:

php artisan make:controller UserController

The above command will create app/Http/Controllers/UserController.php file. Then put the following code inside this newly generated UserController.php;

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;

class UserController extends Controller
{
    public function index()
    {
        $users = User::all();
        return view('users.index', compact('users'));
    }
}

Now, edit routes/web.php by importing UserController at the upper side and then inserting a route as follows:

// ...
use App\Http\Controllers\UserController;

// ...
Route::get('users', [UserController::class, 'index'])
    ->name('users');

Create directory named users inside resources/views directory. If you are in parent directory (app), you may execute following command to make this directory;

mkdir resources/views/users

Now create a file named index.blade.php inside resources/views/users directory. You can execute following command inside parent directory (app) in order to create this file;

touch resources/views/users/index.blade.php

Insert following code inside index.blade.php;

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Users') }}
        </h2>
    </x-slot>

	<div
		x-data="{
			search: '',
			users: {{$users}},
			get filteredUsers() {
				return this.users.filter(
					i => i.name.toLowerCase().startsWith(this.search.toLowerCase())
				)
			}
		}"
	>

	<div class="py-6">
		<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
			<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
				<div class="p-4 bg-white border-b border-gray-200">
					<input x-model="search" class="mb-2 p-1 border border-gray-400 rounded-md" placeholder="Search...">
					<table class="shadow-lg">
						<thead>
							<tr class="bg-gray-400 text-white font-extrabold" >
							<td class="px-4 py-1 text-center" >ID</td>
							<td class="px-4 py-1">Name</td>
							<td class="px-4 py-1">Email</td>
							</tr>
						</thead>
						<tbody>
							<template x-for="user in filteredUsers" :key="user.id">
								<tr>
									<td x-text="user.id" class="border px-4 py-1 text-center"></td>
									<td x-text="user.name" class="border px-4 py-1"></td>
									<td x-text="user.email" class="border px-4 py-1"></td>
								</tr>
							</template>
						</tbody>
					</table>
				</div>
			</div>
		</div>
	</div>
	
	</div>
	
</x-app-layout>

Here, we have used x-data attribute of Alpine.js in order to define two data fields i.e., search, users and a function filteredUsers() respectively. search field is kept empty initially whereas users field carries the $users data as passed on by UserController‘s index function. filteredUsers() function actually filters users in order to return only those records where name starts with the value which exists in search field. To simplify comparisons, all strings i.e., name and search fields are converted into lower case by using toLowerCase() function. Then, we utilized x-model attribute in order to synchronize the search data field with search input element. Finally, we used x-for along with x-text attributes within <template> tags in order to iterate records of the data as returned by filteredUsers() method.

Now execute following command in parent directory to compile UI assets:

npm run build

Finally, execute following command in parent directory to run PHP development server;

php artisan serve

Open browser and enter following URL in address bar;

http://localhost:8000

Login with the noted email address as above (just remember, the default password for all users generated through above factory is ‘password’). After logging in, enter following URL to list all users and then use search field to filter records;

http://localhost:8000/users

In next article, we’ll see how to incorporate dynamic search using Alpine.js and AJAX to fetch updated data from the backend database.

1 thought on “Simple search in Laravel 9 app using Alpine.js”

Leave a Comment