DEV Community

Cover image for Laravel Caching Strategies Every Developer Wishes They Knew Sooner
Kinsta
Kinsta

Posted on • Originally published at kinsta.com

Laravel Caching Strategies Every Developer Wishes They Knew Sooner

Caching is essential for fast and efficient websites. Implementing the right caching strategy from the start is crucial to avoid slow APIs and page load times that can harm your SEO, user engagement, and conversion rates.

There are several cache strategies available, and you can even create a custom strategy to fit your needs. Here are some popular caching strategies that you can implement in your Laravel project.

Laravel Caching Strategies

writeThrough

In the writeThrough strategy, the cache server sits between the requests and the database server, making every write operation go through the cache server before going to the Database Server. Thus, the writeThrough caching strategy is similar to the readThrough strategy.

You can implement this strategy with the Laravel cache with the following code:

public function writeThrough($key, $data, $minutes) {
    $cacheData = Cache::put($key, $data, $minutes)

    // Database Server is called from(after) the Cache Server.
    $this->storeToDB($cachedData)
    return $cacheData
}

private function storeToDB($data){
    Database::create($data)
    return true
}
Enter fullscreen mode Exit fullscreen mode

writeBack (writeBehind)

This strategy is a more advanced way of implementing the writeThrough strategy by adding writing operations delays.

You can also call this the writeBehind strategy because of the delay in time applied to the cache server before writing the data to the database server.

You can implement this strategy with the Laravel cache with the following code:

$durationToFlush = 1; // (in minute)
 $tempDataToFlush = [];

  public function writeBack($key, $data, $minutes){
    return $this->writeThrough($key, $data, $minutes);
  }

  public function writeThrough($key, $data, $minutes) {
      $cacheData = Cache::put($key, $data, $minutes);
      $this->storeForUpdates($cacheData);
      return $cacheData;
  }

// Stores new data to temp Array for updating
  private function storeForUpdates($data){
    $tempData = {};
    $tempData['duration'] = this.getMinutesInMilli();
    $tempData['data'] = data;
    array_push($tempDataToFlush, data);
  }

// Converts minutes to millisecond
private function getMinutesInMilli(){
  $currentDate = now();
  $futureDate = Carbon(Carbon::now()->timestamp + $this->durationToFlush * 60000)
  return $futureDate->timestamp
}

// Calls to update the Database Server.
public function updateDatabaseServer(){
  if($this->tempDataToFlush){
    foreach($this->tempDataToFlush as $index => $obj){
      if($obj->duration timestamp){
        if(Database::create($obj->data)){
            array_splice($this->tempDataToFlush, $index, 1);
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The writeBack method calls to the writeThrough method, which stores the data to the cache server and a temporary array to be pushed later to the database server using the updateDatabaseServer method. You can set up a CronJob to update the database server every five minutes.

writeAround

This strategy allows all the write operations to go directly to the database server without updating the cache server — only during the read operations is the cache server updated.

Assuming a user wants to create a new Article, the Article stores directly to the database server. When the user wants to read the Article‘s content for the first time, the Article is retrieved from the database server and updates the cache server for subsequent requests.

You can implement this strategy with the Laravel cache with the following code:

public function writeAround($data) {
    $storedData = Database::create($data);
    return $storedData;
}

public function readOperation($key, $minutes){
    $cacheData = Cache::remember($key, $minutes, function() {
      return Article::all();
    })
    return $cacheData;
}
Enter fullscreen mode Exit fullscreen mode

Cache Aside (Lazy Loading)

The database is sitting aside in this strategy, and the application requests data from the cache server first. Then, if there’s a hit (found), the data is returned to the client. Otherwise, if there’s a miss (not found), the database server requests the data and updates the cache server for subsequent requests.

You can implement this strategy with the Laravel cache with the following code:

public function lazyLoadingStrategy($key, $minutes, $callback) {
  if (Cache::has($key)) {
      $data = Cache::get($key);
      return $data;
  } else {
      // Database Server is called outside the Cache Server.
      $data = $callback();
      Cache::set($key, $data, $minutes);
      return $data;
  }
}
Enter fullscreen mode Exit fullscreen mode

The code above shows the implementation of the cache Aside Strategy, which is equivalent to implementing the Cache::remember method.

Read Through

This strategy is the direct opposite of the cache Aside Strategy. In this strategy, the cache Server sits between the Client Request and the Database Server.

Requests go directly to the cache server, and the cache server is responsible for retrieving the data from the database server if not found in the cache server.

You can implement this strategy with the Laravel cache with the following code:

public function readThrough($key, $minutes) {
      $data = Cache::find($key, $minutes);
      return $data;
}

private function find($key, $minutes){
    if(Cache::has($key);){
      return Cache::get($key);
    }

    // Database Server is called from the Cache Server.
    $DBdata = Database::find($key);
    Cache:put($key, $DBdata, $minutes);
    return $DBdata;
}
Enter fullscreen mode Exit fullscreen mode

There you have it! We’ve now discussed a few popular caching strategies for your next Laravel application. Remember, you can even use a custom caching strategy that best suits your project requirements.

Caching the UI Part of a Laravel App

Caching the UI of our Laravel App is a concept known as Full Page cache FPC. The term refers to the process of caching the HTML response from an application.

It’s excellent for applications where the dynamic HTML data doesn’t change frequently. You can cache the HTML response for a faster overall response and rendering of the HTML.

We can implement FPC with the following line of code:

class ArticlesController extends Controller {
    public function index() {
        if ( Cache::has('articles_index') ) {
            return Cache::get('articles_index');
        } else {
            $news = News::all();
            $cachedData = view('articles.index')->with('articles', $news)->render();
            Cache::put('articles_index', $cachedData);                                         
            return $cachedData;           
        }  
    }
}
Enter fullscreen mode Exit fullscreen mode

At first glance, you might have noticed that we check if that articles_index page already exists in our cache server. Then we return the page by rendering it with Laravel’s view() and render() methods.

Otherwise, we render the page and store the output in our cache server for subsequent requests before returning the rendered page to the browser.

Build a Laravel App

Now we’re going to apply what we’ve learned so far by creating a new Laravel project and implementing Laravel cache.

If you haven’t used Laravel, you can read through what Laravel is and peek at our list of excellent Laravel tutorials to get started.

Setting Up Laravel

First, we’re going to create a fresh Laravel instance using the below command. You can look up the official documentation for more.

Open your console and navigate to where you store your PHP projects before running the commands below. Make sure to have Composer installed and configured correctly.

composer create-project laravel/laravel fast-blog-app

// Change directory to current Laravel installation
cd fast-blog-app

// Start Laravel development server.
php artisan serve
Enter fullscreen mode Exit fullscreen mode

Configuring and Seeding the Database

Next, we will set up our database, create a new Article model, and seed 500 fake data points for testing.

Open your database client and create a new database. We’ll do the same with the name fast_blog_app_db and then fill up our .env file with the database credentials:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=fast_blog_app_db
DB_USERNAME=//DB USERNAME HERE
DB_PASSWORD=//DB PASSWORD HERE
Enter fullscreen mode Exit fullscreen mode

Next, we’ll run the following command to create the migration and the Article model simultaneously:

php artisan make:model Article -m
Enter fullscreen mode Exit fullscreen mode

Open the newly created migration found database/migrations/xxx-create-articles-xxx.php and paste in the following code:

id();
            $table->string('title');
            $table->text('description');
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('articles');
    }
}
Enter fullscreen mode Exit fullscreen mode

Next, run the command below to create a new seeder:

php artisan make:seeder ArticleSeeder
Enter fullscreen mode Exit fullscreen mode

Open the newly created seeder file found in database/seeders/ArticleSeeder.php and paste in the following code:

count(500)->create();
    }
}
Enter fullscreen mode Exit fullscreen mode

Open the DatabaseSeeder.php file in the same directory and add the following code:

call(ArticleSeeder::class);
    }
}
Enter fullscreen mode Exit fullscreen mode

Next, run the command below to create a new factory:

php artisan make:factory ArticleFactory
Enter fullscreen mode Exit fullscreen mode

Open the newly built factory file found in database/factories/ArticleFactory.php and paste in the following code:

 $this->faker->text(),
            'description' => $this->faker->paragraph(20)
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, run the command below to migrate our newly created schema and also seed our fake data for testing:

php artisan migrate --seed
Enter fullscreen mode Exit fullscreen mode

Creating the Article Controller

Next, we will create our controller and set up our routes to handle our request and retrieve data using the above model.

Run the following command to create a new ArticlesController inside the app/Http/Controllers folder:

php artisan make:controller ArticlesController --resource
Enter fullscreen mode Exit fullscreen mode

Open the file and add the following code to the class:

// Returns all 500 articles with Caching
public function index() {
  return Cache::remember('articles', 60, function () {
      return Article::all();
  });
}

// Returns all 500 without Caching 
public function allWithoutCache() {
  return Article::all();
}
Enter fullscreen mode Exit fullscreen mode

After that, open the api.php file found inside the routes/ folder and paste in the following code to create an endpoint we can call to retrieve our data:

Route::get('/articles', 'ArticlesController@index');

Route::get('/articles/withoutcache', 'ArticlesController@allWithoutcache');
Enter fullscreen mode Exit fullscreen mode

Testing the Performance

Lastly, we will test the performance of our app’s response with or without the implementation of the Laravel cache.

This screenshot shows the response time of the API with cache implemented:

Laravel API response time with cache

Laravel API response time with cache.

The following screenshot shows the response time of the API without cache implemented — note that the response time has increased over the cached instance by over 5,000%:

Laravel API response time without cache

Laravel API response time without cache.


For more Laravel, explore the best laravel tutorials. Whether you’re a beginner or an advanced Laravel developer, there’s something for everyone in there.

Got questions about Laravel caching? Just drop a comment below!

Top comments (0)