Home arrow PHP arrow Page 3 - PHP MVC Framework: the Cache Class

Caching database result sets - PHP

Welcome to the final installment of a series that shows you how to build a simple MVC-based framework in PHP 5. This series walks you in a step-by-step fashion through the development of a stack of reusable components, which can be easily put to work together under a strict MVC-based layer.

TABLE OF CONTENTS:
  1. PHP MVC Framework: the Cache Class
  2. Review: the sample application
  3. Caching database result sets
  4. The user controller class
By: Alejandro Gervasio
Rating: starstarstarstarstar / 2
July 12, 2010

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

As you may know, one of the most common approaches used for enhancing the performance of applications, including web- and desktop-based apps, is the implementation of a cache mechanism. This mechanism may use the file system, shared memory, etc. to reduce the number of accesses to a specified resource, such as a relational database.

In the case of the web application being developed here, the simplest way to decrease the number of accesses to its associated MySQL database is by using the framework’s cache class. If you don’t recall how this class was originally defined, below I listed its definition for you:

(Cache.php)

<?php

class Cache

{

private $cachedir = 'cache/';

private $expire = 60;

private static $instance = NULL;

 

// get Singleton instance of Cache class

public static function getInstance($cachedir = '')

{

if (self::$instance === NULL)

{

self::$instance = new self($cachedir);

}

return self::$instance;

}

 

// constructor

public function __construct($cachedir = '')

{

if ($cachedir !== '')

{

if (!is_dir($cachedir) or !is_writable($cachedir))

{

throw new Exception('Cache directory must be a valid writeable directory.');

}

$this->cachedir = $cachedir;

}

}

 

// write data to cache file given an ID

public function set($id, $data)

{

$file = $this->cachedir . $id;

if (file_exists($file))

{

unlink($file);

}

// write data to cache

if (!file_put_contents($file, serialize($data)))

{

throw new Exception('Error writing data to cache file.');

}

}

 

// read data from cache file given an ID

public function get($id)

{

$file = glob($this->cachedir . $id);

$file = array_shift($file);

if (!$data = file_get_contents($file))

{

throw new Exception('Error reading data from cache file.');

}

return unserialize($data);

}

 

// check if the cache file is valid or not

public function valid($id)

{

$file = glob($this->cachedir . $id);

$file = array_shift($file);

return (bool)(time() - filemtime($file) <= $this->expire);

}

}// End Cache class

Even though its driving logic is very simple, the cache class permits you to store serialized data in a specified directory during a predefined time period, which can be modified at will. With this caching mechanism available for use, I’m now going to modify the signature of the model class so it can use an instance of the cache class to store in the cache, for at least 60 seconds, the row set returned by its “fetchAll()” method.

The modified version of the model class will look like this:

(Model.php)

<?php

class Model

{

private $db = NULL;

private $cache = NULL;

private $table = 'users';

 

// constructor

public function __construct(MySQL $db, $table = '')

{

// store database class instance

$this->db = $db;

// store Cache class instance

$this->cache = Cache::getInstance();

if ($table !== '')

{

$this->table = $table;

}

}

// get all rows from specified table

public function fetchAll()

{

// if the cache file is valid fetch records from cache file

if ($this->cache->valid($this->table))

{

return $this->cache->get($this->table);

}

else

{

// otherwise fetch records from database table

$rows = array();

$this->db->query('SELECT * FROM ' . $this->table);

while ($row = $this->db->fetch())

{

$rows[] = $row;

}

// save data set to cache file

$this->cache->set($this->table, $rows);

return $rows;

}

}

// update/insert row

public function save(array $data, $id = NULL)

{

if (!empty($data))

{

// quote strings

foreach ($data as $field => $value)

{

$value = mysql_escape_string($value);

if (!is_numeric($value))

{

$data[$field] = '\'' . $value . '\'';

}

}

// update row

if ($id !== NULL)

{

$set = '';

foreach($data as $field => $value)

{

$set .= $field .'=' . $value . ',';

}

$set = substr($set, 0, -1);

$this->db->query('UPDATE ' . $this->table . ' SET ' . $set . ' WHERE id=' . (int)$id);

}

// insert new row

else

{

$fields = implode(',', array_keys($data));

$values = implode(',', array_values($data));

$this->db->query('INSERT INTO ' . $this->table . ' (' . $fields . ')' . ' VALUES (' . $values . ')');

}

}

}

 

// delete row

public function delete($id = NULL)

{

if ($id !== NULL)

{

$this->db->query('DELETE FROM ' . $this->table . ' WHERE id=' . (int)$id);

}

}

}// End Model class

If you take a close look at the “fetchAll()” method in the model, you’ll quickly grasp how it works. Essentially, this method will first check to see if there’s some data in the specified cache, and if it’s valid. If this happens to be true, then it’ll retrieve this data directly from the cache; otherwise, the data will be fetched from the database and cached for 60 seconds. This simple use of the cache class will notably reduce the overhead for accessing the MySQL table, and the cache expiration is entirely customizable.

You may be wondering why I decided to cache result sets in the model and not in the controller instead. Well, I did it that way because I’m a strong advocate of delegating to the model all the tasks that handle the data layer. In addition, if you’re using a framework that works with a generic model like this one does, then centralizing the caching of data can make your application easier to maintain and scale.

If you find it's more flexible to implement the cache system within the controller, though, go ahead and do it. That will be fine too.

So far, so good. Now that the model class has been modified to cache data within its “fetchAll()” method, it’s necessary to evaluate the impact that this change has introduced to the controller. Will its definition remain exactly the same,  or will it need to be modified too?

The answer to these question will be revealed in the final section. So go ahead and read the following segment.



 
 
>>> More PHP Articles          >>> More By Alejandro Gervasio
 

blog comments powered by Disqus
escort Bursa Bursa escort Antalya eskort
   

PHP ARTICLES

- Hackers Compromise PHP Sites to Launch Attac...
- Red Hat, Zend Form OpenShift PaaS Alliance
- PHP IDE News
- BCD, Zend Extend PHP Partnership
- PHP FAQ Highlight
- PHP Creator Didn't Set Out to Create a Langu...
- PHP Trends Revealed in Zend Study
- PHP: Best Methods for Running Scheduled Jobs
- PHP Array Functions: array_change_key_case
- PHP array_combine Function
- PHP array_chunk Function
- PHP Closures as View Helpers: Lazy-Loading F...
- Using PHP Closures as View Helpers
- PHP File and Operating System Program Execut...
- PHP: Effects of Wrapping Code in Class Const...

Developer Shed Affiliates

 


Dev Shed Tutorial Topics: