nolros's avatar
Level 23

Cache Eloquent Queries - Boot OR Service Provider OR ...

There must be a better way of caching teh model. Lets assume 5 product families with 5 products in each product family with approx 20 product features for each product i.e. a family will have 5 products and each will have 20 features so a total of 100 features for each family. So the relation will be product_family -> hasMany products -> hasMany features.

Example, family = SEO and products Audit, Local SEO, eCommerce SEO. Mobile SEO, and Voice SEO. I want to cache the family with products and features or each product with features.

The example below works, but doesn't seem elegant.

Other options might include:

  1. A service provider (ProductsServiceProvider) that caches all requests on boot
  2. A Model boot trait
  3. A get attribute mutator

Thoughts?

<?php


namespace App\Products;


use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;

class Product extends Model
{
    /**
     * Create unique cache key
     *
     * @param string $slug
     * @param array $relations
     * @return string
     */
    public function cacheKey($slug = "", $relations = [])
    {
        return sprintf(
            "%s-%s-%s",
            $this->getTable(),
            $slug,
            implode("-",$relations)
        );
    }


    /**
     * Cache Product of slug
     * 
     * @param $slug
     * @param array $relations
     * @return mixed
     */
    public function getCachedProductOfSlug($slug, $relations = [])
    {
        return Cache::remember($this->cacheKey($slug, $relations) , 60, function ()  use($slug, $relations) {
            return $this->where('slug',$slug)->with($relations)->first();
        });
    }

    /**
     * Product of slug
     *
     * @param $slug
     * @param array $relations
     * @return mixed
     */
    public function productOfSlug($slug, $relations = [])
    {
        return $this->getCachedProductOfSlug($slug, $relations);
    }
}


0 likes
4 replies
bobbybouwmann's avatar

Well, you say it's not elegant, but I'm not sure what you're trying to achieve. Do you want to cache every call to the database, which sounds really dangerous to me? Or do you want to cache this specific relationship, which looks fine in my opinion? You might be able to clean this code a bit more, but for the rest, it looks fine and does exactly what you want it to do!

nolros's avatar
Level 23

@bobbybouwmann I'm trying to cache the all the product queries for 24 hour period. This would be somewhat static content for a website. Why? caching the product and features helps with SEO page speed, which works In the case of a B2B company (e.g. software company or marketing agency) where their products or services don't often change often. Obviously this approach wouldn't work well for products with an ecommerce site.

1 like
nolros's avatar
Level 23

@bobbybouwmann as a side comment. I cache OS, PHP, routes, queries and have written my own Vue Session Storage cache which results in sites loading in 1 - 1.5 seconds thereby scoring a perfect 100% on both Google and GTMetrix pagespeed tests.

bobbybouwmann's avatar
Level 88

@nolros Well that helps for the context, thanks for that.

I think your current approach is perfectly fine. If this works for you, then you're good to go ;)

Please or to participate in this conversation.