Laravel Speed Optimization & Complete Code Performance Guide

John on December 15, 2017 @ 11:18 PM

Laravel is one of the fastest PHP frameworks out there and in most cases your web application is going to be pretty fast when deployed to a web server. But if you feel that your application is not as snappy as you would like there are a number of things you can do to improve Laravel's performance that we will go to in-depth on this speed optimisation guide.


Out of the box Laravel is quick, so it is most likely that you have not used any strict coding practices if your end product is slow. So as well as tweaking the framework we will also look at best practices for code that will work efficiently on high load apps. In fact lets go into this first because it is most likely where you are going to gain the most speed in Laravel.


How to Monitor the Performance of Your Queries in Laravel


You could log them yourself manually but that is no fun and certainly something you wouldn't want to be doing in the long run. There is a great tool for monitoring the performance of your queries in Laravel 5.5 called "Laravel Debugbar". It is really easy to get set up and will be an invaluable tool when it comes to debugging what is slowing your site down. It shows you how many queries were made, where they were initiated and importantly how long each one took to run when loading a page. It even shows your how many partial views were loaded as well as the total load time of queries.



To install Laravel Debugbar do the following:


Run this composer command in your app root:


composer require barryvdh/laravel-debugbar --dev


Then publish the tool using the following command:


artisan vendor:publish --provider="Barryvdh\Debugbar\ServiceProvider"


To run Debugbar in your browser open .env file in your app root and make sure the following line is set to true:


APP_DEBUG=true


If you are not eager loading you are likely to find that your app is making a shocking number of database requests. I have seen some apps making over 100 to load a seemingly simple page. This happens because you are not combining your requests and are making a DB request on each cycle of your foreach.


Use Eager Loading


For a faster performing Laravel application using eager loading is imperative (where necessary). Lets have a look at a query that does not utilise eager loading and then convert it so it does.


Controller:


$item = Movie::all()->get();


Blade view:


@foreach($item as $i)
  <p>{{ $i->name }}</p>
  <p>{{ $i->genre->name }}</p>
  <p>{{ $i->director->name }}</p>
@endforeach


Seems ok right? Well for each iteration of a movie in this example we make a request to the DB for it's genre and its director. Only a couple of iterations and this immediately becomes enormously inefficient. To load 50 movies with this data you would make a total of 101 queries, one for the movies 50 (if it only had one genre) for genres 50 director requests which is not good.


As the name suggests eager loading will combine those queries into one with the anticipation that we are going to be looping through the data and using it. Eager loading this query is as simple as using a "with" function:


$item = Movie::all()->with('genre')->with('director')->get();


The "with" calls the database relationships in your model, Belongstomany, belongsto etc, and with a little bit of magic we get everything back in one response. The blade is used in exactly the same manner as before.


So go though your application and make sure your are implementing eager loading where needed.


Make Sure you are Running PHP 7


PHP 7 runs a lot nicer with Laravel 5.5 and will give you some performance benefits. Make Sure you are have enabled it on your web hosting. The only potential problem with running the latest version of php is if you have dated packages installed which your application relies on; so make sure they don't use depreciated php functions and you have composer updated them.


Don't Worry About Including Too Many Views


I'm sure I read somewhere that this would slow down your application. It does not however since Laravel caches all blade view templates in their compiled state until a change is made, so keep your view files semantically structured, beautiful and don't worry about this one.


Laravel Artisan Optimize Command


php artisan optimize


You don't need to run this one any more and if you do on Laravel 5.3 or above it will give you a message saying; "The compiled services file has been removed". The reason why this was taken out is because php 5.6 and above offers the same class compiling optimizations built in therefore rendering the function redundant. The command is due to be completely removed in Laravel 5.6.


Are Your Running the Latest Version of Laravel?


The Laravel core code is being worked on 24-7 and with each update improvements are always made often related to performance. If you are running an ancient version update it! Not just for the performance but to ensure that you are not running any known security loopholes.


The Laravel website offers a really helpful upgrade guide telling you everything that has changed from one version to the next. You can effectively go from version 4.0 all the way up to 5.5 in incremental steps using composer and manually updating files. Yes, it does take a little while to do but you shouldn't be running outdated software on an app with any kind of importance.


Remove Unused Packages


Over time you will have probably played about with a number of packages on your project. If you still have them published they will still be put into your app boot que and will cost some performance. To remove a Laravel package:


  • open composer.json (in root)
  • remove the line(s) referring to the package(s) you would like to remove
  • open app/config/app.php and remove references to those packages
  • Run "composer update" in terminal and all unnecessary files will be removed.


Cache Routes


If you have quite a large application your routes file(s) are most likely enormous. This can have a small performance impact if php has to find a route within hundreds. Fortunately Laravel provides a caching function to absolve this:


php artisan route:cache


I highly recommend that you only run this command when your app is up and live on a production server with no other foreseeable changes to routes files needed. If you have made a change to routes after running this command you will have to clear the cache by running:


php artisan route:clear


Cache the Configuration File


The Configuration file can also be cached using the following command:


php artisan config:cache


And like the route cache you will also have to clear this whenever you make any changes:


php artisan config:clear


Caching Queries


Caching queries in Laravel is reasonably easy and will give you some nice performance improvements. The ideal scenario is that once a dynamic page has been loaded when the next user comes along a cacheing engine can pull a cached copy of an eager loaded query and the system will not have to make a single request to the DB.


Of course this functionality has to used with some thought since it would not be a "performance improvement" if your latest posts query is cached for a month! Lets have a look at a cached query which takes this into account:


$theme = \Cache::remember('theme', 30, function() {
	return db::table('spotlight')->first();
});


Cache::remember is taking two arguments, the name of the cached item (or one to be cached) and the duration that the caching engine should keep the aforementioned file for. In this case it is 30 minuets. Inside the function we are returning what database information we would like to save.


This works great for static data but what about dynamic information that uses the same query but with a variable? We we could use this:


$crumb = \Cache::remember('crumb-' . $item->id, 10, function() use ($item) {
     return Category::ancestorsAndSelf($item->category->where('parent_id', '!=', '0')->first()->id ?? null)->toFlatTree();
});


What this function is returning is kind of irrelevant, but as you can see we can simply concatenate a variable to the cached files name so Laravel will know to save a unique file. The great thing is Laravel uses the excellent memcached caching engine as default meaning it will automatically delete expired caches so your server will never get filled with junk. If you have not heard about them before please visit there website because despite the enormous number of minds used to create it they are quite modest about their amazing achievements.


Index Commonly Used Table Columns


If you are using an SQL database make sure you have set an index on commonly used column names as it will make queries faster. The ID column will already have an index but if you find items by name, url etc you will need to set up an index yourself. With Lavavel it is very easy to do. In your table add either unique or index to a column and Laravel will instruct SQL to add the appropriate indexes.


$table->boolean('status')->nullable()->index();
$table->string('url')->unique();



So there you have it, a Laravel speed guide. I hope you have found something useful here and that you can keep your Laravel application running smoothly in the future. If you have anything to say please comment below.