blog

  • Home
  • blog
  • How to Build a Lyrics Website with Laravel Scout and Algolia

How to Build a Lyrics Website with Laravel Scout and Algolia

Laravel Scout is a driver-based library for adding full-text search to Eloquent models. If you’re not familiar with Laravel or Eloquent models in general, we have a good premium course available here.

In this tutorial, we will create a minimal lyrics web site using Laravel Scout and Algolia. To do so, we need a CRUD app to work with our data, and a search interface to let users search through the content. However, instead of creating the full app from scratch, let’s just install the finished CRUD app that we’ve created for this tutorial.

Algolia is a hosted search engine API, providing us with all the tools and resources we’ll ever need to create products with awesome search capabilities.

Search stock vector image

By the end of the tutorial, we’ll have a product like this:

Lyrics_Website_With_Algolia_Scout

Lyrics Details Page

Bootstrapping the Application

I assume you already have your development environment up and running. However, If you need a good development environment to get into action right away, you should use Homestead Improved.

Stranger to Vagrant? Read this. Want to go more in depth? Buy our book on PHP Environments!

Feel free to skip this part if you already have a similar application or you have enough experience to build one relatively quickly.

CRUD Application

The most convenient way to download the pre-built CRUD project is to clone it:

git clone git@github.com:lavary/lyrics-crud.git coolyrics
cd coolyrics
composer install

Setting Up the Database

Now, let’s create a MySQL database. The settings below apply to the Homestead Improved environment mentioned above. Change as needed.

mysql -h localhost -u homestead -psecret
mysql> CREATE DATABASE lyrics

After the database has been created, we make a copy of .env.example (located in our project’s root directory) and name it .env. This is where we put our database credentials:

#...

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=lyrics
DB_USERNAME=root
DB_PASSWORD=password

# ...

Again, apply to your own preferences as needed.

Now, we run the migration command to create the tables:

php artisan migrate

Filling Up the Database with Sample Data

A lyrics website contains information about musicians and their work, and, of course, their songs’ lyrics. To make a bare minimum data structure, we have created only two Eloquent models for this project, namely Artist and Song. The Artist model has a one-to-many relationship with the the Song model. This means each artist can have many songs and each song belongs to an artist in our database.

Before moving forward to the next section, you may go ahead and insert a few records into the database, starting with your favorite artists and then adding a few songs for each.

This is what we have so far:

Lyrics Songs list

You can also use the SQL file included with the project files and dump it into your database with the following command:

mysql -h localhost -u {USERNAME} -p{PASSWORD} lyrics  < /path/to/the/sql/file

You can also import the file by using your favorite MySQL management application, like Sequel Pro, MySQL Workbench or PHPMyAdmin.

Installing Scout

Let’s continue by installing Scout:

composer require laravel/scout

Then, we add the service provider to $providers in the config/app.php file:

LaravelScoutScoutServiceProvider::class,

Now we need to generate the Scout’s configuration file using the publish:config artisan command:

php artisan vendor:publish --provider="LaravelScoutScoutServiceProvider"

As a result, a configuration file named scout.php is generated inside the config directory. We’ll edit this file later.

To make a data model searchable, we need to use the LaravelScoutSearchable trait inside the respective model class. That’s the Song model in our case:

<?php

namespace App;

use IlluminateDatabaseEloquentModel;
use LaravelScoutSearchable;

class Song extends Model
{
    use Searchable;

    protected $fillable = ['title', 'album', 'lyrics', 'youtube_link'];

    public function artist()
    {
        return $this->belongsTo('AppArtist');
    }
}

Setting Up Algolia

As planned, we’ll use Algolia as our search engine API.

First, let’s create an account to obtain our application ID. Scout requires Application ID and Admin API Key to operate. After the registration is complete, we can find our credentials under API Keys in the left menu.

Algolia API Keys

Now, we open the config/scout.php configuration file and put our credentials there:

<?php
'algolia' => [
        'id' => env('ALGOLIA_APP_ID', ''),
        'secret' => env('ALGOLIA_SECRET', ''),
    ],

It’s a good practice to keep the keys in .env and load them into scout.php using the env() or getenv() functions.

To use the Algolia’s API, we need to install Algolia’s SDK for PHP, which is also available as a Composer package:

composer require algolia/algoliasearch-client-php

Indexing Our Data

At this point, we need to create our index on Algolia. Each record in the index is a schema-less JSON object (each one represents a record in our database) with a set of attributes that can be used for searching, displaying, ranking and filtering data.

Rather than indexing the whole record, we only need to index the data needed for the above operations. This helps keep our index clean and optimized.

Apart from that, the index is not a relational database, meaning when searching through the index, we cannot use complex where clauses or SQL joins. To work around this limitation, we should define a custom structure for our index records. On the other hand, we should join all the needed tables, preparing a customized JSON object before indexing.

To do this, we override the toSearchableArray() method in the Song model (this method is added to the class by the LaravelScoutSearchable trait). By default, the toSearchableArray() method returns the $this->toArray() output as our index object (when sending the index record to Algolia), whereas we need additional data, like artist’s name, genres and the image URL, which reside in another table – the artists table.

Here’s how we do it:

<?php

namespace App;

use IlluminateDatabaseEloquentModel;
use LaravelScoutSearchable;

class Song extends Model
{
    use Searchable;

    protected $fillable = ['title', 'album', 'lyrics', 'youtube_link'];

    public function toSearchableArray()
    {
        $genres = array_map(function($item) {
            return trim($item);
        }, explode(',', $this->artist->genres));

        return array_merge( $this->toArray(), ['artist' => $this->artist->name, 'photo' => $this->artist->photo, 'genres' => $genres]);
    }

    public function artist()
    {
        return $this->belongsTo('AppArtist');
    }
}

Since the genres field may contain a comma-separated value in our database (it has a simple text field in our CRUD app), we separate genres using explode. Then, we iterate over the results, stripping off any unwanted spaces before and after each part – using the map() function:

<?php

// ...

$genres = array_map(function($item) {
            return trim($item);
        }, explode(',', $this->artist->genres));

// ...

Finally, we merge the output of $this->toArray() with our desired attributes, returning the final array.

<?php

// ...

return array_merge( $this->toArray(), ['artist' => $this->artist->name, 'photo' => $this->artist->photo, 'genres' => $genres]);

// ...

Why do we need to index a randomly generated string like image URL you may be wondering. We’ll get to this shortly.

For the existing records in the database, we can import the index at once with the scout:import artisan command, like this:

php artisan scout:import "AppSong"

When the indexing process is completed, we can see the index on Algolia by going to Indices and choosing our index name from the drop-down menu:

Algolia Index Dropdown

Indexing is not a one-off task. After the initial import, the index needs to be kept in sync with the database. Since we’re using Scout, everything is already taken care of. From now on, any time a record is inserted, updated, or deleted from the database, the respective index record will be affected accordingly – thanks to Laravel’s model observers.

To learn more about how Scout manages the indexing process, have a look at the documentation.

Configuring Algolia

The next thing to do is to configure our Algolia index for optimal operation. These settings can be modified either from the dashboard or programmatically using Algolia’s API.

The most important configuration options are the Searchable attributes and the Custom Ranking Attributes.

The Searchable attributes setting defines which record attributes are used for searching. The order of these attributes also matters as those at the top are considered more important and control the ranking.

Algolia Searchable Attributes

The Custom Ranking Attributes option indicates the popularity of each record which also affects the ranking. They can be anything from the number of likes, views, downloads, to comments. That said, we need to include this information in the index.

Algolia Custom Ranking

Algolia comes with plenty of configuration options. Going through each setting is beyond the scope of this tutorial. To learn more about the configuration options, you should have a look at Algolia’s FAQ on configuration and relevance.

Continue reading %How to Build a Lyrics Website with Laravel Scout and Algolia%

LEAVE A REPLY