Create a MeiliSearch Laravel Blade Component

Create a MeiliSearch Laravel Blade Component

Written by Mithicher Baro on Sep 28th, 2021 Views Report Post

What is MeiliSearch?

As stated in their official website - MeiliSearch is an open source, blazingly fast and hyper relevant search-engine that will improve your search experience.

Laravel also supports MeiliSearch out of the box. Feel free to visit the documentation to know more about it: https://laravel.com/docs/8.x/scout#meilisearch

Install MeiliSearch on a Local Machine

Run the following command in your terminal to download and install meilisearch in your local machine.

curl -L https://install.meilisearch.com | sh

Then run the following command to start meilisearch in your terminal.

./meilisearch

Feel free to checkout their docs: https://docs.meilisearch.com/learn/getting_started/quick_start.html

Let's add some sample index. Your can download a sample books.json data from here:

https://gist.github.com/mithicher/cba0358aa205abc9795e33510a876aa6

Then open another terminal and run the following curl command:

curl \
  -X POST 'http://127.0.0.1:7700/indexes/books/documents' \
  --data @/Users/your-user-name/Downloads/books.json 

Reusable Autocomplete Search Input

We will attempt to create a reusable search input component for MeiliSearch here by leveraging the power of Laravel's blade components, Alpine.js for a little javascript, and TailwindCSS for styling.

Install and Configure MeiliSearch With Laravel

composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle

Then updated your .env file with the credentials

SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=meilisearch-master-key
MEILISEARCH_PRIVATE_KEY=meilisearch-private-key
MEILISEARCH_PUBLIC_KEY=meilisearch-public-key

To obtain public and private key from meilisearch run the following curl command:

curl -X GET 'http://127.0.0.1:7700/keys' \
--header "X-Meili-API-Key: your-master-key

Then go to config/scout.php and update this

'meilisearch' => [
    'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
    'key' => env('MEILISEARCH_KEY', null),
    'public_key' => env('MEILISEARCH_PUBLIC_KEY', null),
    'private_key' => env('MEILISEARCH_PRIVATE_KEY', null),
]

Make sure to install the scout package via the following composer command

composer require laravel/scout

Assuming you have already configured alpine.js and tailwind css in your app.blade.php let us a create a blade component named meilisearch-autocomplete.blade.php inside your resources/views/components folder.

Full component can be found here:

https://gist.github.com/mithicher/76a551e0989df1b326ddbabf7f8edc21

Let's see how it works

<input
	type="search"
	autocomplete="off"
	id="search"
	name="search"
	class="form-input bg-gray-50 focus:bg-white border-gray-200 mt-px pl-10 leading-none placeholder-gray-400 shadow-none rounded-lg flex-1 w-full transition duration-150 ease-in-out"
	placeholder="{{ $placeholder }}"
	x-model="searchString"
	x-on:input.debounce.600ms="search()"
	x-on:keydown.arrow-down.stop.prevent="highlightNext()"
	x-on:keydown.arrow-up.stop.prevent="highlightPrevious()"
	x-on:keydown.enter="goToLink()"
/>

We are adding a x-model that allows us to bind the value of an input element to an Alpine data. Then on every input with a delay of 600 milliseconds search() method is triggered.

<div
	x-data="{
		client: null,
		host: '{{ $host }}',
		apiKey: '{{ $publicKey }}',
		state: '',
		searchString: '',
		results: {
			hits: [],
		}
		async search() {
			this.state = 'searching';
			this.results = await this.client.index('{{ $indexName }}')
																			.search(this.searchString);
			this.state = 'finished';
		}
	}"
	x-init="client = new MeiliSearch({
		host: host,
		apiKey: apiKey
	})"
	x-cloak
	class="relative"
>
...
</div>

As stated in the alpinejs.dev website:

Everything in Alpine starts with the x-data directive.

So we define an x-data along with some initial values. We then initialize the meilisearch client with the required credentials in the x-init directive and put in a local variable to reuse later.

async search() {
	this.state = 'searching';
	this.results = await this.client.index('{{ $indexName }}')
									.search(this.searchString);
	this.state = 'finished';
}

How to use it on our blade views

<x-meilisearch-autocomplete
	placeholder="Search books..."
	index-name="books"
	public-key=""
	host="http://127.0.0.1:7700/"
>
	<a class="flex-1 flex" href="#">
		<div class="w-24 h-32 rounded bg-gray-100 flex-shrink-0 mr-3 border">
			<img :src="result.thumbnailUrl" alt="poster" loading="lazy" class="rounded w-full h-32 object-cover">
		</div>

		<div class="flex-1">
			<div class="text-gray-800">
				<p class="text-lg mb-1 font-medium leading-tight line-clamp-2" x-text="result.title"></p>
				<p class="text-sm text-gray-500" x-text="result.authors.join(', ')"></p>
			</div>

			<div class="mt-2 flex space-x-2 items-center leading-none">
				<div class="text-xs text-gray-500 truncate capitialize" x-text="result.categories.join(', ')"></div>
				<div class="text-gray-400">&bull;</div>
				<div class="text-xs text-gray-500" x-text="result.isbn"></div>
				<div class="text-gray-400">&bull;</div>
				<div class="text-xs text-gray-500" x-text="result.pageCount"></div>
			</div>

			<p class="text-xs mt-2 text-gray-500 line-clamp-3" x-text="result.shortDescription ? result.shortDescription.substr(0, 220) + '...' : result.shortDescription"></p>
		</div>
	</a>
</x-meilisearch-autocomplete>

So here we have a meilisearch blade component. Feel free to comment and any suggestion for improvement. I have skipped the keyboard navigation part but feel free to use the component and see.

Here are some resources that you should definitely check it out:

Follow me on twitter @mithicher and checkout my codepen https://codepen.io/mithicher.

See you next time. Bye 👋

Comments (0)