Yii2 Caching Active Record - php

I need help with caching Active Record data in Yii2.
In documentation has example:
$result = Customer::getDb()->cache(function ($db) {
return Customer::find()->where(['id' => 1])->one();
});
I understand that it need to implement the method in model class such a:
public static function getByPk($pk)
{
$result = self::getDb()->cache(function ($db) use ($pk) {
return self::find()->where(['id' => $pk])->one();
});
return $result;
}
If this query result was already cached in $result will set value from cache, otherwise will execute query, correctly?
Also I have question about dependency, what dependency I can use for invalidation this cache value?

As an opinion! You know it depends on data that you add to cache storage. If it's some static data such as status, position etc. you can set the second param duration. You can read about at official doc.
If you make a few same queries on the page you can set 1 or 2 seconds and it helps you a little.

Related

How to return once from database and store in memory in Laravel

What I want to do:
Return a bunch of rows from database, convert in a array stored in memory and make this array visible from the whole project in such a way that other controllers for example can read it. My function is simple as that:
class BoardController extends Controller
{
/*
* returns something like
* ['name' => 'description',
...
]
*
* */
public static function getAll()
{
$boards = Board::orderBy('ordem')->get();
$retorno = array();
foreach($boards as $b)
{
$retorno[$b->sigla] = $b->nome;
}
return $retorno;
}
}
If I just keep calling BoardController::getAll() it will again read from database again. I also tried making this call inside a config file into a variable and returning it there but laravel gave me a 500 error. So, what is the best practice/way to do it?
If you don't want to call the database everytime then the best approach that can be followed here is to use caching and cache the results.
The Approach is simple, You make a Database call once and cache the reaults and the next time you hit the same function you check the cache first whether its empty or not. If its not empty, then return the cached results.
Remember, the cache has a time limit otherwise if you change/update anything in the database then you'll have to clear the cache that is already stored.
Laravel has some features for caching the results. You can see it Here.
Also You can also view this link for more effective implementation of cache in Laravel.
Hope this helps.

Codeigniter: DB Cache check if record loaded from Database OR from Cache

I'm implementing a DB cache in Codeigniter and want to check if the record or the cache file EXISTS against the query.
As far as I read the Codeigniter Documentation probably, there is no way to check it. So, can anyone give me a hint if its possible?
Here is the flowchart that I would like to achieve
if (!$this->db->query_exists("QUERY HERE"))
// DO SOMETHING. Query Doesn't exists in CACHE
// Run the query in any case
$this->db->query("QUERY HERE");
Thank you!
It is unclear why you need/want to do this. Assuming query caching is enabled, the Codeigniter DB driver already does this checking and will use a cached query if it exists or will perform the query and cache it if not. The main thing you have to implement is clearing the appropriate cache when you perform record inserts, updates, or deletions that invalidate the cached results.
That said, the easiest way to determine if a cache exists is the CI_DB_Cache::read($sql) method. That method returns FALSE if a matching cache file is not found.
The DB driver has a public property - $CACHE. (Yes, it's an all uppercase variable name.) $CACHE is an instance of the CI_DB_Cache class.
This property will only be defined after a read type query has been performed. That could be a "catch 22" - you can't check for a cache file until a read query is performed, but you want to check before performing one.
Take a look at protected function _cache_init() in /system/database/DB_driver.php to see how the CI_DB_Cache class is instantiated. You might need to implement the same sort of thing in your code.
You might also find it useful to see how $this->CACHE is used in public function query($sql, $binds = FALSE, $return_object = NULL) in /system/database/DB_driver.php.
You might be able to determine if the query in question exists by using the approach that follows. I say might because I have not tested this.
$sql = "SELECT * from some_table";
//check that CACHE is defined
if(isset($this->db->CACHE))
{
$query = $this->db->CACHE->read($sql);
if($query === FALSE)
{
// DO SOMETHING. Query Doesn't exists in CACHE
}
}
$query will be a query result object if the cache is found.
However, you have to delete the cache before you can accomplish
// Run the query in any case
$this->db->query("QUERY HERE");
Without the delete you're going to end up with the same query result object you have already retrieved.
Keep in mind that the Caching class is completely dependent on the URI being requested by the browser. Caching uses the first two URI segments (controller class name and function name) to determine the folder that holds the cache file.

How do I check if a Cache element has expired in Laravel 5?

I want to store an array in Cache. I'm using "MemCached" as Cache driver and it works like a charm in queries, but I want to use it for my search suggestions. I've been looking at Laravel Documentation (http://laravel.com/docs/5.1/cache) but I haven't been able to find what I were looking for.
For example, I have this function in which if I find the element on Cache I get it. If not, I get results from DB and then store them on Cache for 10 minutes:
public function get_suggestions() {
if(Cache::get('search_suggestions')) {
return $search_suggestions;
} else {
$search_suggestions = Suggestions::get();
Cache::put('search_suggestions', '$search_suggestions', 10);
return $search_suggestions;
}
}
Some questions:
If the element in Cache expires, what will the Cache::get function returns?
There is a function to check if the element exists in the database but is there anyone to check if the Cache item has expires?
Is my approach OK to manage the Cache?
You could use Cache::has to check for existance.
But in your case I suggest to use Cache::remember
public function get_suggestions() {
return Cache::remember('search_suggestions', 10, function() {
return Suggestions::get();
});
}

How to use query caching in yii2 ActiveRecord

I am quoting the guide:
``Query caching is a special caching feature built on top of data caching. It is provided to cache the result of database queries.
Query caching requires a DB connection and a valid cache application component. The basic usage of query caching is as follows, assuming $db is a yii\db\Connection instance:
$result = $db->cache(function ($db) {
// the result of the SQL query will be served from the cache
// if query caching is enabled and the query result is found in the cache
return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();
});
``
I do not think that I will manually create db connection in AR classes. So how to do this in my AR models ?
I have asked the same question on yii2 forum but I got no answer. It seems that people do not know how to do query caching in Active Record.
Yii 2 now requires closures to wrap the query. AR does a query eventually so you can put that in the closure. In an AR class, get the db and wrap the query you want to use. The closure has a signature function($db) and you usually need to access more variables, so add use($variable) to make variables visible within the closure.
$db = self::getDb();
$object = $db->cache(function ($db) use($id) {
return self::findOne($id);
});
If you write to the db, the cache above won't know about it until the cache duration expires. So dependency should be added to the cache function to tell it when to invalidate the cache. Dependency gets complicated fast...
http://www.yiiframework.com/doc-2.0/yii-caching-dependency.html
maybe this help: yii2 issues on github
qiangxue commented on 11 Jan 2014
In 2.0, you need to use the following code:
$db->beginCache();
// your db query code here...
$db->endCache();

Cakephp caching assotiation result

I'm querying big chunks of data with cachephp's find. I use recursive 2. (I really need that much recursion sadly.) I want to cache the result from associations, but I don't know where to return them. For example I have a Card table and card belongs to Artist. When I query something from Card, the find method runs in the Card table, but not in the Artist table, but I get the Artist value for the Card's artist_id field and I see a query in the query log like this:
`Artist`.`id`, `Artist`.`name` FROM `swords`.`artists` AS `Artist` WHERE `Artist`.`id` = 93
My question is how can I cache this type of queries?
Thanks!
1. Where does Cake "do" this?
CakePHP does this really cool but - as you have discovered yourself - sometimes expensive operation in its different DataSource::read() Method implementations. For example in the Dbo Datasources its here. As you can see, you have no direct 'hooks' (= callbacks) at the point where Cake determines the value of the $recursive option and may decides to query your associations. BUT we have before and after callbacks.
2. Where to Cache the associated Data?
Such an operation is in my opinion best suited in the beforeFind and afterFind callback method of your Model classes OR equivalent with Model.beforeFind and Model.afterFind event listeners attached to the models event manager.
The general idea is to check your Cache in the beforeFind method. If you have some data cached, change the $recursive option to a lower value (e.g. -1, 0 or 1) and do the normal query. In the afterFind method, you merge your cached data with the newly fetched data from your database.
Note that beforeFind is only called on the Model from which you are actually fetching the data, whereas afterFind is also called on every associated Model, thus the $primary parameter.
3. An Example?
// We are in a Model.
protected $cacheKey;
public function beforeFind($query) {
if (isset($query["recursive"]) && $query["recursive"] == 2) {
$this->cacheKey = genereate_my_unique_query_cache_key($query); // Todo
if (Cache::read($this->cacheKey) !== false) {
$query["recursive"] = 0; // 1, -1, ...
return $query;
}
}
return parent::beforeFind($query);
}
public function afterFind($results, $primary = false) {
if ($primary && $this->cacheKey) {
if (($cachedData = Cache::read($this->cacheKey)) !== false) {
$results = array_merge($results, $cachedData);
// Maybe use Hash::merge() instead of array_merge
// or something completely different.
} else {
$data = ...;
// Extract your data from $results here,
// Hash::extract() is your friend!
// But use debug($results) if you have no clue :)
Cache::write($this->cacheKey, $data);
}
$this->cacheKey = null;
}
return parent::afterFind($results, $primary);
}
4. What else?
If you are having trouble with deep / high values of $recursion, have a look into Cake's Containable Behavior. This allows you to filter even the deepest recursions.
As another tip: sometimes such deep recursions can be a sign of a general bad or suboptimal design (Database Schema, general Software Architecture, Process and Functional flow of the Appliaction, and so on). Maybe there is an easier way to achieve your desired result?
The easiest way to do this is to install the CakePHP Autocache Plugin.
I've been using this (with several custom modifications) for the last 6 months, and it works extremely well. It will not only cache the recursive data as you want, but also any other model query. It can bring the number of queries per request to zero, and still be able to invalidate its cache when the data changes. It's the holy grail of caching... ad-hoc solutions aren't anywhere near as good as this plugin.
Write query result like following
Cache::write("cache_name",$result);
When you want to retrieve data from cache then write like
$results = Cache::read("cache_name");

Categories