Do you ever wanted to accomplish a better reusable CMS which helps you do things faster? If yes, you definitely need to know more about CakePHP Plugins. This manual about plugins is not just enough, there are many cool things you can do with them.
In this post, I will explain you, how to create a reusable Backend Plugin, which can easily fit in with any of your project. Although, I've written a tutorial before on how to create an administrator panel, but that is not so RAD approach.
Creating necessary files for plugin:
/app/plugins/backend
Ok, now comes interesting part:
Telling Plugin about our Admin Links
We will need our other controllers to tell Admin plugin about all the admin URLs/function. For this, we will create two new class variables called $admin_links and $admin_menu_order in all our controllers.
-
<?
-
Class ClientsController extends AppController
-
{
-
Var $name = ‘Clients’;
-
var $adminLinks = array('add' => 'Add New Client, 'index' => 'Show all clients); // add points to admin_add() and ‘Add New Client’ corresponds to the anchor of link.
-
var $adminOrder = 1; // Ordering inside admin panel
-
-
// functions etc goes here..
-
}
-
?>
Add these variables in all necessary controller files.
Plugin Connectivity to the rest of system
Since our plugin needs to look each and every controller present in system. I decided to create BackendController::cacheAdminLinks(),this function will get all admin links as specified in controllers, then store it to a temporary cache file. This way, all controllers need not to be loaded in every call.
-
<?php
-
-
class BackendController extends BackendAppController
-
{
-
var $name = 'Backend';
-
-
function cacheAdminLinks()
-
{
-
-
// collect all controller paths to find backend links from
-
$allPaths = paths();
-
$appControllers = $allPaths['Controllers'];
-
-
// find controllers of other plugins
-
$folder =& new Folder(APP.'plugins');
-
$plugins = $folder->ls();
-
-
$ControllersPaths = am($ControllersPaths, $appControllers); // merge app with all paths
-
-
foreach($plugins[0] as $plugin)
-
{
-
$pluginControllers = $allPaths[Inflector::camelize($plugin)]['Controllers'];
-
$ControllersPaths = am($ControllersPaths, $pluginControllers); //add plugin controllers path
-
}
-
-
foreach($ControllersPaths as $path)
-
{
-
$folder->cd($path);
-
$controllerFiles = $folder->find('.+_controller\.php$');
-
}
-
-
-
foreach($controllers as $controller)
-
{
-
App::import('Controller', $controller);
-
$className = $controller."Controller";
-
$controllerInst = &new $className();
-
-
$adminLinks[$controllerInst->adminOrder] = array('name' => $controllerInst->name, $controllerInst->adminLinks);
-
-
}
-
-
// reorder & reform admin links in better form
-
foreach($adminLinks as $adminLink)
-
{
-
$links = $adminLink[0];
-
foreach($adminLink[0] as $funcName => $link)
-
{
-
$tmp_reformedLink = array('url' => $this->_getAdminUrl($adminLink['name'], $funcName) , 'title' => $link);
-
$reformedLinks[] = $tmp_reformedLink;
-
}
-
-
$tmp_adminLinks[$adminLink['name']] = $reformedLinks;
-
-
}
-
$adminLinks = $tmp_adminLinks;
-
-
if (Configure::read('debug')==3)
-
$cacheExpires = '+5 seconds';
-
elseif (Configure::read('debug')==1 || Configure::read('debug')==2)
-
$cacheExpires = '+60 seconds';
-
else
-
$cacheExpires = '+24 hours';
-
-
exit;
-
-
}
-
-
function _getAdminUrl($singular_name, $funcName)
-
{
-
return '/'.Configure::read('Routing.admin').'/'.low(Inflector::pluralize($singular_name)).'/'.$funcName;
-
}
-
-
function __controllerize($file)
-
{
-
return Inflector::camelize(r('_controller.php', '', $file));
-
}
-
}
-
?>
Setting up $layout & $admin_links at every admin call
Now, this is a bit tricky part – for every plugin you create in your app, you wouldn't want to add custom hacks anywhere throughout your app for every plugin. This is where you can utilize plugin hooks, as introduced by Felix in this post. I wouldn't go too deep to explain how it works, but it just basically allows you to create a file hooks.php inside your plugin folder. You can attach these hooks with your app, in this case just attach it to AppController::beforeFilter()
-
function beforeFilter()
-
{
-
callHooks('beforeFilter', null, $this); // you need to have this function defined somewhere – I guess bootstrap.php in best place
-
}
In our hooks.php, we will create following function:
-
// this function will be called by AppController::beforeFilter() on every HTTP request
-
function BackendbeforeFilterHook(&$controller)
-
{
-
// if its the administrator/manager - change the layout
-
if($pos == true)
-
{
-
$controller->plugin='backend';
-
$controller->layout='admin';
-
-
$controller->set('adminLinks', $adminLinks);
-
-
}
-
}
As you can see, an instance of controller is being passed to this function, and you can modify it as per your needs. So, we just overwritten the layout to admin.ctp inside app/plugins/backend/views/layouts and as also set our admin links to view from cache (saved by BackendController::cacheAdminLinks() )
-
<div id="content">
-
-
<div id="menu">
-
<? foreach($adminLinks as $title=>$adminLink) { ?>
-
<div class="admin_box">
-
<?=$title;?>
-
<ul>
-
<? foreach($adminLink as $link) { ?>
-
<li><a href="<?=$html->url($link['url']);?>"><?=$link['title'];?></a></li>
-
<? } ?>
-
</ul>
-
</div>
-
<? } ?>
-
</div>
-
<div id="admin_content">
-
<?php
-
if ($session->check('Message.flash')):
-
$session->flash();
-
endif;
-
?>
-
</div>
-
-
</div>
This plugin gives you a nice looking basic backend panel, by just dropping a folder. [except you have to have callHooks() in your bootstrap.php, however this function is not just for this specific plugin, it will be utilized while working with other plugins as well]
I think the idea of connecting plugins with hooks is amazing, I just realized that it can increase reusability greatly. Just drop folders to your app, and add up a new functionality instantly. There was a project called SpliceIt started in Feb 2006 – which was fully based on plugins, but its not active anymore.
Hope you liked this idea and tutorial. I would like to thank my friend Jacob Mather, he originally inspired me to implement reusability this way.
- Abhimanyu Grover
Just came across a different type of API just now… It's the world's first API for semantic discovery. From long time back, I used to dream about the API or some Web service which could easily understand the context and classify information using its special intelligence, today its reality. Yes guys, check out
You can see how smartly, it has given out the categories to which this topic belongs. Even google search is not capable of doing that as of now, so I can confidently say that something like this is going to change the way of search. No more punishments of using wrong keywords, and no more would web stay as old boring thing – you would actually start interacting with it, like you do with other humans (in future..).
Time Saver: BullsCash reduces time and effort needed to regularly check websites for updates. People under finance sector in India spend hours in reading irrelevant/unimportant news. It lets you go through important news only – that too in min. time.