much appreciated for this feature.
Never Execute a Duplicate Query Again
I bet you have Barryvdh's Debug Bar or some equivalent enabled, and you're seeing tons of duplicate queries, and you simply don't have the time to track them all down.
Boy do I have the solution for you!
Have you considered caching every single query per request? Check this out:
Every SELECT query in Laravel, whether it's using Eloquent, or the DB Facade, all runs through a single function. We're going to override that function:
~/app/Support/Database/Builder.php
<?php
namespace App\Support\Database;
use Cache;
use Illuminate\Database\Query\Builder as QueryBuilder;
class Builder extends QueryBuilder
{
/**
* Run the query as a "select" statement against the connection.
*
* @return array
*/
protected function runSelect()
{
return Cache::store('request')->remember($this->getCacheKey(), 1, function() {
return parent::runSelect();
});
}
/**
* Returns a Unique String that can identify this Query.
*
* @return string
*/
protected function getCacheKey()
{
return json_encode([
$this->toSql() => $this->getBindings()
]);
}
}
So, we're caching every query by it's SQL and it's Bindings. Neat, right? But what's this all about?
Cache::store('request')->...
That's right, we need to set up a custom Cache Driver. We're doing this so that we don't mess with any existing Cache logic you might already be using. Here's how I've defined this:
~/config/cache.php
'stores' => [
'request' => [
'driver' => 'array'
]
]
In case you're wondering, the array driver is an in-memory Cache store that just stores everything inside an array. This is typically wiped at the end of each request, as once the application gives a response, this is naturally freed from memory. This is actually exactly what we want, as we only care about queries being run within the same request.
Just in case you decide to set things up differently, you can flush the Cache at the end of every request by adding this:
~/public/index.php
try
{
// Flush the Request Cache
$app->make('cache')->store('request')->flush();
}
catch(ReflectionException $ex)
{
// Do nothing
}
After the $kernel->terminate($request, $response) statement.
Now, don't go trying this yet, as it's not going to work. Remember how we "created" a custom Query Builder? Well, we have to tell our Models how to use it. This can be done with the following trait:
~/app/Support/Database/CacheQueryBuilder.php
<?php
namespace App\Support\Database;
trait CacheQueryBuilder
{
/**
* Get a new query builder instance for the connection.
*
* @return \Illuminate\Database\Query\Builder
*/
protected function newBaseQueryBuilder()
{
$conn = $this->getConnection();
$grammar = $conn->getQueryGrammar();
return new Builder($conn, $grammar, $conn->getPostProcessor());
}
}
You'll want to slap this on any Model that you want to use this new feature:
~/.../SomeModel.php
<?php
namespace App;
use App\Support\Database\CacheQueryBuilder;
use Illuminate\Database\Eloquent\Model;
class SomeModel extends BaseModel
{
/**
* For Caching all Queries.
*/
use CacheQueryBuilder;
}
And viola! Give it a try! See what happens!
Please or to participate in this conversation.