{"action":"created","issue":{"url":"https://api.github.com/repos/yiisoft/yii2/issues/12312","repository_url":"https://api.github.com/repos/yiisoft/yii2","labels_url":"https://api.github.com/repos/yiisoft/yii2/issues/12312/labels{/name}","comments_url":"https://api.github.com/repos/yiisoft/yii2/issues/12312/comments","events_url":"https://api.github.com/repos/yiisoft/yii2/issues/12312/events","html_url":"https://github.com/yiisoft/yii2/issues/12312","id":173132230,"number":12312,"title":"Proposal: Enhance cache component to cache values by callback","user":{"login":"mikehaertl","id":675062,"avatar_url":"https://avatars.githubusercontent.com/u/675062?v=3","gravatar_id":"","url":"https://api.github.com/users/mikehaertl","html_url":"https://github.com/mikehaertl","followers_url":"https://api.github.com/users/mikehaertl/followers","following_url":"https://api.github.com/users/mikehaertl/following{/other_user}","gists_url":"https://api.github.com/users/mikehaertl/gists{/gist_id}","starred_url":"https://api.github.com/users/mikehaertl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mikehaertl/subscriptions","organizations_url":"https://api.github.com/users/mikehaertl/orgs","repos_url":"https://api.github.com/users/mikehaertl/repos","events_url":"https://api.github.com/users/mikehaertl/events{/privacy}","received_events_url":"https://api.github.com/users/mikehaertl/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/yiisoft/yii2/labels/status:under%20discussion","name":"status:under discussion","color":"f7c6c7"},{"url":"https://api.github.com/repos/yiisoft/yii2/labels/type:enhancement","name":"type:enhancement","color":"d7e102"}],"state":"open","locked":false,"assignee":{"login":"SilverFire","id":4499203,"avatar_url":"https://avatars.githubusercontent.com/u/4499203?v=3","gravatar_id":"","url":"https://api.github.com/users/SilverFire","html_url":"https://github.com/SilverFire","followers_url":"https://api.github.com/users/SilverFire/followers","following_url":"https://api.github.com/users/SilverFire/following{/other_user}","gists_url":"https://api.github.com/users/SilverFire/gists{/gist_id}","starred_url":"https://api.github.com/users/SilverFire/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SilverFire/subscriptions","organizations_url":"https://api.github.com/users/SilverFire/orgs","repos_url":"https://api.github.com/users/SilverFire/repos","events_url":"https://api.github.com/users/SilverFire/events{/privacy}","received_events_url":"https://api.github.com/users/SilverFire/received_events","type":"User","site_admin":false},"assignees":[{"login":"SilverFire","id":4499203,"avatar_url":"https://avatars.githubusercontent.com/u/4499203?v=3","gravatar_id":"","url":"https://api.github.com/users/SilverFire","html_url":"https://github.com/SilverFire","followers_url":"https://api.github.com/users/SilverFire/followers","following_url":"https://api.github.com/users/SilverFire/following{/other_user}","gists_url":"https://api.github.com/users/SilverFire/gists{/gist_id}","starred_url":"https://api.github.com/users/SilverFire/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SilverFire/subscriptions","organizations_url":"https://api.github.com/users/SilverFire/orgs","repos_url":"https://api.github.com/users/SilverFire/repos","events_url":"https://api.github.com/users/SilverFire/events{/privacy}","received_events_url":"https://api.github.com/users/SilverFire/received_events","type":"User","site_admin":false}],"milestone":{"url":"https://api.github.com/repos/yiisoft/yii2/milestones/9","html_url":"https://github.com/yiisoft/yii2/milestones/2.0.x","labels_url":"https://api.github.com/repos/yiisoft/yii2/milestones/9/labels","id":831720,"number":9,"title":"2.0.x","description":"","creator":{"login":"cebe","id":189796,"avatar_url":"https://avatars.githubusercontent.com/u/189796?v=3","gravatar_id":"","url":"https://api.github.com/users/cebe","html_url":"https://github.com/cebe","followers_url":"https://api.github.com/users/cebe/followers","following_url":"https://api.github.com/users/cebe/following{/other_user}","gists_url":"https://api.github.com/users/cebe/gists{/gist_id}","starred_url":"https://api.github.com/users/cebe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cebe/subscriptions","organizations_url":"https://api.github.com/users/cebe/orgs","repos_url":"https://api.github.com/users/cebe/repos","events_url":"https://api.github.com/users/cebe/events{/privacy}","received_events_url":"https://api.github.com/users/cebe/received_events","type":"User","site_admin":false},"open_issues":434,"closed_issues":3,"state":"open","created_at":"2014-10-19T13:49:45Z","updated_at":"2016-08-25T10:26:35Z","due_on":null,"closed_at":null},"comments":7,"created_at":"2016-08-25T07:30:05Z","updated_at":"2016-08-25T11:27:50Z","closed_at":null,"body":"When using the cache component I often find myself writing the same repetitive pattern like this:\r\n\r\n```php\r\n<?php\r\nclass Article\r\n{\r\n    public function getAvailability()\r\n    {\r\n        $cache = Yii::$app->cache;\r\n        $key = 'article:availabilty:'.$this->number;\r\n        $cachedValue = $cache->get($key);\r\n        if ($cachedValue===false) {\r\n            // ... some expensive operation here e.g. a remote API call ...\r\n            $cache->set($key, $value);\r\n            return $value;\r\n        } else {\r\n            return $cachedValue;\r\n        }\r\n    }\r\n}\r\n```\r\n\r\nTo make this easier, I propose to add a new very simple method to `yii\\caching\\Cache`:\r\n\r\n```php\r\n    /**\r\n     * Gets the value stored under $key from cache. If there is no value stored for\r\n     * that key, the value is evaluated from the callback and stored to the cache.\r\n     *\r\n     * @param string $key the cache key\r\n     * @param Callable $callback the callback that evaluates the cache value\r\n     * @param int $duration the caching duration. Default is 3600.\r\n     * @param yii\\caching\\Dependency|null $dependency the cache dependency\r\n     * or `null` for none.\r\n     * @return mixed the cached value\r\n     */\r\n    public function cached($key, $callback, $duration = 3600, $dependency = null)\r\n    {\r\n        $cachedValue = $this->get($key);\r\n        if ($cachedValue===false) {\r\n            Yii::trace(\"Evaluating and storing value for $key\", __METHOD__);\r\n            $value = call_user_func($callback);\r\n            $this->set($key, $value, $duration, $dependency);\r\n            return $value;\r\n        } else {\r\n            Yii::trace(\"Serving $key from cache\", __METHOD__);\r\n            return $cachedValue;\r\n        }\r\n    }\r\n```\r\n\r\nWith this method, the above getter can be simplified to:\r\n\r\n```php\r\n    public function getAvailability()\r\n    {\r\n        $key = 'article:availabilty:'.$this->number;\r\n        return Yii::$app->cache->cached($key, function () {\r\n            // ... some expensive operation here e.g. a remote API call ...\r\n            return $value;\r\n        });\r\n    }\r\n```\r\n\r\nAs an alternative, we could even enhance the implementation of `Cache::get()`. I'm missing a `$default` argument there anyway. This is how it could look:\r\n\r\n```php\r\n/**\r\n * @param string $key the cache key\r\n * @param mixed|Callable $default the default value to return if $key does not exist.\r\n * If this is a Callable, its return value is stored to the cache under the given key.\r\n * @param int $duration the caching duration if a callback is used. Default is 3600.\r\n * @param yii\\caching\\Dependency|null $dependency the cache dependency if a callback\r\n * is used or `null` for none.\r\n * @return mixed the cached or evaluated value\r\n */\r\npublic function get($key, $default, $duration = 3600, $dependency = null)\r\n```\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/yiisoft/yii2/issues/comments/242354902","html_url":"https://github.com/yiisoft/yii2/issues/12312#issuecomment-242354902","issue_url":"https://api.github.com/repos/yiisoft/yii2/issues/12312","id":242354902,"user":{"login":"samdark","id":47294,"avatar_url":"https://avatars.githubusercontent.com/u/47294?v=3","gravatar_id":"","url":"https://api.github.com/users/samdark","html_url":"https://github.com/samdark","followers_url":"https://api.github.com/users/samdark/followers","following_url":"https://api.github.com/users/samdark/following{/other_user}","gists_url":"https://api.github.com/users/samdark/gists{/gist_id}","starred_url":"https://api.github.com/users/samdark/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/samdark/subscriptions","organizations_url":"https://api.github.com/users/samdark/orgs","repos_url":"https://api.github.com/users/samdark/repos","events_url":"https://api.github.com/users/samdark/events{/privacy}","received_events_url":"https://api.github.com/users/samdark/received_events","type":"User","site_admin":false},"created_at":"2016-08-25T11:27:50Z","updated_at":"2016-08-25T11:27:50Z","body":"In this case *technically* BC isn't broken."}}
{"actor":{"display_login":"samdark"}}