Posts Tagged ‘codeigniter’

Simple general data cache for Codeigniter

Thursday, September 24th, 2009

Here’s a very simple and generic data cache class for Codeigniter:

  • since the end of 2008 renames are atomic on both Windows and Linux
  • I use the filemtime with the ttl, this means no extra expiry file
  • the getter doesn’t return the data, but stores it in a buffer
  • values are stored as serialized php files in the CI cache directory
  • use it as a plugin: $CI-<load-<plugin(‘SerCache’); //I didn’t want it to behave like a library

Hope someone finds it useful. A more advanced cache for CI may be found here and another one here.

class SerCache {
public $filename;
private $tempfile;
private $ttl;
private $default_ext = '.ser';
public $buffer;

/**
* Simple data cache
* @param string name filename of the cache file (probably an md5 hash)
* @param mixed ttl time to live in seconds; -1 circumvents caching, 'forever' is infinite
*/
public function __construct($name, $ttl = 'forever')
{
$CI =& get_instance();
$path = $CI->config->item('cache_path');
$this->cache_dir = ($path == '') ? BASEPATH.'cache/' : $path;

$this->ttl = $ttl;
if ($ttl == -1)
return FALSE;
$this->filename = $this->cache_dir.$name.$this->default_ext;
$this->tempfile = $name.getmypid();
}

/**
* Fetch data into $this->buffer
* @return bool FALSE if expired or not set, TRUE otherwise
*/
public function get()
{
if ($this->ttl == -1)
return FALSE;
if ($this->ttl == 'forever')
{
if (!file_exists($this->filename))
return FALSE;
}
else
{
if ((!file_exists($this->filename)) OR (filemtime($this->filename) + $this->ttl < time()))
return FALSE;
}
$this->buffer = unserialize(file_get_contents($this->filename));
return TRUE;
}

/**
* Save serialized value to file
* @param mixed value value to be serialized
*/
public function put($value) {
if ($this->ttl == -1)
return FALSE;
file_put_contents($this->tempfile, serialize($value));
rename($this->tempfile, $this->filename);
}

/**
* Deletes the cachefile
*/
public function destroy() {
if (file_exists($this->filename))
{
unlink($this->filename);
}
}

/**
* Returns the filemtime of the cache file
*/
public function filemtime() {
if (file_exists($this->filename))
return filemtime($this->filename);
return 0;
}
}

Example:

$cache = new SerCache('something', $cacheTTL);
if ($cache->get()) {
$var = $cache->buffer;
} else {
//create var here...
$cache->put($var);
}

Doctrine vs Codeigniter (classname collitions)

Sunday, October 5th, 2008

Doctrine is really nice, but upon generating schemas the generated classes are a bit “general”. For example I have a User, a Post and a PostTag table, after building the schema I will end up with similar classnames (no, renaming does not work). Yes, this is cute on one hand (new User), but on the other if I happen to have a User class in my controllers (not terribly hard with Codeigniter) then whoops, I have a classname collition where finetuning the autoloader will not help. Possible solutions:

  • rename the generated models: not a good idea, will throw nice fat errors (‘Couldn’t find class User’), there maybe a solution for this, but I really doubt it
  • rename the controller: pain in the back with Codeigniter (and routing), no thanks
  • finetune CI so that Controllers have a distinct name (UserController), with routing made aware of this fact (/user/ routes to UserController); this is buried in the CI core code, no thanks again. Also User_Controller would be nice, Kohana seemed to understand this.
  • set up custom routes for conflicting classes: CI route-manager is a bit dumb, finetuning routing for all cases does hurt. One can set up url rewriting too, but that’s even more pain in the back, because of the internal routing and redirection schemes.
  • use namespaces, which is possible with PHP5.3 (currently alpha 2), but namespacing the whole Doctrine system and ironing out class declaration problems is beyond my knowledge (though I must admit it, I tried).

Codeigniter Pagination vs. url parameters

Wednesday, September 10th, 2008

Just like others, I had to realize that the CI paginator is pretty dumb in itself. By default it uses the $base_url parameter attached with the “page number” and nothing else – but first things first: jfyi, the page number is not the page number (like the documentation suggests) but the offset, this gave me a bit of a headache, but now that I know that this is intentional, I’m relieved. Well, I’m not, and besides this is confusing it just makes me shorter with one line and some elementary school math, but have it your way…

Back to the original problem: CI paginated urls look something like this (with an “/items/from/” baseurl):

  • /items/from
  • /items/from/10
  • /items/from/20
  • etc.

Now, what happens if I add more “get-like” parameters, like
“/items/sort/name”? For example because I want to do sorting or filtering?

Instead of having “/items/sort/name/from/10″ it is very easy to end up with “/items/from/10″, which means the url is being overwritten by the paginator. Why? Because the paginator, as I mentioned above, doesn’t care for the “get-like” (post, to be exact) parameter list in the url field.

The simplest solution is to reuse the current URI string, but with removing the paginator “duplicate” (if it exists):

//$this->base_url = rtrim($this->base_url, '/') .'/';
$trimmed = rtrim($this->base_url, '/');
$big_url = preg_replace('@'.$trimmed.'/?-?\d+@', '', $CI->uri->uri_string());
$this->base_url = $big_url . $trimmed . '/';

Just like the original poster, I ended up modifying the paginator, though this solution would require some further tweaking with url suffixes (not to mention the instability of the urls concerning bookmarking) but as a quick fix it is okay – the other solution is using sessions, but either sessions or the patch above, it’s not the solution that’s important, but the problem itself.

Codeigniter ActiveRecord stupidity

Monday, July 14th, 2008

While working with CI is moderately fun, sometimes this framework is getting on my nerves. The ActiveRecord, apart from the fact that it has nothing to do with ROR ActiveRecord philosophy is pretty annoying when it comes to “more complex” (not awfully dumb) queries. Let’s assume the following query (plain sql)

SELECT product.id AS id, name, shortdesc, price, hidden, issmall,
uniqueness, images, COUNT(product_id) AS categorynum
FROM product
LEFT JOIN productcategory AS pc
ON product.id = pc.product_id
GROUP BY product_id
ORDER BY name ASC
LIMIT 15
OFFSET 2

This is not that complicated, is it? Product and category is a many to many relationship via productcategory, everything else should be pretty straightforward, including the pagination. Btw offset is a valid keyword, the Jedit hiliter is a bit outdated.

Since I don’t want to spend my time rewriting this query to something activerecordish, but while I still am in need of escaping/filtering, I would do something like this (which would not work):

$sql = '
SELECT product.id AS id, name, shortdesc, price, hidden, issmall,
uniqueness, images, COUNT(product_id) AS categorynum
FROM product
LEFT JOIN productcategory AS pc
ON product.id = pc.product_id
GROUP BY product_id
ORDER BY ? ?
LIMIT ?
OFFSET ?
';
$query = $this->db->query($sql, array($sort, $dir, $perpage, $offset));

The problem is the order by clause, which does have parameters from outside php, so should be filtered, but is enclosed with backticks (yes, I can turn off escaping, but that’s not what I want to, especially not for all the fields). By the way, the binding is done with question marks, which is a common practice, but which is in my opinion can be very annoying. As a sidenote, I think the “%VAR% -> foreach replace with array[VAR]” template-like syntax is much better (for quick and simple solutions), but back to the original topic, moving onto the final solution:

$this->db->select("product.id AS id, name, shortdesc, price, hidden, 
issmall, uniqueness, images, COUNT(product_id) AS categorynum");
$this->db->join('productcategory AS pc', 'product.id = pc.product_id');
$this->db->group_by('product_id');
$this->db->order_by($sort, $dir);
$this->db->limit($perpage, $offset);
$query = $this->db->get('product');

Is this nicer than the original? For hardcore php fans it may be, but to me it is pretty much the same as the original sql code; add the fact that I’m stuck with this solution, I tend to dislike it even more than hybrid sql queries (from different orm layers).