Welcome Guest, Not a member yet? Register   Sign In
Caching in Models
#1

[eluser]meigwilym[/eluser]
Hi,

My site uses a model to generate the menus. It works great in creating menus from the database.

However, I'm presently caching entire pages, and I'd like to start caching these menus so to leave the pages to update more often.

Has anyone ideas on this? I've searched the forums and looked on the wiki for other partial caching solutions, but they're not what I'm looking for.

Something like this:
Code:
# file: menu_mod.php

if(menu_cache_is_current){
  # serve menu cache file
}else{
  # write cache file
  # serve cache file
}

Thanks,

Mei
#2

[eluser]TheFuzzy0ne[/eluser]
I'm not sure if it's possible to cache pieces of a page with CodeIgniter.

Just an observation though:

Code:
# file: menu_mod.php

if( ! menu_cache_is_current){
  # write cache file
}
# serve cache file

I think that would work more in your favour. I'm sure you would have noticed this, but just in case.
#3

[eluser]Jelmer[/eluser]
It'd be pretty easy to change my MP_Cache library for this behavior, it's in the Wiki.

It works like this:
Code:
$example = $this->mp_cache->get('example');
if ($example === false)
{
    // parts of code that generate your $example array, like for example below
    $example = $this->my_model->get_pages_from_db();
    
    $this->mp_cache->write($example, 'example');
}

All you'd need to do is add a function to test wheter it is current or not. I don't test it like that, I just delete the cache every time its data is changed:
Code:
$this->mp_cache->delete('example');

Another way would be to use expiration (optional third parameter in the write() function):
Code:
$example = $this->mp_cache->get('example');
if ($example === false)
{
    // parts of code that generate your $example array, like for example below
    $example = $this->my_model->get_pages_from_db();
    $this->mp_cache->write($example, 'example', (60*60*24));
}

EDIT: Just added expiration functionality to the Cache library and edited the last code example to reflect this.
#4

[eluser]bretticus[/eluser]
I have used APC to cache database results that are fairly static (such as a data-driven menu.) At the time I didn't try extending the model class, I just wrote the code directly into my model. Basically, I store a hash of the sql query as my cache key and I store the results as an array. APC automatically handles content expiration. Here is a sample:

Code:
<?php
$this->db->from('Products');
$compiled_query = $this->db->_compile_select();
if ( apc_fetch(md5($compiled_query)) ) {
    // clear select
    $this->db->_reset_select();
    $results = apc_fetch(md5($compiled_query));
} else {            
    $query = $this->db->get();
    foreach ($query->result_array() as $row)
        $results[] = $row;
    apc_store(md5($compiled_query), $returned, APC_CACHE_TTL);
    $query->free_result();
}
?>

It's as simple as that. Of course, in the code that adds a product (in my case) etc. I simply clear the cache. Of course I could have made this more elegant by extending the Model class, but it's such a simple hack, that it works great.
#5

[eluser]meigwilym[/eluser]
Thanks for your help guys.

After some googling, I decided to roll my own.

I included these two protected methods in the model class
Code:
/*
* get cache contents
*/
function _getMenuCache($name){
        
        
        $cachefile = BASEPATH.'cache/menu/'.$name;
        $cachetime = 3600;
        
        if (file_exists($cachefile) && (time() - $cachetime < filemtime($cachefile))){
            # if cache file exists, get contents
            $fp = fopen($cachefile, 'r');
            if($menucache = fread($fp, filesize($cachefile))){
                log_message('debug', 'Menu Cache file fetched: '.$name);
            }
            fclose($fp);
            
            return $menucache;
        }else{
            return FALSE;
        }
            
    }
    
    /*
     * write the contents of the cache
     */
    function _writeMenuCache($name, $contents){
        
        $cachefile = BASEPATH.'cache/menu/'.$name;
        
        $fp = @fopen($cachefile, 'w');
        @fwrite($fp, $contents."\n&lt;!--".time().'--&gt;');
        fclose($fp);
        
        log_message('debug', 'Menu Cache file written: '.$name);
    }
I then used them in each method like this:
Code:
function getTopMenu(){

        # adraniau = 0
    
        $topmenu = $this->_getMenuCache('topmenu');
        
        if($topmenu === FALSE){
                    # database queries
                    # and html formatting here

                    $this->_writeMenuCache('topmenu', $topmenu);
        }
        
        return $topmenu;
}

I'd love to see any improvements you may have for the above code.

Best,

Mei
#6

[eluser]Jelmer[/eluser]
Your solution does pretty much exactly what MP_Cache does with a little less checking. It does have the upside that checking for the expiration doesn't need to load the cached file into memory, but that downside isn't a very big one since it should happen so little it won't have any real impact on performance. And this implementation allows for more flexibility in the when & how of the expiration checking.

With MP_Cache you wouldn't have to write cache functions into every model you'd want it in and your getTopMenu() function would look like this (pretty much the same):
Code:
function getTopMenu(){
        $this->load->library('MP_Cache');

        $topmenu = $this->mp_cache->get('topmenu');
        
        if($topmenu === FALSE){
                    // database queries
                    // and html formatting here

                    $this->mp_cache->write($topmenu, 'topmenu', 3600);
        }
        
        return $topmenu;
}




Theme © iAndrew 2016 - Forum software by © MyBB