CakePHP Cache::write() Can keys be grouped by model? - php

Consider the following;
Cache::write('Model.key1' , 'stuff');
Cache::write('AnotherModel.key1' , 'stuff');
Cache::write('Model.key2' , 'stuff');
Can I delete a group of keys from the Cache?
For instance, if I wanted to clear all cached data for "Model" but leave "AnotherModel" in the cache, I would like to use the following;
Cache::delete('Model.*');
Can this kind of thing be achieved in CakePHP 1.3.x?
Thanks!

For those just Googling this issue (as I was), Cake 2.2 now supports this kind of functionality (without having to create separate cache configs for each 'group').
There is a little explanation here, although it lacks some details:
http://book.cakephp.org/2.0/en/core-libraries/caching.html#using-groups
But this is what I did in my app and it appears to work well. ;-)
In /app/Config/core.php
Cache::config('default', array(
'engine' => $engine,
...
'groups' => ['navigation'],
));
Model afterSave hook:
function afterSave($created) {
// This deletes all keys beginning with 'navigation'
Cache::clearGroup('navigation');
parent::afterSave($created);
}
Then in my controller/model that requires an expensive query...
// We create a unique key based on parameters passed in
$cacheKey = "navigation.$sitemapId.$levelsDeep.$rootPageId";
$nav = Cache::read($cacheKey);
if (!$nav) {
$nav = $this->recursiveFind(
'ChildPage',
['page_id' => $rootPageId],
$levelsDeep
);
Cache::write($cacheKey, $nav);
}

You can set up a different cache config for each model such that each config has a different path.
http://book.cakephp.org/view/1515/Cache-config
I don't see a way to completely delete all data using the cache class but you could write your own function to delete all of the data (cached files) out of the specific cache directory for the specific model. By using different config files for different models, you can isolate the model's cached data into a unique directory for each model and then manually delete the contents when you want to flush the data for that model.

Related

How to save data on server without using database?

I have an application developed with Laravel. My software has settings that are used globally and should be available in all controllers (such as default information). I take this information from the database in the main controller every time a request is sent and save it in a variable.
namespace App\Http\Controllers;
class Controller extends BaseController
{
protected $config;
public function __construct()
{
$this->config= DB::table('config')->get();
}
}
Is there a way to save and use this information without the intervention of a database? I don't want to use sessions.
It is better if a solution is introduced using laravel packages.
Thanks
Assuming that you collection doesn't hold a lot of data, you can always put it inside your custom config. Create a php file inside your app/config directory, where you can put all your values like this:
<?php
return [
'key1' => value1,
'key2' => value2,
];
You can create any data structure here that you might need. Now, when you need to read single key from this data, you can use Laravel's config() helper:
$config = config('config_name.key');
If you want to get whole collection of the data, you can do it with the same config() helper, like this:
$config = config('app.config_name');
Hope that I understood your question right, and that this can lead you in right direction. You can read more about config on official documentation.

CodeIgniter how to set autoloaded database dynamically

Using CodeIgniter 3, I autoload my database config, now how do I change the database connected dynamically ? I was thinking like using session to pass the database value, but session cannot be used in the database config file.
I know I can manually load database and change it, but then I have to call and load the database in every controller and I have tons of the controller, therefore I would like to avoid setting the database manually.
There is probably more than one way to do what you want. The solution shown here uses CodeIgniter’s "Hooks" feature. Specifically, it uses the "post_controller_constructor" hook to match the name of a controller with a specific database configuration defined in database.php.
After the hook does its work the application can make calls to the database in the typical CI way using $this->db->. For example...
$query = $this->db->get('mytable');
This solution is based on the assumption that only one database connection is need for any given controller. This means that all methods in that controller (or any models loaded by the controller) use the same connection.
Here's how it is done.
In application/config/config.php
$config['enable_hooks'] = TRUE;
In application/config/hooks.php
$hook['post_controller_constructor'][] = array(
'class' => '',
'function' => 'set_db_connection',
'filename' => 'post_controller_hook.php',
'filepath' => 'hooks'
);
The file post_controller_hook.php is where the work gets done. It uses lists of controller names to determine which database config is to be loaded.
The list ($controller_lists) contains sub-arrays which group controller names by the db configuration needed. A search is done through each sub-array to find the matching controller name. When a controller name is found the key of that sub-array is the db config to be loaded. If no match is found the 'default' config is used.
The $controller_lists array is hard-coded here but it could easily be loaded from a config file instead. A config file might make maintaining the lists easier.
file application/config/post_controller_hook.php
function set_db_connection()
{
$CI = get_instance();
$controller = $CI->router->class;
$loadConfig = 'default'; //if nothing found in lists we're still good
$controller_lists = array(
'config2' => ['profile'],
'config3' => ['discusion', 'home'],
'config4' => ['suppliers', 'customers', 'inventory', 'orders']
);
foreach($controller_lists as $config_name => $list)
{
if(in_array($controller, $list))
{
$loadConfig = $config_name;
break;
}
}
$CI->load->database($loadConfig);
}
The ability to not load a database for controllers that don't need one could be added if that was desirable. But I'm not going there.
As stated earlier, this solution uses the assumption that only one database configuration (connection) is used for any given controller. If certain methods of a controller need to use a different db configuration this solution becomes more complicated.
Adding the method to the search is easy. The first few lines of set_db_connection() would look like this.
function set_db_connection()
{
$CI = get_instance();
$controller = $CI->router->class;
$method = $CI->router->method;
if($method !== 'index')
{
$controller .= '/'.$method; //append method name
}
$loadConfig = 'default'; //if nothing found in lists we're still good
So now $controller will hold either 'controller/method', or just 'controller' if index() is to being called.
Consider a controller called Viewstate with three methods
class Viewstate extends CI_Controller
{
public function index(){
//uses db 'config4'
}
public function report(){
//uses db 'Config2'
}
public function process(){
//uses db 'Config3'
}
}
We have to include each 'viewstate/method' in the sub-arrays like this.
$controller_lists = array(
'config2' => ['profile', 'viewstate/report'],
'config3' => ['disscusion', 'home', 'viewstate/process'],
'config4' => ['viewstate', 'customers', 'inventory', 'orders']
);
//the rest of the function is as shown earlier
Any 'viewstate/method' not in the search lists it will be assigned the 'default' db config. So it's easy to sort the various needs of viewstate.
The problem is that every 'controller/method' in the site must now be included in the search lists. If the Profile controller has ten methods every combination must now be in the config2 sub-array. So if there are lots of controllers and controller/methods this solution is a poor choice. There might be an elegant way around this problem but that's probably a topic for a new question.

Laravel 5 - How to pass 'route' to view when generating cache via render() method

I am creating a cache generator to cache all the content on my site to redis.
On request middleware checks if Redis key exists and returns pre-compiled HTML.
The problem I have is when generating the cache there is no true 'Request' therefore 'Route::currentRouteName()' isn't returning anything.
However I am generating the cache key by using named routes, EG:
$key = route('named-route', [], false);
$controller = new ContentController();
$content = $controller->function()->render();
Is it possible to retrieve the $key value inside the Controller globally, given I have many different controllers and I don't want to copy and paste this, I thought about creating some sort of parent::key value but wanted some advice if there is a better way to achieve this?

ZF2: Custom user mapper for ZfcUser module

I`ve added module ZfcUser on my Zend Framework 2 application.
But I have to use existing database table,
which has slightly different column names than the default table structure for ZfcUser.
In ZfcUser wiki page it says that it is possible to use custom mapper if my model doesn`t conform to the provided interface.
And since my database table is different than default, my user entity class is also different than
standard ZfcUser\Entity\User. But I can tell ZfcUser to work with my own class easily
by overriding setting in file config/autoload/zfcuser.global.php:
'user_entity_class' => 'MyApp\Entity\MyUser',
But I`ve not found an easy way to tell ZfcUser to use my mapper class so far.
I have only found that the mapper is created by ZfcUser\Module::getServiceConfig()
inside which, I can see the mapper is returned from its factory function:
// ...
public function getServiceConfig()
{
return array(
// ...
'factories' => array(
// ...
'zfcuser_user_mapper' => function ($sm) {
$options = $sm->get('zfcuser_module_options');
$mapper = new Mapper\User();
$mapper->setDbAdapter($sm->get('zfcuser_zend_db_adapter'));
$entityClass = $options->getUserEntityClass();
$mapper->setEntityPrototype(new $entityClass);
$mapper->setHydrator(new Mapper\UserHydrator());
return $mapper;
},
// ...
Is there a way to make ZfcUser to use my custom user mapper class?
I've had the same problem as you, but managed to log in to my application at last. I followed Rob's advice and created my own service factory within my existing user module. Unfortunately Bernhard is also spot on. You kinda have to dig into the ZfcUser source code to get it to work. The project I am working on now has a MSSQL server and I must say that it's been tough getting a handle on things. I've ended up tweaking only one function in the ZfcUser source to get the login page to work.
I only need log in functionality for the current application, but the upcoming project is much more role driven. I was looking for something that wouldn't be too complicated to hook up quickly, and at the same time offer more options and possibilities for the future.
Here is what I did for now and what I've learned:
I copied the Entity and Mapper folders from the ZfcUser directory over to my existing b2bUser (my module) folder. Everything...even the Exception folder inside Mapper. It might not be necessary, but I wasn't in the mood to figure out dependencies.
In the zfcuser.global.php file, my active configuration looks as follows:
'user_entity_class' => 'b2bUser\Entity\User',
'enable_registration' => false,
'enable_username' => true,
'auth_identity_fields' => array( 'username' ),
'login_redirect_route' => 'home',
'enable_user_state' => false,
I left the rest of the settings on default. I removed the email option from auth identities because they won't use email addresses to log into the system. The user_entity_class is the one I copied over...
Module.php (b2bUser)
Copied the following to the service manager config:
'zfcuser_user_mapper' => function ($sm) {
$mapper = new Mapper\User();
$mapper->setDbAdapter($sm->get('Zend\Db\Adapter\Adapter'));
$mapper->setEntityPrototype(new Entity\User());
$mapper->setHydrator(new Mapper\UserHydrator());
return $mapper;
},
After the setup was done, I changed the namespaces, etc of the files in Entity and Mapper to reflect their new home. Changed the Entity and the Interface to reflect my own data structure. I did the same with the Mapper files and made sure the variable names in the Hydrator file is the same as my database column names.
I left the AbstractDbMapper file where it was. But this is the file I tweaked a bit.
Here is what mine looks like. The SQLSRV driver was full of pooh, complaining the whole time about an object or a string...
protected function select(Select $select, $entityPrototype = null, HydratorInterface $hydrator = null)
{
$this->initialize();
$selectString = $this->getSlaveSql()->getSqlStringForSqlObject($select);
$stmt = $this->getDbAdapter()->driver->createStatement($selectString);
$stmt->prepare();
$res = $stmt->execute($stmt);
$resultSet = new HydratingResultSet($hydrator ?: $this->getHydrator(),
$entityPrototype ?: $this->getEntityPrototype());
$resultSet->initialize($res);
return $resultSet;
}
And that is that. I hope it helps someone to get it up and running on their own system at least. I won't leave mine like this, but it was a bit of a mission to get it to work.
Ceate your own service factory for zfcuser_user_mapper and it will get used.
Currently there seems to be no easy way to change the table structure without modifying the ZfcUser source. There's a pull request on Github that should solve this problem:
https://github.com/ZF-Commons/ZfcUser/pull/174

Zend how to use cache component

Let's say you have this scenario:a simple blog home-page that loads both static content as well as dynamic content.
The static content is composed of images that rarely changes.I also have database-driven,dynamic content.The dynamic content consists in all of your blog posts (text and image) and related users comments.The dynamic content changes periodically from every hour to every day.
How would you go with caching?And in particular supposing a user is leaving a comment or the admin is adding/editing a post to you would want to manually trigger the cache clearing to have the update version of this blog home-page?
thanks for your patience.
Luca
thanks again
First, a link: http://framework.zend.com/manual/1.11/en/zend.cache.html
Basically, what you need to do is set up a cache mechanism and then manually call it when you want to retrieve something from the cache.
In the bootstrap, I might have this code:
public function _initCache () {
$cache = Zend_Cache::factory(
'Core',
'File',
array(
'lifetime' => 3600 * 24, //cache is cleaned once a day
'automatic_serialization' => true
),
array('cache_dir' => APPLICATION_PATH.'/cache')
);
Zend_Db_Table_Abstract::setDefaultMetadataCache($cache); //cache database table schemata metadata for faster SQL queries
Zend_Registry::set('Cache', $cache);
}
Then, you may use the load() and save() functions to manipulate the cache. An example from my controller:
$cache = Zend_Registry::get('Cache');
if (!$this->menu = $cache->load('main_menu')) {
$model = new Model_Menu();
$this->menu = $model->get();
$cache->save($this->menu,'main_menu');
}
Here, I check whether the key "main_menu" is cached. If a cache miss is scored, the main menu is generated and cached.
If I edit the main menu, I'll want to regenerate the cache as well. I simply call this:
Zend_Registry::get('Cache')->remove('main_menu');
It's pretty simple, just read the documentation. It's well written.
Zend cache provide a very simple way to store data in cache and to increase the speed. Zend uses frontend and back end to caching. Front end is useful to access or operate the cache. Back end is useful to store data in File , memcache, Sqlite etc.
First of all initialize the fronted and backed in bootstrap file by creating on function in bootstrap file.
protected function _initCache(){
$frontend= array(
'lifetime' => 7200,
'automatic_serialization' => true
);
$backend= array(
'cache_dir' => '../application/tmp/',
);
$cache = Zend_Cache::factory('core',
'File',
$frontend,
$backend
);
Zend_Registry::set('cache',$cache);
}
Then use the zend cache factory to define the cache object. The parameter core define the zend cache core means of generic type File parameter is to define the cache storage means where to store the records of cache then second and forth is for frontend and backend.
Now register that cache array using zend registry so that you can use that are in any controller , model etc.
Define Below code in any controller or any model where you want to use caching of data.
$result1 =””;
$cache = Zend_Registry::get('cache');
if(!$result1 = $cache->load('mydata')) {
echo 'caching the data…..';
$data=array(1,2,3);
$cache->save($data, 'mydata');
} else {
echo 'retrieving cache data…….';
Zend_Debug::dump($result1);
}
First of all in above code we get the cache array. Now if result one is not set then caching done means the file is generated at the path that you define in back-end array
For the Next time page load that data is retrieve from the file where the caching data store.
You can check the file as per defined path.
In that file data is in json format.
So the basic usage of cache is shown by #mingos. He talks about generic cache, which is good. However, ZF has few different cache mechanisms that you can use for different things. You don't need to limit yourself for one type of cache. You can use a mixture of them. For example, for caching your static content Zend_Cache_Frontend_Page would be worth considering as it would generate a full html file of your static pages. If you have lots of config files, e.g. long routes.ini or whatever, you can cache them using Zend_Cache_Frontend_File. With this you would save time parsing the ini files for every request. Significant parts of your views could be cached using Zend_Cache_Frontend_Output, etc.
What to cache and when to update a cache is a tricky question. It all depends on how fast and how often your content is changing. For example, if you have like 100 new comments per second, there is no point in cleaning your comment cache 100 times per second (i.e. for each new comment). It would be better maybe to have comments for each post cached separately from the comments of other posts. Then you would clean/refresh a cache associated with only this post.
A simple cache is one that times out after the defined period of time. This keeps the caching layer simple and easy to implement. The Zend Manual has more information on caching basics.
However real-time information and cached information are two worlds. If you need real-time, don't cache.
If you make the caching layer too complex, you can destroy your whole application.

Categories