Static Data Helper


July 27, 2007
<strong>Static Data in my Database?</strong> Why would I want to store data in the database that never changes? <br/> <br/><strong>First,</strong> with proper usage you can minimize the load on your database server by permanently caching the results of the queries. <br/><strong>Secondly,</strong> it becomes easy to JOIN to when you do need it. You never should need it though. <br/><strong>Third,</strong> it becomes very easy for you as a developer to get data from a single row or get a full list of data with which you can create a &lt;SELECT&gt; dropdown. <br/><strong>Fourth,</strong> It becomes exceedingling easy to say add a 51st state to your website. Just add a row to the table, and delete the cache. Perhaps adding an Order status at the request of a customer is a more common thing. <br/> <br/>A very simple to understand example of this is states. All you need to do is store all states in your database, and say you want to store where each user in your system lives. In the <em>Users</em> table create a <em>state_id</em> field. After you have installed the Helper, you can simply call <em>&lt;?= $static-&gt;status($User, 'name'); ?&gt;</em> to get the name of the state. This will not query your database, and will add zero extra load onto your database server. And notice how easy it was for you to write in code. <br/> <br/>Some real life usage examples <br/><div class="code"><pre><span class="x">&lt;!-- it&#39;s easy to make dropdown to choose a country --&gt;</span> <span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nv">$form</span><span class="o">-&gt;</span><span class="na">input</span><span class="p">(</span><span class="s1">&#39;country_id&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;options&#39;</span> <span class="o">=&gt;</span> <span class="nv">$static</span><span class="o">-&gt;</span><span class="na">countries</span><span class="p">(</span><span class="s1">&#39;id&#39;</span><span class="p">,</span> <span class="s1">&#39;name&#39;</span><span class="p">)));</span> <span class="cp">?&gt;</span><span class="x"></span> <span class="x">&lt;!-- quickly get the description of the status with ID = 40 --&gt;</span> <span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nv">$static</span><span class="o">-&gt;</span><span class="na">status</span><span class="p">(</span><span class="m">40</span><span class="p">,</span> <span class="s1">&#39;desc&#39;</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span> <span class="x">&lt;!-- dropdown of abbreviated states (NY, CA, etc) --&gt;</span> <span class="cp">&lt;?</span><span class="o">=</span> <span class="nv">$form</span><span class="o">-&gt;</span><span class="na">select</span><span class="p">(</span><span class="s1">&#39;user/state_id&#39;</span><span class="p">,</span> <span class="nv">$static</span><span class="o">-&gt;</span><span class="na">states</span><span class="p">(</span><span class="s1">&#39;id&#39;</span><span class="p">,</span> <span class="s1">&#39;abbrev&#39;</span><span class="p">),</span> <span class="nv">$user</span><span class="p">[</span><span class="s1">&#39;User&#39;</span><span class="p">][</span><span class="s1">&#39;state_id&#39;</span><span class="p">]);</span> <span class="cp">?&gt;</span><span class="x"></span> </pre></div> <div style='display:none'>&lt;!-- it's easy to make dropdown to choose a country --&gt; <br/>&lt;?php echo $form-&gt;input('country_id', array('options' =&gt; $static-&gt;countries('id', 'name'))); ?&gt; <br/> <br/>&lt;!-- quickly get the description of the status with ID = 40 --&gt; <br/>&lt;?php echo $static-&gt;status(40, 'desc'); ?&gt; <br/> <br/>&lt;!-- dropdown of abbreviated states (NY, CA, etc) --&gt; <br/>&lt;?= $form-&gt;select('user/state_id', $static-&gt;states('id', 'abbrev'), $user['User']['state_id']); ?&gt; <br/></div> <br/> <br/>Sorry, you have to get the data to put into your database on your own. This Helper will cache the data from that data for 1 year, making it so the data is almost never selected from your database. <br/> <br/>Installation1. Take the code below and put it into the file app/views/helpers/static.php <br/>2. Config the file if you want (I suggest changing the $tmp_folder var) <br/>3. In your controller add <em>var $helpers = array('Static'); <br/> <br/>The Code (intended to go in a helper named static.php) <br/><div class="code"><pre><span class="cp">&lt;?php</span> <span class="c">/**</span> <span class="c"> * CakePHP Helper to handle use of data in database that never changes</span> <span class="c"> * </span> <span class="c"> * array plural($order, $field=&#39;name&#39;); // ready for use in FormHelper::select</span> <span class="c"> * string singular($id, $field=&#39;name&#39;);</span> <span class="c"> * </span> <span class="c"> * usage examples (in Views):</span> <span class="c"> * </span> <span class="c"> * $static-&gt;states(&#39;abbrev&#39;); // all states sorted by abbrev</span> <span class="c"> * $static-&gt;statuses(&#39;id&#39;, &#39;name&#39;); // get list of all status names, sorted by id </span> <span class="c"> * $static-&gt;status(40, &#39;desc&#39;); // description of status with id 40</span> <span class="c"> * $status-&gt;countries(); // get all countries sorted by id</span> <span class="c"> * </span> <span class="c"> * @author Rob Conner aka rtconner</span> <span class="c"> */</span> <span class="k">class</span> <span class="nc">StaticHelper</span> <span class="k">extends</span> <span class="nx">Helper</span> <span class="p">{</span> <span class="c">// set this to a folder within your app/tmp folder to store the data (no trailing slash)</span> <span class="k">var</span> <span class="nv">$tmp_folder</span> <span class="o">=</span> <span class="s1">&#39;cache&#39;</span><span class="p">;</span> <span class="k">var</span> <span class="nv">$cache_expires</span> <span class="o">=</span> <span class="s1">&#39;+1 Year&#39;</span><span class="p">;</span> <span class="k">var</span> <span class="nv">$database</span> <span class="o">=</span> <span class="s1">&#39;default&#39;</span><span class="p">;</span> <span class="c">//plural only please, will enable both plural and singular methods (status needs to be added to inflections.php)</span> <span class="k">var</span> <span class="nv">$methods</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;states&#39;</span><span class="p">,</span> <span class="s1">&#39;countries&#39;</span><span class="p">,</span> <span class="s1">&#39;statuses&#39;</span><span class="p">);</span> <span class="k">function</span> <span class="nf">__call</span><span class="p">(</span><span class="nv">$method</span><span class="p">,</span> <span class="nv">$args</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$plural</span> <span class="o">=</span> <span class="nx">Inflector</span><span class="o">::</span><span class="na">pluralize</span><span class="p">(</span><span class="nv">$method</span><span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="nf">in_array</span><span class="p">(</span><span class="nv">$plural</span><span class="p">,</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">methods</span><span class="p">))</span> <span class="p">{</span> <span class="nf">array_unshift</span><span class="p">(</span><span class="nv">$args</span><span class="p">,</span> <span class="nv">$method</span><span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="nv">$plural</span><span class="o">==</span><span class="nv">$method</span><span class="p">)</span> <span class="c">// return list of all static items</span> <span class="k">return</span> <span class="nf">call_user_func_array</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="o">&amp;</span><span class="nv">$this</span><span class="p">,</span> <span class="s1">&#39;all&#39;</span><span class="p">),</span> <span class="nv">$args</span><span class="p">);</span> <span class="k">else</span> <span class="c">// return single item</span> <span class="k">return</span> <span class="nf">call_user_func_array</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="o">&amp;</span><span class="nv">$this</span><span class="p">,</span> <span class="s1">&#39;row&#39;</span><span class="p">),</span> <span class="nv">$args</span><span class="p">);</span> <span class="p">}</span> <span class="k">else</span> <span class="c">// try to imitate php&#39;s errors</span> <span class="nf">trigger_error</span><span class="p">(</span><span class="nf">sprintf</span><span class="p">(</span><span class="s1">&#39;Call to undefined function: %s::%s().&#39;</span><span class="p">,</span> <span class="nf">get_class</span><span class="p">(</span><span class="nv">$this</span><span class="p">),</span> <span class="nv">$method</span><span class="p">),</span> <span class="nx">E_USER_ERROR</span><span class="p">);</span> <span class="p">}</span> <span class="c">/**</span> <span class="c"> * @param string $table to get list from</span> <span class="c"> * @param string $order sort order of list</span> <span class="c"> * @param string $field field data to return</span> <span class="c"> * @return array</span> <span class="c"> */</span> <span class="k">function</span> <span class="nf">all</span><span class="p">(</span><span class="nv">$table</span><span class="p">,</span> <span class="nv">$order</span><span class="o">=</span><span class="s1">&#39;id&#39;</span><span class="p">,</span> <span class="nv">$field</span><span class="o">=</span><span class="s1">&#39;name&#39;</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$cache_name</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">tmp_folder</span><span class="o">.</span><span class="nx">DS</span><span class="o">.</span><span class="nv">$table</span><span class="o">.</span><span class="s1">&#39;_&#39;</span><span class="o">.</span><span class="nv">$order</span><span class="o">.</span><span class="s1">&#39;_&#39;</span><span class="o">.</span><span class="nv">$field</span><span class="p">;</span> <span class="nv">$cache_data</span> <span class="o">=</span> <span class="nx">cache</span><span class="p">(</span><span class="nv">$cache_name</span><span class="p">,</span> <span class="k">null</span><span class="p">,</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">cache_expires</span><span class="p">,</span> <span class="s1">&#39;tmp&#39;</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="k">empty</span><span class="p">(</span><span class="nv">$cache_data</span><span class="p">))</span> <span class="p">{</span> <span class="nv">$db</span> <span class="o">=&amp;</span> <span class="nx">ConnectionManager</span><span class="o">::</span><span class="na">getDataSource</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">database</span><span class="p">);</span> <span class="nv">$table</span> <span class="o">=</span> <span class="nx">Inflector</span><span class="o">::</span><span class="na">tableize</span><span class="p">(</span><span class="nv">$table</span><span class="p">);</span> <span class="nv">$qs</span> <span class="o">=</span> <span class="s1">&#39;SELECT * FROM `&#39;</span><span class="o">.</span><span class="nv">$table</span><span class="o">.</span><span class="s1">&#39;` ORDER BY `&#39;</span><span class="o">.</span><span class="nv">$order</span><span class="o">.</span><span class="s1">&#39;`;&#39;</span><span class="p">;</span> <span class="nv">$results</span> <span class="o">=</span> <span class="nv">$db</span><span class="o">-&gt;</span><span class="na">fetchAll</span><span class="p">(</span><span class="nv">$qs</span><span class="p">,</span> <span class="k">false</span><span class="p">,</span> <span class="s1">&#39;States&#39;</span><span class="p">);</span> <span class="nv">$list</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span> <span class="k">foreach</span><span class="p">(</span><span class="nv">$results</span> <span class="k">as</span> <span class="nv">$r</span><span class="p">)</span> <span class="nv">$list</span><span class="p">[</span><span class="nv">$r</span><span class="p">[</span><span class="nv">$table</span><span class="p">][</span><span class="s1">&#39;id&#39;</span><span class="p">]]</span> <span class="o">=</span> <span class="nv">$r</span><span class="p">[</span><span class="nv">$table</span><span class="p">][</span><span class="nv">$field</span><span class="p">];</span> <span class="nv">$cache_data</span> <span class="o">=</span> <span class="nx">cache</span><span class="p">(</span><span class="nv">$cache_name</span><span class="p">,</span> <span class="nf">serialize</span><span class="p">(</span><span class="nv">$list</span><span class="p">),</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">cache_expires</span><span class="p">,</span> <span class="s1">&#39;tmp&#39;</span><span class="p">);</span> <span class="p">}</span> <span class="k">return</span> <span class="o">@</span><span class="nf">unserialize</span><span class="p">(</span><span class="nv">$cache_data</span><span class="p">);</span> <span class="p">}</span> <span class="c">/**</span> <span class="c"> * @param string $table to get row from</span> <span class="c"> * @param string $field name to return (ex: name, abbrev, etc)</span> <span class="c"> * @param int $id primary id or item in row</span> <span class="c"> * @return string $value</span> <span class="c"> */</span> <span class="k">function</span> <span class="nf">row</span><span class="p">(</span><span class="nv">$table</span><span class="p">,</span> <span class="nv">$id</span><span class="p">,</span> <span class="nv">$field</span><span class="o">=</span><span class="s1">&#39;name&#39;</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$all</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">all</span><span class="p">(</span><span class="nv">$table</span><span class="p">,</span> <span class="nv">$field</span><span class="p">);</span> <span class="k">return</span> <span class="nf">array_key_exists</span><span class="p">(</span><span class="nv">$id</span><span class="p">,</span> <span class="nv">$all</span><span class="p">)</span> <span class="o">?</span> <span class="nv">$all</span><span class="p">[</span><span class="nv">$id</span><span class="p">]</span> <span class="o">:</span> <span class="k">null</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </pre></div> <div style='display:none'>&lt;?php <br/>/** <br/> * CakePHP Helper to handle use of data in database that never changes <br/> * <br/> * array plural($order, $field='name'); // ready for use in FormHelper::select <br/> * string singular($id, $field='name'); <br/> * <br/> * usage examples (in Views): <br/> * <br/> * $static-&gt;states('abbrev'); // all states sorted by abbrev <br/> * $static-&gt;statuses('id', 'name'); // get list of all status names, sorted by id <br/> * $static-&gt;status(40, 'desc'); // description of status with id 40 <br/> * $status-&gt;countries(); // get all countries sorted by id <br/> * <br/> * @author Rob Conner aka rtconner <br/> */ <br/>class StaticHelper extends Helper { <br/> <br/> // set this to a folder within your app/tmp folder to store the data (no trailing slash) <br/> var $tmp_folder = 'cache'; <br/> <br/> var $cache_expires = '+1 Year'; <br/> <br/> var $database = 'default'; <br/> <br/> //plural only please, will enable both plural and singular methods (status needs to be added to inflections.php) <br/> var $methods = array('states', 'countries', 'statuses'); <br/> <br/> function __call($method, $args) { <br/> $plural = Inflector::pluralize($method); <br/> if(in_array($plural, $this-&gt;methods)) { <br/> array_unshift($args, $method); <br/> if($plural==$method) // return list of all static items <br/> return call_user_func_array(array(&amp;$this, 'all'), $args); <br/> else // return single item <br/> return call_user_func_array(array(&amp;$this, 'row'), $args); <br/> } else // try to imitate php's errors <br/> trigger_error(sprintf('Call to undefined function: %s::%s().', get_class($this), $method), E_USER_ERROR); <br/> } <br/> <br/> /** <br/> * @param string $table to get list from <br/> * @param string $order sort order of list <br/> * @param string $field field data to return <br/> * @return array <br/> */ <br/> function all($table, $order='id', $field='name') { <br/> $cache_name = $this-&gt;tmp_folder.DS.$table.'_'.$order.'_'.$field; <br/> $cache_data = cache($cache_name, null, $this-&gt;cache_expires, 'tmp'); <br/> <br/> if (empty($cache_data)) { <br/> <br/> $db =&amp; ConnectionManager::getDataSource($this-&gt;database); <br/> <br/> $table = Inflector::tableize($table); <br/> $qs = 'SELECT * FROM `'.$table.'` ORDER BY `'.$order.'`;'; <br/> <br/> $results = $db-&gt;fetchAll($qs, false, 'States'); <br/> <br/> $list = array(); <br/> foreach($results as $r) <br/> $list[$r[$table]['id']] = $r[$table][$field]; <br/> <br/> $cache_data = cache($cache_name, serialize($list), $this-&gt;cache_expires, 'tmp'); <br/> } <br/> <br/> return @unserialize($cache_data); <br/> } <br/> <br/> /** <br/> * @param string $table to get row from <br/> * @param string $field name to return (ex: name, abbrev, etc) <br/> * @param int $id primary id or item in row <br/> * @return string $value <br/> */ <br/> function row($table, $id, $field='name') { <br/> $all = $this-&gt;all($table, $field); <br/> return array_key_exists($id, $all) <br/> ? $all[$id] <br/> : null; <br/> } <br/> <br/>} <br/></div></em>