Storing the settings into database

Today I would like to show you how I am storing and fetching the settings for my applications in CakePHP. The reason why I am doing this is pretty obvious – once the application is complete and installed it’s better to have settings such as

  • How many rows per page to show in the paginated table
  • Where is the upload dir for the files
  • What is the Date format used in the application

And many other.

Ok, without other words here is the code explanation:
The Database table is very simple:

CREATE TABLE IF NOT EXISTS `settings` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `key` VARCHAR(255) NOT NULL,
  `pair` text NOT NULL,
  `description` text NOT NULL,
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
  PRIMARY KEY  (`id`)
)

Key column will store the name of the setting /I am using only capital words with underscore (_) for the replacement of the spaces.
Pair is the settings value.
Description will store the human readable description of the setting as well as some explanation of the possible values.

The other 3 fields are pretty simple – ID is the primary key of the table, created and modified are the system columns where cakePHP stores the creation and last modification date.

Pair is text because this field also can store large texts like some disclaimer or some footer notes. Otherwise it should be simple varchar, but because we wont make heavy searched in that column it’s not critical 🙂

So, I wont stop to the UI for the settings interface. Mine was created with /console/cake script and it’s quite simple:
Add – you can fill all fields in the form,
Edit – you can change only the pair /the value/ and the description,
View – preview of the single row,
Index – showing the table with Description and pair just to indicate what we have in the settings table.
Delete – action is removed – and you know why better than me :), we don’t want some “smart” guy to decide that something is not important and to wipe it out and after this YOU are guilty and you have to search and check which setting is missing and to enter it again.

Ok, let’s imagine that we have all the UI set up and working, so how to load these things into the application so every script can see them?

Let’s create small function in the AppController class which will do the job /if you don’t have that file in you app directory, just copy it from /cake/:

<?php
class AppController extends Controller {
...
/* Function which read settings from DB and populate them in constants */
function fetchSettings(){
   //Loading model on the fly
   App::import(’Model’, ‘Setting’);
   $settings = new Setting();
   //Fetching All params
   $settings_array = $settings->find('all');
   foreach($settings_array as $key=>$value){
      $constant = "__".$value['Setting']['key'];
      $val = $value['Setting']['pair'];
      if(!defined($constant)){
         eval("define(\$constant, \$val);");
      }
   }
}
...
}
?>

What is the role of this function?
First – load the Settings model on the fly /we want to make sure that the model is loaded when we call it no matter on which Controller/. Then fetching all Settings rows in $settings_array.
After this in the loop we define all key values as name of the constant and we assign to all of them the pair value.
Because it’s not possible to define them all with deinfe($key, $value) – it’s not possible with PHP. I am creating a scring which after this I evaluate as PHP code.

That’s it, you have all your settings as constants they will look like:

define(__SHOW_ROWS_PER_PAGE, 20);

Update:
As rightly pointed, the trend in the next release of CakePHP is to move all constants in core.php to be loaded and modified with Configure class. Here is the modification which will meet this code standart:

<?php
class AppController extends Controller {
...
/* Function which read settings from DB and populate them in constants */
function fetchSettings(){
   //Loading model on the fly
   loadModel('Setting');
   $settings = new Setting();
   //Fetching All params
   $settings_array = $setting->findAll();
   foreach($settings_array as $key=>$value){
      Configure::write("__".$value['Setting']['key'], $val);
   }
}
...
}
?>

So what is the change – I am setting all variables with Configure::write() function in the configurations and after this if I need to use one of them I just need to call:

$var = Configure::read('__variable_setting');

End Update

Note the double underscore in front of each constant. They are attached in that function, I put them to make the constant unique in Cake world – so far there are only functions starting with double underscore.

Now you have just to add this function in your beforeFilter() callbacks on each controller and that’s it 🙂 I am joking, you need to add this just in one place in the AppController class so here is the code:

<?php
class AppController extends Controller {
...
function beforeFilter(){
   $this->fetchSettings();  
   ...
}
...
function fetchSettings(){
...
}
}
?>

That’s it! Now everywhere, in controllers and helpers /all user interface logic/ you have these constants, so you can use them easily.

I will be glad to hear our opinion for this.

20 thoughts on “Storing the settings into database

  1. JadB

    Nice overview, but like Daniel said, would be better to use the Configure::write() / read() and avoid constants.
    Another thing also, isn’t it better to make a component for the settings and instead of a findAll(), use some conditions and specific fields to return?

  2. Nik Chankov Post author

    Thank you, for your comments guys.

    @Daniel, I am frequent reader of your blog and I read about this from your article about “Deprecation warnings…”. It’s nice approach.

    So far it’s still not in the official release /at least the settings in core are constants.

    Anyway, this is a nice hint and I will modify the code of the example.

    Update:
    About the suggestion for Component – I think the question is reasonable, but so far I didn’t use many settings /but it’s nice tohave in the future/. About limitations: if we have limitations then it’s better to use them directly from the database, but this is not the aim I think especially when I have only 10-15 rows in that table. 🙂

    Thanks again.

  3. Tane Piper

    There is actually a component for this, the ConfComponent by Othman, however I found I could only get the variables to work in views. I’ve taken it out and I’m using your technique just now, although I might end going down the Configure::write option.

    You can check out the code in action on http://dev.pastemonkey.org, as I’ve not migrated it into the live version yet.

  4. Nik Chankov Post author

    @Tane – thanks for your comment.

    I didn’t know about the component by Othman, but I think this one it quite simple to be used as component /if we need to be quite strict – yes but it’s 10 lines of code :)/

    About pastemonkey – very neat design. Hope it become popular in the future 🙂

  5. Tane Piper

    @Nik

    Thanks.

    I’ve been playing about with things and I decided to go down the Configure::write option instead. I’ve been cutting a lot of chaff of the app today, and now have it down to six simple tables 🙂

    It’ll only become popular if people know about it, so please help spread the word 🙂

  6. fLUx

    I do this, but I also cache the settings….

    Not sure why, grabbing a few settings is trivial, but I guess I am a cache whore! xD

  7. Pingback: table setting | Journal article report

  8. snowdog

    Great article. I was just lookin for such solution. One thing – loadModel is deprecated, you should use App::import(‘Model’, ‘Setting’) instead.

  9. Nik Chankov Post author

    @snowdog – glad that helps 😉
    About loadModel – I know, but the article was written before the function has been depricated, but it’s nice to be updated anyway. Thanks for the buzz.

  10. Josh Crowder

    Hi Nik, I needed to get this working, and it wasn’t so I rewrote your fetchSettings function, this one works a lot better as there is a problem with app:import()

    Here is the function now

    function fetchSettings(){
    $setting = ClassRegistry::init(‘Setting’);
    $settings = $setting->find(‘all’);
    //Fetching All params
    foreach($settings as $setting){
    Configure::write(‘__’ . $setting[‘Setting’][‘key’],$setting[‘Setting’][‘pair’]);
    }
    }

  11. Hemant Hepat

    I liked this approach..
    But what do u think about a way in which
    we will serialize the settings in a file
    and upload it to server and database will only
    point to this file.On start of the application
    we can deserialize and use the settings. If anybody have tried
    it i would like to know any problems u have faced.

  12. Nik Chankov Post author

    Hemant, the idea behind this is to make easy access to settings from regular users which don’t know how to touch files or things on the server.

    The idea of these settings is like “site e-mail” or “how many rows in tables” etc. So User can go and correct them as he like.

    If you want to store settings, you just can use /app/config/bootstrap.php.

  13. Pingback: [PRONIQUE] CakePHP Developer Links

  14. Spock

    This post was very useful to me but the code snippet provided caused errors in cakephp 2.0 so I modified it to my liking and thought I would share:

    function _fetchSettings(){
    //Loading model on the fly
    $this->loadModel(‘Setting’);

    //Fetching All params
    $settings_array = $this->Setting->find(‘all’);
    foreach($settings_array as $key=>$value){
    Configure::write($value[‘Setting’][‘key’], $value[‘Setting’][‘pair’]);
    }
    }

  15. Adam

    Thankyou for this, I am using the above 2.0 version. How do we actually call out the values within other controllers or views?

    Thanks

  16. Carlos García

    Thank you so much! Spock, can’t find the words to thank you for updating to 2.x, and Nik, this was very useful to me as well; you guys make cakes come true. Blessings for all of you.

  17. LinkinTED

    Why do you call the fetch function inside the beforeFilter() function?

    Why not in the __construct??

Leave a Reply

Your email address will not be published. Required fields are marked *