01 Oct 07 _ Caching Model Data in CakePHP (Part 2)


By casey
in Awesomeness, CakePHP, Casey's Corner

In my last post on this topic, I discussed the occasional need to make large SQL calls (for example, calculating the navigation structure of your site), and the relative lack of good options for reducing the number of times Cake would need to make those large calls.

We needed to create a basic caching function that would work to reduce the load on the SQL server for common find and findAll queries. We might as well make it a general function, so any model can use it. So let’s add this function to our app_model.php file, located in the /app folder of the Cake distribution. If the file doesn’t exist (by default it does not), you can create it.

I made two functions for data-caching: cachedFind and cachedFindAll. They take the same inputs as the standard Cake functions find and findAll, to retain any and all flexibility we need. Here’s the code:


< ?php
class AppModel extends Model{

function cachedFindAll($conditions = null, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null)
{
$findID = null;
//construct the FindID
if (is_array($conditions)) {
$conditionsID = implode('.',$conditions);
} else {
$conditionsID = $conditions;
}

if (is_array($order)) {
$orderID = implode('.',$order);
} else {
$orderID = $order;
}

if (is_array($fields)) {
$fieldsID = implode('.',$fields);
} else {
$fieldsID = $fields;
}

$findID = md5($this->useTable . $conditionsID . $orderID . $fieldsID);

if (!$findID) {
return false;
}

//check to see if a cached version exists
$cachedSQLFile = CACHE . DS . 'models' . DS . $findID.'_data.php';

if (file_exists($cachedSQLFile)) {
//the file exists, so read the file and unserialize it.
if (function_exists('file_get_contents')) {
$serializedNav = file_get_contents($cachedSQLFile);
} else {
$serializedNav = implode('', file($cachedSQLFile));
}
$data = unserialize($serializedNav);
}

if (!isset($data) || !is_array($data)) {
//either we can't find the variable and it doesn't exist, or the data isn't an array, so reload it

$data = $this->findAll($conditions, $fields, $order, $limit, $page, $recursive);

//if it's there and the data was bad, delete the file
if (file_exists($cachedSQLFile)) {
@unlink(file_exists($cachedSQLFile));
}

//write the new, cached(!!) version
$cacheFile = fopen($cachedSQLFile,"w");
fwrite($cacheFile,serialize($data));
fclose($cacheFile);

}

return $data;

}

}
?>

I’ll go through the main points of this function:


$findID = null;
//construct the FindID
if (is_array($conditions)) {
$conditionsID = implode('.',$conditions);
} else {
$conditionsID = $conditions;
}

if (is_array($order)) {
$orderID = implode('.',$order);
} else {
$orderID = $order;
}

if (is_array($fields)) {
$fieldsID = implode('.',$fields);
} else {
$fieldsID = $fields;
}

$findID = md5($this->useTable . $conditionsID . $orderID . $fieldsID);

if (!$findID) {
return false;
}

Because we want flexibility here, we need to have a way to uniquely identify the cached data. The way I approach that is to create an MD5 – encoded string based on the input parameters of the findAll query, which associates a unique string with distinct queries. (A hat-tip to August Kaiser at Beehive Media for the recommendation to add the $this->useTable to ensure unique calls for each model).


$cachedSQLFile = CACHE . DS . 'models' . DS . $findID.'_data.php';

if (file_exists($cachedSQLFile)) {
//the file exists, so read the file and unserialize it.
if (function_exists('file_get_contents')) {
$serializedNav = file_get_contents($cachedSQLFile);
} else {
$serializedNav = implode('', file($cachedSQLFile));
}
$data = unserialize($serializedNav);
}

Now we create a link to where the cached file *should* be, using Cake’s defined CACHE location, the model directory, and then the fileID we created above, with the suffix of “_data.php”. If the file is found, the function attempts to read the contents of the file and unserialize it. The function check exists because file_get_contents isn’t standard until PHP 4.3 and up.


if (!isset($data) || !is_array($data)) {
//either we can't find the variable and it doesn't exist, or the data isn't an array, so reload it

$data = $this->findAll($conditions, $fields, $order, $limit, $page, $recursive);

//if it's there and the data was bad, delete the file
if (file_exists($cachedSQLFile)) {
@unlink(file_exists($cachedSQLFile));
}

//write the new, cached(!!) version
$cacheFile = fopen($cachedSQLFile,"w");
fwrite($cacheFile,serialize($data));
fclose($cacheFile);

}

Next we check if the unserialized data exists, and if does, if it’s an array. If either of those conditions are not met, we go and retrieve the data from the database, try to delete an old copy of the file if it exists, and serialize the new data, and write it to the model cache directory for next time.

There’s one more step we need to do before this function becomes completely useful. In the same app_model.php file, we need to add one more function:


function afterSave()
{
clearCache('_data', 'models', '.php');
return true;
}

AfterSave() is a Cake-specific callback function that is executed after data is inserted into the model. What this function does, then, is clear out all the cached versions of our find calls every time we update the database, in order to make sure the user sees the most recent version of the data. Since we put this code into the AppModel class, it runs everytime we save something from ANY model in our system. This may not be the best implementation for web apps that have user-updated content areas, but for a single or multiuser CMS such as our WebTree system, it works quite nicely.

Now anytime you want to have Cake cache the returned data from a SQL call, you can use the cachedFindAll function exactly like the standard findAll function:


< ?php
$data = $this->Model->cachedFindAll(array('Model.displayed'=>'1'),null,array('Model.date'=>'DESC'));
?>

In case you’re curious, the cachedFind function is modeled exactly like the find function in Cake, where it just grabs the first array of data from a findAll call and returns that:


function cachedFind($conditions = null, $fields = null, $order = null, $recursive = null)
{
$data = $this->cachedFindAll($conditions, $fields, $order, 1, null, $recursive);
if (empty($data[0])) {
return false;
}
return $data[0];

}

Now you have two functions that will drastically reduce the amount of SQL calls made by your app. Enjoy.

Spread the Word:
  • Slashdot
  • Digg
  • Facebook
  • Reddit
  • del.icio.us
  • StumbleUpon
  • Technorati
  • NewsVine


Comments are closed.

  • guitar modification parts
  • butcher paper roll dispenser
  • makers of dog kennels
  • credit card debt relief program
  • billet grills
  • studio rta the bug computer desk
  • shark cordless cyclonic hand vacuum
  • toastmaster parts toaster
  • men's suits shirts barney
  • saucony shoes outlet stores
  • 1 2 decorative square post caps
  • fruit of the loom union suits
  • maine snowmobile insurance requirements
  • boyne mountain grand lodge and spa
  • triple pet natural toothpaste
  • used kids dirt bikes for sale
  • 30 inch slide in electric range
  • create pdf word embed fonts
  • wholesale jelly jars
  • water as fuel denny klein
  • couch replacement pillows
  • coleman bayside elite replacement parts
  • sponge bob crabby patty candy
  • wholesale winter gloves hats
  • best at home treatment for blackheads
  • tub refinishing pgh pa
  • tile shower seat vintage
  • caught wearing high heels split skirt
  • kawasaki kx85 parts and exploded view
  • india and business dress
  • kid's camp crafts
  • cuff bracelets wholesale
  • golf gps systems reviews
  • wholesale patio pool furniture
  • crystal on the plaza
  • global travel shoes
  • spectrum products tanning bed
  • 84 wide wooden window blinds
  • consigning my lilly pulitzer clothes
  • no smoking rooms in boston hotels
  • aubrey organics skin cleanser
  • home decor liquidators furniture and flooring
  • hair regrowth products reviews
  • black bean corn citrus vinegar
  • jt paintball gear
  • how to create finger nail designs
  • popup tent cleaning
  • knife sound editing
  • hippie chick costumes
  • miniature surveillance cameras detection
  • hd high def video edit
  • recycle styrofoam cups
  • bamboo patio door blinds
  • money order promise rings
  • resurfacing bathtubs
  • mens star earrings
  • spray face foundation
  • remote home thermostats
  • phoenix skin rejuvenation
  • shower enclosures walls
  • kawasaki 440 aircraft engine parts
  • women's clothing sleep shirts
  • material requirements planning mrp systems
  • cryoserver back email compliance archiving message
  • home flea infestation
  • cuisinart 9-speed hand mixer brushed chrome
  • wholesale military surgical caps
  • nike drawstring backpack lavender
  • acutane colon problems
  • mercedes 320 wagon tires
  • ucla womens basketball camps
  • vida international fruta
  • kids camp crafts
  • compliance corporate email
  • good feet store onalaska wi
  • coach purse party
  • women's shoulder handbags
  • diy exterior patio blinds
  • pivot tub shower enclosures
  • dog harness and collar
  • shoi racing helmets
  • free shawl knit patterns
  • thyme essential oil price
  • tubeless bicycle wheels road
  • black edition magazine man swimsuit
  • 5 star car wax
  • italian charm bracelets wholesale
  • sleep t shirts
  • audio cards upside down
  • dell 5100cn colour network laser printer
  • fj cruiser trailer hitch receivers
  • sell structured settlement payment
  • organizational monitor stands
  • prom decorations and costumes
  • cats on behavior medication
  • golf palm computer gps system
  • budget wedding planning checklist
  • movie theater popcorn maker canada
  • water pump 3-inch suction hose
  • invest in stocks cheap
  • nike max air inline skates
  • star wars galactic heroes bedding comforter
  • airsoft outfitters gear
  • cheap new chevy trucks
  • discounted airfares cheapest airfares erfurt
  • floral furniture throws
  • schutt baseball equipment
  • acrobat distiller settings for news print
  • 2.4 to 3.8 s10 truck conversion
  • westgate villia resorts
  • fuzzy bunny slippers adult
  • shock doctor mouth pieces
  • midwest family vacations
  • red kap highland uniform shirts
  • play euchre free online
  • rockport shoes rosemont
  • elo entuitive touchscreen drivers
  • decorative garden pond fountains
  • kids online cake decorating games
  • america west airlines dividend miles program
  • in kind giving from popcorn company
  • camera ccd industrial
  • outdoor furniture pool harrows
  • raymond waites spring garland
  • lil boosie new mixtapes
  • household and cleaning forum
  • video piggy 1.3.0 crack