Introduction
QCache is primarily designed to help with caching things site-wide, that are presented the same to all users. But that doesn't mean you can't use it in more advanced ways.
Here is an example of using QCache to prevent re-calculating a menu, even though the menu changes dynamically for different clients, roles, and role permissions.
Data Model
We have the following data model:
client
id
name
User
id
role_id
client_id
name
Role
id
name
Permission
id
name
Role Permissions
role_id
permission_id
client_id
Example Setup
Our menu should show everything the user can do, based on the permissions assigned to their role for the appropriate client.
Data
eg:
- Bill is part of ACME, and has Sys Admin role
- Joe is part of ACME, and has Team Lead role
- George is part of StampsRUs, and has Sys Admin role
- Phil is part of StampsRUs, and has Team Lead role
- Shawn is part of StampsRUs, and has Team Lead role
- Sys Admin is assigned User Admin permissions for ACME
- Sys Admin is assigned Role Admin permissions for ACME
- Sys Admin is assigned User Admin permissions for StampsRUs
- Sys Admin is assigned Role Admin permissions for StampsRUs
- Team Lead is assigned User Admin permissions for ACME
- Team Lead is assigned User Admin permissions for StampsRUs
- Team Lead is assigned Role Admin permissions for StampsRUs
(So Team Lead is the same as Sys Admin, except that in ACME, Team Leads can't admin Roles)
Result
So these are the menus we expect to see:
- Bill: User Admin, Role Admin
- Joe: User Admin
- George: User Admin, Role Admin
- Phil: User Admin, Role Admin
- Shawn: User Admin, Role Admin
Code
Now, since we're smart, we realize that user 4 and 5 have the same circumstances, and re-checking permissions when they need a menu is a waste.
So here's what we do:
<?PHP
$thisUser = session['User'];
$cache = new QCache('Menu '.$thisUser->RoleId, $thisUser->ClientId);
$cachedMenu = $cache->GetData();
if(false !== $cachedMenu && '' != $cachedMenu)
{
print($cachedMenu);
return;
}
ob_start();
/**** BUILD MENU HERE ****/
$rolePermissionArray = RolePermission::QueryArray(QQ::AndCondition(
QQ::Equal(QQN::RolePermission()->RoleId, $thisUser->RoleId),
QQ::Equal(QQN::RolePermission()->ClientId, $thisUser->ClientId)
),
QQ::Clause(QQ::Expand(QQN::RolePermission()->Permission))
));
foreach($rolePermissionArray as $rolePermission)
echo $rolePermission->Permission->Name.', ';
/**** END MENU BUILDING ****/
$contents = ob_get_contents();
ob_end_clean();
//cache the menu
$cache->SaveData($contents);
print($contents);
?>
Pretty simple. We've gone and cached the menu for each role, specific to each client.
But once it's cached, what if something changes?
That's not too bad either: In RolePermission?.class.php
public function Save($blnForceInsert = false, $blnForceUpdate = false) {
//wipe the cached menus, since they may have been affected by this change
QCache::ClearNamespace('Menu '.$this->RoleId);
return parent::Save($blnForceInsert, $blnForceUpdate);
}
Ok, so you may be saying "What's ClearNamespace?? My copy of QCubed doesn't have that!", and you'd be right. I had to hack QCache.class.php to make this work nicely. Here's the 2 functions I added:
public static function ClearNamespace($strNamespace)
{
$path = sprintf('%s/%s', QCache::$CachePath, trim(strtolower($strNamespace)));
QCache::clear_directory($path);
}
protected static function clear_directory($strDir)
{
if (!is_dir($strDir))
return false;
$dir = opendir($strDir);
if (false === $dir)
return false;
while($filename = readdir($dir))
{
if ($filename != "." && $filename != "..")
{
if (!is_dir($strDir."/".$filename))
unlink($strDir."/".$filename);
else
{
if(false === clear_directory($strDir.'/'.$filename))
return false;
rmdir($strDir.'/'.$filename);
}
}
}
closedir($dir);
return true;
}
We're investigating inclusion of this QCache extensions into a future version of QCubed: ticket #143.
