ConfComponent DB based configuration Tutorial

By Othman ouahbi (CraZyLeGs)
ConfComponent allows you to store your configuration into the db, set and retrieve values organized into categories very easily.Now with caching.

changelog
=========
0.9.1:
- Added the ability to call the component in beforeFilter().
0.9:
- Added file caching to minimize db hits.
- Added default value for get()
- Changed the way you set and get values to be more cake-ish
- Introducing $getEmpty
- Introducing some options to deal with boolean values stored as 'true' and 'false'.
- Added setCat to set a category of configs at once
- Added setBatch to save a set of categories with their configs at once
Update 09-04-2007

Cake already has a configuration system, and it's really neat. You can put files in app/config, fill them with values and load them automagically. config::read(). This is however something different. This is db based, meaning your values are stored in the db.
Let's see how we can set it up and use it.
Download the component from: http://bakery.cakephp.org/articles/view/242
and save it as conf.php in your components' folder.

Create the DB tables


Alright, so the first thing we need to do is actually create the database tables

Download code
CREATE TABLE `configs` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `config_category_id` int(10) unsigned NOT NULL,
  `key` varchar(50) NOT NULL,
  `value` varchar(50) NOT NULL,
  PRIMARY KEY  (`id`)
)


Download code
CREATE TABLE `config_categories` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `name` varchar(50) NOT NULL,
  PRIMARY KEY  (`id`)



I gave the key and value columns a length of 50, but you can choose whatever you want.

Create the associated models


Next, we create the associated models

Model Class:

Download code <?php 
class Config extends AppModel {
    
    var 
$name "Config";
    
    var 
$belongsTo = array('ConfigCategory');
}
?>

Model Class:

Download code <?php 
class ConfigCategory extends AppModel {
    
    var 
$name "ConfigCategory";
    
    var 
$hasMany = array('Config');
}
?>


In the controller


As with any component, if we want to use it, we need to include it in the controller's $components array.

Controller Class:

Download code <?php 
var $components = array('Conf');
?>


That's all we need, we can start using our component now.
insert some data in the db tables for testing, I'll assume you have created a conf category named 'app', and some configs values.

Usage



Retrieving values


To retrieve conf values, we have two methods, get and find.

Controller Class:

Download code <?php 
$fooVal 
$this->Conf->get('app.foo');
$barVal $this->Conf->find('bar');
$foo2Val $this->Conf->get('app.foo2','defaultFoo');
$bar2Val $this->Conf->find('bar2','defaultBar');
$fooAll $this->Conf->find('foo.*');

$booleanFoo $this->Conf->get('app.foo2',true,true);

?>


Simple, in the first one, we know that the key 'foo' belongs to the conf category 'app', so we specify the name of the category and the key.
In the second, we actually don't know which category the bar key belongs to, so we use the method find. beware though, if you have two keys with the same name, it will return the first one it finds.
In the third example we introduce a new parameter, the default value that should be returned if the key we're looking for doesn't exist.
Same thing with the forth example, but with find(). And in the fifth example we introduce a new parameter that if set to true will return true instead of 'true' and false instead of 'false', it's basically to deal with boolean values.
The last example shows the syntax for getting all values of a category.

Saving values



To save values to the db, we have a method named set.

function set($key,$val,$possibleValues = null,$addCat = false,$addKey = false)


the first parameter is the key of the form category.key, the second is the value, simple. The third is ( if set ) an array of possible values, it's basically a quick validation test, $val must be one of the values in $possibleValues. You can ignore that parameter by setting it to null and indeed it's the default behavior. There are two extra parameters that default to false.
If the category passed in doesn't exist and addCat is true, the category will actually be created.
If the key passed in doesn't exist and addKey is true, the Key will actually be created.

If there is an error, set returns false.

Controller Class:

Download code <?php 
//
$this->Conf->set('app.foo','Cake!');
// weee and bar will be created if they don't exist
$this->Conf->set('app.lang','php',array('php','python','ruby'));
$this->Conf->set('weee.bar','chocolat',null,'true','true');
?>


Also, there is setCat and setBatch, the first expects a cat name and a data array where the keys are the conf names and the values are the config values something like array('i_am_a_key'=>'i_am_a_value','aww'=>'wee')

setBatch expects an array where the keys are the category names, and the values are arrays like the one passed to setCat

Caching


A new important feature has been introduced which is caching. File caching to be more specific. This was added to minimize DB hits.
A word of warning though:
Caching is really problematic in the sense, you can't know if a value has changed in the db. Maybe another user that has write access changed it and you still have the old value. There is no efficient way of detecting change. Looping through all the values in the db is just a no-no.
So, you shouldn't really change values through other interfaces than the conf component because, set() clears the cache. If you do, clear the cache manually using $this->Conf->clearCache().

The cache file is stored in app/tmp/persistent/conf.component.data.php you can change the name in the component.

Using the component in beforeFilter


Because of how components work, you can't use this component in beforeFilter. The reason is that Conf uses the startup() method to uh-huh startup. This method however is called by Cake after beforeFilter and before the current action. So as you guessed the component has not been initialized yet when beforeFilter is called and thus can't be used.
I LIE I LIE!!1 Well since the version 0.9.1 of the component, you can use the component in beforeFilter, provided you call startup() manually. I added logic so startup is not called twice by Cake.

Controller Class:

Download code <?php 
function beforeFilter()
{
  
$this->Conf->startup(&$this);
  
// call the component's methods ..
}
?>



That's it, have fun with this simple component, and as usual comments are welcome.

 

Comments 243

CakePHP Team Comments Author Comments
 

Comment

1 Looks cool

Great component, can't wait to try it out later today as I was about to implement options into my application.

One thing I think that may benefit this component however is a delete method. For example, my component or plugin may have some default values which I can override with an option from the db, but I might want to revert back to the default option. I think the easiest way would be to have a delete method in there. Just my 2p.
Posted Dec 31, 1969 by Tane Piper
 

Comment

2 caching

i wrote something very similar not too long ago for a client to be able to set some config vars, like the HTML title, username maximum lengths, etc, but to reduce db load, I had my app load all config var's when you first hit the site and write them to a session; in my version of the config->get(), the values were grabbed from the session; also I had the config component check the db instead of the session if DEBUG was enabled... then I gave the client some standard CRUD forms to modify stored config information...
Posted Mar 14, 2007 by Ryan
 

Comment

3 answers

@Tane Piper: you still can delete, $this->Conf->ccModel->Conf->delete(..)

@Ryan: Yes, minimizing db hits can help in high traffic webapps. Though session caching is problematic:
You can't know if a value has changed in the db. Maybe another user (different session ) has changed it; and you still have the old value in the session.
There is no efficient way of detecting that change.

Another solution for caching is file based, 1 db hit and x file hit is probably better than x+1 db hit.

I might look at it someday.

Regards

Posted Mar 15, 2007 by Othman ouahbi
 

Question

4 Displaying Config Data inside of a View

I am fairly new to CakePHP still. I can't seem to figure out quite how to request the data from the configs table properly.

What I am trying to do is set a variable for the siteName to be displayed in the . I have all of the code installed as per directions.

Where do I place the code:

$this->set('siteName', $this->get('display','siteName'));

I tried placing it in ConfigsController::beforeFilter() but still get,

undefined variable: siteName when I load the default page. I know I'm not doing something quite right. Any help is greatly appreciated.
Posted Mar 25, 2007 by Ron Chaplin
 

Comment

5 Adendum to 4

I meant to say:

What I am trying to do is set a variable for the siteName to be displayed in the title.

forgot to not submit html w/o the < and >
Posted Mar 25, 2007 by Ron Chaplin
 

Comment

6 You can not use the component in beforeFilter

I tried placing it in ConfigsController::beforeFilter() but still get,

Hi Ron,

You can't use the component in beforeFilter, because it fetches values in the startup() method. The startup method in cake is called after beforeFilter() and before the current action. As much as I would like it to be possible, this is how cake works. That's why I don't use the startup method in othAuth. So anyway, a solution might be to store values in the session; however, I don't want to require session to use the component. So see, it's a dilemma.
Posted Apr 4, 2007 by Othman ouahbi
 

Comment

7 Interesting and handful

This component is really useful! As to me, it could be improved adding a DataType model. Config belongsTo DataType (integer, text, boolean, array, etc.).
I would add 'default_value' and 'modified' columns in Config model. 'modified' allows to have a cache more aggressive : we can track the last modification of a row and the table, so it's possible to regenerate the cache only when needed.
'default_value' allow to retrieve default value without setting it in the get method.

Last thing, is ConfigCategory really important? I mean if the key was 'app.foocat.foo' it would be more simple isn't it ? In addition the key could be unique...
Posted Apr 6, 2007 by Julien Polo
 

Question

8 the same things wihout table config categories

Hi,
i'm interessting by the same feature but just with the Configs database table .
What will i modify in the component to make it work ?
Kind Regards.
Posted Nov 16, 2007 by foxmask