Building a Store Locator using Laravel, Leaflet and Meilisearch - Part 1
Overview
The requirement for data to be displayed on a map is something i've witnessed over again. Often in completely different industries and circumstances but usually implemented in similar manners. Meilisearch and Leaflet JS make usefully displaying this data extremely easy to achieve.
In this walkthrough, we will create a store locator in Laravel that uses Meilisearch and Leaflet JS to allow a user find results within a radius of the postcode that they provide.
Meilisearch
Meilisearch is an open source, super fast, search engine inspired by Algolia and requiring very little setup. Meilisearch provides instant search and typo tolerance out of the box and with a few updates to settings Geosearches become a breeze.
Leaflet
Leaflet is a lightweight open source JS library that makes mapping incredibly quick and easy.
For this project i used:
Requirements:
Laravel Project Setup
Create a new project using your preferred method. I use the Laravel installer via Composer and use Laravel Valet which lets me quickly access the new project via store-locator.test in the browser.
// Composer
composer create-project laravel/laravel store-locator
// Laravel Installer
laravel new store-locator
Although authentication is not needed for this example, I added Laravel Breeze to the fresh Laravel application to quickly make use of the frontend scaffolding that comes for free.
composer require laravel/breeze --dev
Install Laravel Breeze by running the install command with the frontend option of choice. For this project i opted for Vue.
php artisan breeze:install vue
Database
Set up the database of choice by updating the necessary database settings in the project .env file.
For speed I used SQLite by creating a file named database.sqlite inside the database directory and updating the database connection to sqlite in .env.
Once updated continue the Breeze instructions to run database migrations, install JS dependencies and compile frontend assets.
php artisan migrate
npm install
npm run dev
Leaflet JS Installation
To display the store locator, I used Leaflet, a small JS package that makes mapping incredibly easy.
Install Leaflet into your project using NPM
npm install leaflet
and then import the CSS and JS files in resources/js/app.js
...
import 'leaflet/dist/leaflet.css'
import L from 'leaflet';
Alternatively add the CSS and JS links to the head of your page.
// CSS
<link
rel="stylesheet"
href="https://unpkg.com/leaflet@1.9.2/dist/leaflet.css"
integrity="sha256-sA+zWATbFveLLNqWO2gtiw3HL/lh1giY/Inf1BJ0z14="
crossorigin=""
/>
// JS
<script
src="https://unpkg.com/leaflet@1.9.2/dist/leaflet.js"
integrity="sha256-o9N1jGDZrf5tS+Ft4gbIK7mYMipq9lqpVJ91xHSyKhg="
crossorigin="">
</script>
Postcodes.io
In order to plot locations, real postcodes/LatLng values are required, and as Faker does not necessarily return valid postcodes, I used postcodes.io
This is an open source service that is capable of returning location data when provided with a postcode. As a potentially large number of calls to the service would be required, to save on network calls, I hosted the service locally using Docker.
Launch the postcodes.io service by firstly running the DB container
docker run --name=the_db -p 5432:5432 -e POSTGRES_USER=postcodesio -e POSTGRES_DB=postcodesiodb -e POSTGRES_PASSWORD=password -d idealpostcodes/postcodes.io.db:latest
and then firing up the API.
docker run -p 8000:8000 --link=the_db:db -e POSTGRES_HOST=db -e POSTGRES_USER=postcodesio -e POSTGRES_DATABASE=postcodesiodb -e POSTGRES_PASSWORD=password -d idealpostcodes/postcodes.io:latest
Test that things are running correctly by running curl localhost:8000/random/postcodes
in the terminal or sending a GET request to the same endpoint using your favourite API client. This should return a random location.
Config
Create a file named postcodes_io.php in /config and add a key that returns the POSTCODES_IO_URL value if present from .env or the url to the postcodes.io api if not set so the base url can be set if using a local/remote version of the service.
<?php
return [
'postcodes_io_url' => env('POSTCODES_IO_URL', 'https://api.postcodes.io')
];
If self serving postcodes.io, add the POSTCODES_IO_URL key to .env and set the value to the url of the service with the relevant port. For me this was localhost:8000.
Meilisearch
To run Meilisearch, I used a local Docker container that can be started using the below command1. The below exposes the container on port 7700 in dev mode and uses "MASTER_KEY" for the master key.
docker run -it --rm -p 7700:7700 -e MEILI_MASTER_KEY='MASTER_KEY' -v $(pwd)/meili_data:/meili_data getmeili/meilisearch:v0.29 meilisearch --env="development"
Laravel Scout
Laravel Scout is a first party Laravel package that adds full-text search functionality to Laravel models, the package has drivers for a number of third party solutions including Meilisearch and Algolia out of the box.
Install Laravel Scout to the project
composer require laravel/scout
and then publish the Scout config file
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
Next add the Meilisearch PHP SDK as per the Scout installation instructions 2
composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle
Finally update the Scout and Meilisearch settings in .env, using the master key you set in the Meiliseach setup section and the location of the running Docker container.
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=localhost:7700
MEILISEARCH_KEY=MASTER_KEY
Now we should have the postcodes.io and Meilisearch services running locally and an empty project that is ready to connect with and utilise them both.
In the next part, we'll seed the database and build a front end in Vue that is able to query the services and retrieve geo based results.
Building a Store Locator using Laravel, Leaflet and Meilisearch - Part 2