Optimizing the performance of a Laravel application

optimizing-performance-laravel-header.png

In this article, I will be sharing with you a few ways and techniques to optimize your Laravel application.

Introduction

In this article, I will be sharing with you a few ways and techniques to optimize your Laravel application.

As your web application begins to grow in functionality and usage, the need to optimize its performance arises, so as to give your users a better experience when using your application. Come to think of it, nobody actually likes slow applications. You tend to lose users with slow applications, which is bad for your business.

Routes caching

Routes caching will drastically decrease the amount of time it takes to register all of your application’s routes. To generate a route cache, we make use of the Artisan route:cache command:

1$ php artisan route:cache

Now, instead of loading routes from routes/web.php, the cached routes file will be loaded on every request. Because when new routes are added, you will need to generate a fresh route cache, the route:cache command should only be run during your project’s deployment.

To clear the route cache, you may use the route:clear command:

1php artisan route:clear

NOTE: Closure based routes cannot be cached. To use route caching, you must convert any Closure routes to controller classes.

Configuration caching

Just as with routes caching, we can also cache all our configuration files. We make use of the Artisan config:cache command, which will combine all of the configuration options for our application into a single file which will be loaded quickly by the framework.

1$ php artisan config:cache

As with the route:cache command, this command should only be run during your project’s deployment. The command should not be run during local development as configuration options will frequently need to be changed during the course of your application’s development.

NOTE: if you execute the config:cache command during your deployment process, you should be sure that you are only calling the env function from within your configuration files. Once the configuration has been cached, the .env file will not be loaded and all calls to the env function will return null.

Eager loading

A common problem when retrieving Eloquent relationships is the N + 1 query problem. Let’s consider this scenario: we have two models: Car and Owner, with some sort of relationship. Assume we want to retrieve all cars and their respective owners. To achieve that, we might have the following code:

1$cars = App\Car::all();
2
3    foreach ($cars as $car) {
4      echo $car->owner->name;
5    }

This loop will execute one query to retrieve all of the cars in the database, then another query for each car to retrieve the owner. To further explain this, imagine we have 100 cars, this loop would run 101 queries: one for the original car, and an additional 100 queries to retrieve the owner of each car. This may seem like a few queries, obviously because we are using a small dataset. But imagine a large data set and the number of queries that would be made on the database. Yeah, you get the picture.

To solve this problem, we make use of eager loading. So instead of the above code, we’ll have:

1$cars = App\Car::with('owner')->get();
2
3    foreach ($cars as $car) {
4      echo $car->owner->name;
5    }

At the point of retrieving all the cars, we eager load the owner of the cars as well. With this, we have reduced this operation to just two queries.

Queues

Queues can also be used to improve the performance of an application. Imagine we have an application that sends a welcome email to new users upon sign up. This application makes use of third-party email service such as Mailgun for sending the emails. When a user fills and submits the signup form, a new user record will be inserted into the database, then a call will be made to Mailgun to send the welcome email.

The actual sending of the email can take a few seconds (or milliseconds as the case may be). So as not to make the users think the signup process is slow, we can make use of queue by queuing the email sending to run as a background task. That way, our users will get a prompt response from the signup form, while the email sending process runs in the background. In code this might look like below:

1public function register(Request $request)
2    {
3      // validate form inputs
4      $this->validator($request->all())->validate();
5
6      // persist user to database
7      $user = $this->create($request->all());
8
9      // send welcome email
10      Mail::to($user)->send(new WelcomeEmail($user));
11
12      // log user in
13      $this->guard()->login($user);
14
15      return $this->registered($request, $user) ?: redirect($this->redirectPath());
16    }

Aside from slightly increasing the performance of the application, it also improves the user experience.

Database caching

With caching backends like Redis and Memcached, we can also cache database results. Instead of having to retrieve the same set of results from the database over and over again. We could employ database caching, by first retrieving the records from the database and then caching it for later use. For example:

1$value = Cache::remember('users', $minutes, function () {
2      return DB::table('users')->get();
3    });

The code above will retrieve all users from the cache or, if they don’t exist, retrieve them from the database and add them to the cache.

For records you know are likely not to change, such as a record of countries in the world, we could store them in the cache forever:

1$value = Cache::rememberForever('countries', function() {
2      return DB::table('countries')->get();
3    });

With little or no calls to your database, you will see a great improvement in the performance of your application.

Assets bundling

Every Laravel applications come with Laravel Mix by default. Laravel Mix provides a fluent API for defining Webpack build steps for your Laravel application using several common CSS and JavaScript pre-processors. We can use Laravel Mix to compile our application assets, such as scripts and styles. For example, we can concatenate several stylesheets into a single file:

1mix.styles([
2      'public/css/vendor/normalize.css',
3      'public/css/styles.css'
4    ], 'public/css/all.css');

It will create all.css, which will contain styles from normalize.css and styles.css. This way, we just use all.css in our HTML, instead of having to include each of them (normalize.css and styles.css) individually. This will, in turn, reduce the number of HTTP requests needed to retrieve these files individually. Now just one request instead of two. Because our application now needs to retrieve just one file, we notice a slight increase in the speed of our application.

Assets minifying

As a result of assets compilation, we might end up with a large single file, which might not allow the application to benefit from the initial compilation. To resolve this, we can tell Laravel Mix to minify our assets:

1$ npm run production

This will run all Mix tasks and make sure our assets are production ready by minifying them. Once minified, the assets will become smaller in size, hence will be retrieved faster, which will speed up the performance of our application.

Running the latest version of PHP

With the latest version of PHP, we have seen a significant improvement in the performance of PHP itself. So as much as possible ensure your Laravel application is running on the latest version of PHP, so as to tap in into these improvements.

Laravel Debugbar

Though not an optimization technique, but a package. Laravel Debugbar is a package to integrate PHP Debug Bar with Laravel 5. It includes a ServiceProvider to register the debugbar and attach it to the output. It is a package that can be used to inspect the performance of your application. It is recommended to make use of this package while developing your application. Because with it, you can easily inspect how your application is running, and then improve accordingly.

Laravel Debugbar in action

Conclusion

In this article, we have seen various ways and techniques that we can use to optimize the performance of a Laravel application. Implement these techniques in your applications to see massive performance improvements. I hope you found this article helpful.