Jay Pardoe
Jay Pardoe

Reputation: 15

PHP Laravel are DB function results cached?

Noob question. I'm working on a project which involves interacting with some legacy software and the database is not compatible with regular Laravel Relationships.

If I'm defining things in a constructor like this:

public function __construct(array $attributes = array())
{
parent::__construct($attributes);

$this->vatpercentage = $this->customer()->vatpercentage;
$this->vatcode = $this->customer()->vatcode;
$this->partrevision = $this->part()->revision;
$this->unitprice = $this->part()->unitprice;
}

public function part(){
    return Part::findOrFail($this->partnum);
}

public function customer(){
    $traderid = Order::where('id', $this->orderid)->value('traderid');
    return Customer::where('id', $traderid)->where('tradertype', 'C')->first();
}

I need to reference customer(), part() and other similar functions many times in the constructor. Is the DB getting queried every time I reference $this->customer(), etc or is the result cached the first time I reference it and then used for all the other times following? Basically am I doing a lot of unnecessary DB calls by coding in this way rather than setting $this->customer = $this->customer() and grabbing the values like $this->customer->example?

Upvotes: 0

Views: 146

Answers (2)

jakub wrona
jakub wrona

Reputation: 2254

In Laravel 4.* there was a remember() method handling the cache in queries. It was removed from 5.1 with a valid reason that it's not the responsibility of Eloquent neither the query Builder to handle cache. A very very simplified version of a decorator class that could handle the caching of your queries:

final class CacheableQueryDecorator
{
    private $remember = 60; //time in minutes, switch to load from config or add a setter
    private $query = null;

    public function __construct(Builder $builder)
    {
        $this->query = $builder;
    }

    private function getCacheKey(string $prefix = ''):string
    {
        return md5($prefix . $this->query->toSql() . implode(',', $this->query->getBindings()));
    }

    public function __call($name, $arguments)
    {
        $cache = Cache::get($this->getCacheKey($name), null);
        if ($cache) {
            return $cache;
        }
        $res = call_user_func_array([$this->query, $name], $arguments);
        Cache::put($this->getCacheKey($name), $res, $this->remember);
        return $res;
    }
}

Using it:

$results = (new CacheableQueryDecorator($query))->get()

Upvotes: 0

Devon Bessemer
Devon Bessemer

Reputation: 35337

No database query or method call is going to be cached automatically in your application, nor should it. Laravel and PHP aren't going to know how you want to use queries or methods.

Everytime you call customer(), you're building up and executing a new query. You could easily cache the result in a property if that's what you want, but you'd have watch the value of the $orderid property:

protected $customerCache;

public function customer()
{
    if ($customerCache) return $customerCache;

    $traderid = Order::where('id', $this->orderid)->value('traderid');
    return $customerCache = Customer::where('id', $traderid)->where('tradertype', 'C')->first();
}

You're also performing too much in your constructor. I would highly recommend not performing queries in any constructors, constructors should be used to pass dependencies. The way you have it designed would make it very hard to unit test.

Upvotes: 1

Related Questions