5 Tricks To Speed Up Your Laravel Website

5 Tricks To Speed Up Your Laravel Website
Photo by Mohammad Rahmani / Unsplash

There's nothing more frustrating than a website that takes time to load. If this is happening to your users, they are going to leave your website before you've even had a chance to convince them otherwise.

Here are a few quick tips for speeding up your Laravel websites and applications.

1. Review slow queries

An easy way to see all the queries that are running for each page request is by installing Laravel Debugbar.

Laravel Debugbar Queries Ouput

Using Laravel Debugbar will allow you to dig into each query, review its binding, and review which queries are taking longer than necessary.

If a query is taking a long time to process. Check if the table its querying has the necessary indexes defined. An index can be added to a column via a database migration and calling the ->index($columnName) method.

public function up()
{
    Schema::table('posts', function (Blueprint $table) {
    	$table->index('author_id');
    });
}

To check if a query is hitting an index, copy the SQL and prepend EXPLAIN to the query before running it in your SQL tool. This will tell you if your query is using an index or not.

Query that is using an index

If you see no "Using index condition" then that's your cue to add an index for the fields being queried on, otherwise to rewrite the query or forgo it completely.

2. Caching

Caching is a technique that can be applied to various operations that don't need to return the latest live data. Whether it's an API request or a slow query - throwing caching in front will make a huge difference.

In Laravel, we can cache results either forever or for a set amount of time before the operation is executed again.

Eg: Caching forever can be done with the Cache Facade.

use Illuminate\Support\Facades\Cache;

...

// Cache forever until busted
$subscriptionPlans = Cache::rememberForever('subscription_plans', function () {
    return SubscriptionPlan::query()
        ->orderBy('price')
        ->get()
        ->groupBy('name');
});

// Bust the cache
Cache::forget('subscription_plans');

Or for example, cache for 2 days before the cache is cleared and the value is regenerated.

use Illuminate\Support\Facades\Cache;
use Carbon\CarbonInterval;

...

// Cache value for 2 days
$subscriptionPlans = Cache::remember('subscription_plans', CarbonInterval::days(2), function () {
    return SubscriptionPlan::query()
        ->orderBy('price')
        ->get()
        ->groupBy('name');
});

Consider employing this technique for expensive queries that your users are hitting frequently. Good ones to start with are COUNT queries which can bring a database to its knees if used on tables with a large number of rows.

3. Select only the data you need in your queries

When models are populated via the database, unless you say otherwise, Eloquent is performing a SELECT * query to retrieve all the rows from the database. This isn't usually a problem until you start pulling in, let's say 1,000 rows each with 200 fields when all you were after was a title.

All this is doing is putting unnecessary strain on the database and subsequentially, the network which now has to transfer more data than necessary.

To mitigate this. When calling the Eloquent builder, specify which columns you require with the select() method.

$subscriptionPlans = SubscriptionPlan::query()
    ->select(['id', 'name', 'price'])
    ->get();

If all you're after is a single value, then ->value($column) or ->pluck($column) are your answer.

// Return the name of the first matching row
$subscriptionPlans = SubscriptionPlan::query()
    ->value('name'); // returns 'Free'

$subscriptionPlans = SubscriptionPlan::query()
    ->pluck('name'); // returns an array (collection) of all matching rows with 'name' as the value.

4. Eager Loading

Eager loading resolves one of the most common, and easily fixed issues new developers usually face when starting out with Laravel.

The N+1 issue is where we're calling an additional query for each model we're looping over. For example, when looping through a list of users we may want to also show associated data from another table.

It could look something like this:

$users = User::query()
    ->take(10)
    ->get(); // 1 Query

foreach ($users as $user) {
    // +1 query every iteration, when accessing the user invoice relationship.
    echo $user->name . ' paid at: ' . $user->invoice->paid_at . PHP_EOL;
}

The above example will be executing 11 queries under the assumption we're retrieving 10 users.

Using eager loading, this can be reduced to 2 queries regardless of how many users are retrieved.

$users = User::query()
    ->with('invoice')
    ->take(10)
    ->get();
    
...

Or loading afterwards:

$users = User::query()
    ->take(10)
    ->get();
        
$users->load('invoice');

...

5. Maybe it's just your server

Take a look at your version of PHP, the specs available to you, and whether or not you're getting value for money with your current hosting. Your website can be hindered by dated hardware so if this sounds like it could be you - take a look at Digital Ocean. By using my link you'll receive $100 in credit to freely flesh out your application to see if it makes a difference for you.

If you're not accustomed to setting up everything yourself from scratch. Laravel Forge can provision a droplet with everything you need to get going including deploying your code.

Digital Ocean also provides managed database instances if you want to separate the database into its own resource. This does come at an additional cost though.

Conclusion

Interested in more Laravel tricks to speed up your website? Check out More Laravel Optimisation Tricks:

More Laravel Optimisation Tricks
In my first blog post, I went over 5 tricks you can use to improve the speed of your Laravel websites. Below are a few additional tricks that may not specifically improve your website’s performance, but will improve your website’s reliability. Query Chunking Loading too much data all at once