Added Cache::getOrSet()

This commit is contained in:
SilverFire - Dmitry Naumenko
2016-12-31 00:01:02 +02:00
parent cb1ff4e0fb
commit 178d319556
5 changed files with 137 additions and 19 deletions

View File

@ -11,8 +11,8 @@
$data = $cache->get($key);
if ($data === false) {
// $data нет в кэше, считаем с нуля.
// $data нет в кэше, вычисляем заново
$data = $this->calculateSomething();
// Сохраняем значение $data в кэше. Данные можно получить в следующий раз.
$cache->set($key, $data);
@ -21,6 +21,33 @@ if ($data === false) {
// Значение $data доступно здесь.
```
Начиная с версии 2.0.11, [компонент кэширования](#cache-components) предоставляет метод
[[yii\caching\Cache::getOrSet()|getOrSet()]], который упрощает код при получении, вычислении и сохранении данных.
Приведённый ниже код делает в точности то же самое, что и код в предыдущем примере:
```php
$data = $cache->getOrSet($key, function () {
return $this->calculateSomething();
});
```
Если в кэше есть данные по ключу `$key`, они будут сразу возвращены.
Иначе, будет вызвана переданная анонимная функция, вычисляющаяя значение, которое будет сохранено в кэш и возвращено
из метода.
В случае, когда анонимной функции требуются данные из внешней области видимости, можно передать их с помощью
оператора `use`. Например:
```php
$user_id = 42;
$data = $cache->getOrSet($key, function () use ($user_id) {
return $this->calculateSomething($user_id);
});
```
> Note: В [[yii\caching\Cache::getOrSet()|getOrSet()]] можно передать срока действия и зависимости кэша.
Прочтите [Срок действия кэша](#cache-expiration) и [Зависимости кеша](#cache-dependencies) чтобы узнать больше.
## Компоненты кэширования <span id="cache-components"></span>
@ -161,8 +188,8 @@ if ($data === false) {
}
```
Начиная с версии 2.0.11 вы можете изменить значение по умолчанию (бесконечность) для длительности кеширования задав
[[yii\caching\Cache::$defaultDuration|defaultDuration]] в конфигурации компонента кеша. Таким образом, можно будет
Начиная с версии 2.0.11 вы можете изменить значение по умолчанию (бесконечность) для длительности кэширования задав
[[yii\caching\Cache::$defaultDuration|defaultDuration]] в конфигурации компонента кэша. Таким образом, можно будет
не передавать значение `duration` в [[yii\caching\Cache::set()|set()]] каждый раз.
### Зависимости кэша <span id="cache-dependencies"></span>
@ -231,7 +258,7 @@ $result = Customer::getDb()->cache(function ($db) {
- `yii cache`: отображает список доступных кэширующих компонентов приложения
- `yii cache/flush cache1 cache2`: очищает кэш в компонентах `cache1`, `cache2` (можно передать несколько названий
компонентов кэширования, разделяя их пробелом)
- `yii cache/flush-all`: очищает кэш во всех кеширующих компонентах приложения
- `yii cache/flush-all`: очищает кэш во всех кэширующих компонентах приложения
> Info: Консольное приложение использует отдельный конфигурационный файл по умолчанию. Для получения должного
результата, убедитесь, что в конфигурациях консольного и веб-приложения у вас одинаковые компоненты кэширования.
@ -311,4 +338,4 @@ $result = $db->cache(function ($db) {
Кэширование запросов не работает с результатами запросов, которые содержат обработчики ресурсов. Например, при использовании типа столбца `BLOB` в некоторых СУБД, в качестве результата запроса будет выведен ресурс обработчик данных столбца.
Некоторые кэш хранилища имеют ограничение в размере данных. Например, Memcache ограничивает максимальный размер каждой записи до 1 Мб. Таким образом, если результат запроса превышает этот предел, данные не будут закешированы.
Некоторые кэш хранилища имеют ограничение в размере данных. Например, Memcache ограничивает максимальный размер каждой записи до 1 Мб. Таким образом, если результат запроса превышает этот предел, данные не будут закэшированы.

View File

@ -13,8 +13,8 @@ a [cache component](#cache-components):
$data = $cache->get($key);
if ($data === false) {
// $data is not found in cache, calculate it from scratch
$data = $this->calculateSomething();
// store $data in cache so that it can be retrieved next time
$cache->set($key, $data);
@ -23,6 +23,32 @@ if ($data === false) {
// $data is available here
```
Since version 2.0.11, [cache component](#cache-components) provides [[yii\caching\Cache::getOrSet()|getOrSet()]] method
that simplifies code for data getting, calculating and storing. The following code does exactly the same as the
previous example:
```php
$data = $cache->getOrSet($key, function () {
return $this->calculateSomething();
});
```
When cache has data associated with the `$key`, the cached value will be returned.
Otherwise, the passed anonymous function will be executed to calculate the value that will be cached and returned.
If the anonymous function requires some data from the outer scope, you can pass it with the `use` statement.
For example:
```php
$user_id = 42;
$data = $cache->getOrSet($key, function () use ($user_id) {
return $this->calculateSomething($user_id);
});
```
> Note: [[yii\caching\Cache::getOrSet()|getOrSet()]] method supports duration and dependencies as well.
See [Cache Expiration](#cache-expiration) and [Cache Dependencies](#cache-dependencies) to know more.
## Cache Components <span id="cache-components"></span>
@ -118,6 +144,8 @@ All cache components have the same base class [[yii\caching\Cache]] and thus sup
value will be returned if the data item is not found in the cache or is expired/invalidated.
* [[yii\caching\Cache::set()|set()]]: stores a data item identified by a key in cache.
* [[yii\caching\Cache::add()|add()]]: stores a data item identified by a key in cache if the key is not found in the cache.
* [[yii\caching\Cache::getOrSet()|getOrSet()]]: retrieves a data item from cache with a specified key or executes passed
callback, stores return of the callback in a cache by a key and returns that data.
* [[yii\caching\Cache::multiGet()|multiGet()]]: retrieves multiple data items from cache with the specified keys.
* [[yii\caching\Cache::multiSet()|multiSet()]]: stores multiple data items in cache. Each item is identified by a key.
* [[yii\caching\Cache::multiAdd()|multiAdd()]]: stores multiple data items in cache. Each item is identified by a key.

View File

@ -85,9 +85,11 @@ Yii Framework 2 Change Log
- Enh #13074: Improved `yii\log\SyslogTarget` with `$options` to be able to change the default `openlog` options (timbeks)
- Bug: #12969: Improved unique ID generation for `yii\widgets\Pjax` widgets (dynasource, samdark, rob006)
- Enh #13122: Optimized query for information about foreign keys in `yii\db\oci` (zlakomanoff)
- Enh #11959: Added `yii\caching\Cache::getOrSet()` method (silverfire)
- Enh #13202: Refactor validateAttribute method in UniqueValidator (developeruz)
- Enh: Added constants for specifying `yii\validators\CompareValidator::$type` (cebe)
- Enh #11959: Added `yii\caching\Cache::closure()` method to cache closures (silverfire)
2.0.10 October 20, 2016
-----------------------

View File

@ -7,6 +7,7 @@
namespace yii\caching;
use Yii;
use yii\base\Component;
use yii\helpers\StringHelper;
@ -537,4 +538,45 @@ abstract class Cache extends Component implements \ArrayAccess
{
$this->delete($key);
}
/**
* Method combines both [[set()]] and [[get()]] methods to retrieve value identified by a $key,
* or to store the result of $closure execution if there is no cache available for the $key.
*
* Usage example:
*
* ```php
* public function getTopProducts($count = 10) {
* $cache = $this->cache; // Could be Yii::$app->cache
* return $cache->getOrSet(['top-n-products', 'n' => $count], function ($cache) use ($count) {
* return Products::find()->mostPopular()->limit(10)->all();
* }, 1000);
* }
* ```
*
* @param mixed $key a key identifying the value to be cached. This can be a simple string or
* a complex data structure consisting of factors representing the key.
* @param \Closure $closure the closure that will be used to generate a value to be cached.
* In case $closure returns `false`, the value will not be cached.
* @param int $duration default duration in seconds before the cache will expire. If not set,
* [[defaultDuration]] value will be used.
* @param Dependency $dependency dependency of the cached item. If the dependency changes,
* the corresponding value in the cache will be invalidated when it is fetched via [[get()]].
* This parameter is ignored if [[serializer]] is `false`.
* @return mixed result of $closure execution
* @since 2.0.11
*/
public function getOrSet($key, \Closure $closure, $duration = null, $dependency = null)
{
if (($value = $this->get($key)) !== false) {
return $value;
}
$value = call_user_func($closure, $this);
if (!$this->set($key, $value, $duration, $dependency)) {
Yii::warning('Failed to set cache value for key ' . json_encode($value), __METHOD__);
}
return $value;
}
}

View File

@ -24,6 +24,7 @@ function microtime($float = false)
namespace yiiunit\framework\caching;
use yii\caching\Cache;
use yii\caching\TagDependency;
use yiiunit\TestCase;
/**
@ -99,22 +100,22 @@ abstract class CacheTestCase extends TestCase
}
/**
* @return array testing mset with and without expiry
* @return array testing multiSet with and without expiry
*/
public function msetExpiry()
public function multiSetExpiry()
{
return [[0], [2]];
}
/**
* @dataProvider msetExpiry
* @dataProvider multiSetExpiry
*/
public function testMset($expiry)
public function testMultiset($expiry)
{
$cache = $this->getCacheInstance();
$cache->flush();
$cache->mset([
$cache->multiSet([
'string_test' => 'string_test',
'number_test' => 42,
'array_test' => ['array_test' => 'array_test'],
@ -167,14 +168,14 @@ abstract class CacheTestCase extends TestCase
$this->assertTrue($cache->get('bool_value'));
}
public function testMget()
public function testMultiGet()
{
$cache = $this->prepare();
$this->assertEquals(['string_test' => 'string_test', 'number_test' => 42], $cache->mget(['string_test', 'number_test']));
$this->assertEquals(['string_test' => 'string_test', 'number_test' => 42], $cache->multiGet(['string_test', 'number_test']));
// ensure that order does not matter
$this->assertEquals(['number_test' => 42, 'string_test' => 'string_test'], $cache->mget(['number_test', 'string_test']));
$this->assertEquals(['number_test' => 42, 'non_existent_key' => null], $cache->mget(['number_test', 'non_existent_key']));
$this->assertEquals(['number_test' => 42, 'string_test' => 'string_test'], $cache->multiGet(['number_test', 'string_test']));
$this->assertEquals(['number_test' => 42, 'non_existent_key' => null], $cache->multiGet(['number_test', 'non_existent_key']));
}
public function testDefaultTtl()
@ -220,13 +221,13 @@ abstract class CacheTestCase extends TestCase
$this->assertEquals(13, $cache->get('add_test'));
}
public function testMadd()
public function testMultiAdd()
{
$cache = $this->prepare();
$this->assertFalse($cache->get('add_test'));
$cache->madd([
$cache->multiAdd([
'number_test' => 13,
'add_test' => 13,
]);
@ -250,4 +251,22 @@ abstract class CacheTestCase extends TestCase
$this->assertTrue($cache->flush());
$this->assertFalse($cache->get('number_test'));
}
public function testGetOrSet()
{
$cache = $this->prepare();
$dependency = new TagDependency(['tags' => 'test']);
$expected = 'SilverFire';
$loginClosure = function ($cache) use (&$login) { return 'SilverFire'; };
$this->assertEquals($expected, $cache->getOrSet('some-login', $loginClosure, null, $dependency));
// Call again with another login to make sure that value is cached
$loginClosure = function ($cache) use (&$login) { return 'SamDark'; };
$this->assertEquals($expected, $cache->getOrSet('some-login', $loginClosure, null, $dependency));
$dependency->invalidate($cache, 'test');
$expected = 'SamDark';
$this->assertEquals($expected, $cache->getOrSet('some-login', $loginClosure, null, $dependency));
}
}