How to list all controllers

Published on July 21, 2006 and tagged with cakephp  component

From time to time the question “how can I list all controllers?” pops up. I provided a (untested) solution in the german google group, but yesterday I had to see that it didn’t work (thanks to Kinesis77). So here is the improved code as a component:

// app/controllers/components/controller_list.php
class ControllerListComponent extends Object {
    public function get() {
        $controllerClasses = App::objects('controller');

        foreach($controllerClasses as $controller) { 
            if ($controller != 'App') { 
                App::import('Controller', $controller);
                $className = $controller . 'Controller';
                $actions = get_class_methods($className);
                foreach($actions as $k => $v) {
                    if ($v{0} == '_') {
                        unset($actions[$k]);
                    }
                }
                $parentActions = get_class_methods('AppController');
                $controllers[$controller] = array_diff($actions, $parentActions);
            }
        }
		     
        return $controllers;  
    }
}

The usage is simple. Just include the component in the $components array of your controller:

public $components = array('ControllerList');

and call it with:

$this->ControllerList->get()

It returns an array with the following format:

Array
(
    [Pages] => Array
        (
            [0] => display
        )
    [Tests] => Array
        (
            [0] => index
            [1] => groups
            [2] => cases
        )
)

You can find the component on Github: http://github.com/cakebaker/controller-list-component.

Update 2008-02-04: Applied Jej’s patch and adapted the component to work with Cake 1.2.
Update 2008-07-17: Applied Raymond’s fix.
Update 2008-11-21: Applied Maxim Tsyngaev’s patch.
Update 2010-05-19: Adapting the component to work with Cake 1.3
Update 2010-06-29: Applying Andrew’s fix plus adding the code to Github.

30 comments baked

  • AD7six July 21, 2006 at 19:26

    Like it.

    I have a sneaky suspicion that many users want to use this to generate a navigation menu whilst in development.

    You seem to be one blog ahead of me ;), here’s a snippet of code taken from something I was going to post in the coming days:

    $class_methods = get_class_methods($this->controller);
    $controller_methods = get_class_methods(“AppController”);
    $class_methods = array_diff($class_methods,$controller_methods);
    foreach ($class_methods as $method)
    {
    if ($method{0}”_”)
    {
    $admin = strpos($method, CAKE_ADMIN);
    if ($admin === false)
    {
    if (!stristr($method,”controller”)) // Without this, class names are appearing in the results (?) with PHP4.
    {
    $this->PublicUrls[] = Array(ucfirst($method),”/”.$this->controller->name.”/”.$method);
    }
    }
    }
    }

    That way, methods defined in app_controller or the plugin app controller are captured, and all the private methods are ignored (as well as the default methods, as is the case for your example.)

  • AD7six July 21, 2006 at 19:28

    Ooops made a copy paste error… that should be:

    $controller_methods = get_class_methods(”Controller”);

  • Felix Geisendörfer July 21, 2006 at 21:03

    Hi, one more thing to consider for any solution that is supposed to list controllers/models/etc., is that they can also be placed outside of app/controllers or app/models, and that those places can be defined in the bootstrap.php. For that reason, it is neccessary to loop through ldall those possible locations which you can get from the Configure singleton. Bear in mind that also plugins can have controllers in them.

    Just do something like this:
    uses(‘Folder’);
    $paths = Configure::getInstance();
    $folder =& new Folder();

    $controllers = array();

    foreach ($paths->controllerPaths as $path)
    {
    $folder->cd($path);
    $files = $folder->findRecursive(‘.*_controller\.php’);
    $controllers = array_merge($controllers, $files);
    }

    $folder->cd(APP.’plugins’);
    $files = $folder->findRecursive(‘.*_controller\.php’);
    $files = $folder->findRecursive(‘.*[^api]_controller\.php’);

    $controllers = array_merge($controllers, $files);

    Ok, this is not 100% perfect either, since it doesn’t go through each plugin folder and only checks the /controllers subdir in it, but you can add that.

    In SpliceIt! I sucessfully am using an automatically generated menu based on all _admin functions, the code can be found here:
    https://cakeforge.org/plugins/scmsvn/viewcvs.php/trunk/spliceit/app/plugins/admin/controllers/admin_api.php?rev=78&root=spliceit&view=markup

    Anyway, other then that thanks for publishing it, I think that’s really useful for a lot of people out there ; ).

  • cakebaker July 22, 2006 at 09:54

    @AD7six, Felix: Thanks for your additions. I have completely missed plug-ins ;)

  • How to list all controllers July 22, 2006 at 23:15

    [...] How to list all controllers: ”From time to time the question ‘how can I list all controllers?’ pops up. I provided a (untested) solution in the german google group, but yesterday I had to see that it didn’t work (thanks to Kinesis77). So here is the improved code as a component: [...]

  • My CakePHP User/Permission Management System part 1 « job’s cry July 11, 2007 at 04:29

    [...] The Permission Model is simplified by the fact that permission are either 1 or 0. There are no levels, it’s either can or can’t. Development was aided by a year old post from Daniel Hofstetter, the cakeBaker himself: How to list all controllers. [...]

  • job’s cry » My CakePHP User/Permission Management System part 1 August 06, 2007 at 20:23

    [...] The Permission Model is simplified by the fact that permission are either 1 or 0. There are no levels, it’s either can or can’t. Development was aided by a year old post from Daniel Hofstetter, the cakeBaker himself: How to list all controllers. [...]

  • Jej February 02, 2008 at 16:54

    Hi,

    Thanks for this hack, very useful for me.

    I noticed that the _private _functions() are returned as controller functions. That’s not correct I think. Here is a small patch for this :

    $actions = get_class_methods($className);
    + foreach($actions as $k => $v)
    + if ($v{0} == ‘_’) unset($actions[$k]);
    $parentActions = get_class_methods(‘AppController’);

    Bye,
    J.

  • Jej February 02, 2008 at 17:14

    Hi again,

    Here is a modified version compatible with cake 1.2 :

    $v)
    if ($v{0} == ‘_’) unset($actions[$k]);
    $parentActions = get_class_methods(‘AppController’);
    $controllersList[$controller] = array_diff($actions, $parentActions);
    }

    return $controllersList;
    }
    }

    ?>

    Bye,
    J.

  • cakebaker February 04, 2008 at 17:30

    @Jej: Thanks for your patch, I modified the article accordingly.

  • Jej February 04, 2008 at 18:27

    Hi cakebaker,

    Thanks for the update. You can delete comment February 02, 2008 at 20:06, it was a mistake (the first submit didn’t appear)

    Cheers,
    Jej

  • cakebaker February 05, 2008 at 08:51

    @Jej: Ok, comment is now deleted.

  • Chris Brinker March 16, 2008 at 06:01

    After the $file = CONTROLLERS.$fileName; You may want to put a check for file existence, just in case a controller file is not there.

    $file = CONTROLLERS.$fileName;
    if(!file_exists($file)) continue; //Just in case
    require_once($file);

  • cakebaker March 16, 2008 at 11:26

    @Chris: Thanks for your comment!

    I’m not sure, if that’s really necessary, as Configure::listObjects(‘controller’); gets the names of the controllers from the file system (at least if there is no caching).

  • Raymond July 17, 2008 at 15:06

    I think you need to use
    $fileName = Inflector::underscore($controller).’_controller.php’;
    instead of
    $fileName = strtolower($controller).’_controller.php’;

    because controllers like WorkingHours have the filename working_hours_controller.php instead of workinghours_controller.php

  • cakebaker July 17, 2008 at 17:48

    @Raymond: Yes, you are right. Thanks for the hint, it is now fixed in the article.

  • Recent URLs tagged Controllers - Urlrecorder October 14, 2008 at 19:31

    [...] Recent public urls tagged “controllers” → Comment on How to list all controllers by Raymond [...]

  • Maxim Tsyngaev November 19, 2008 at 21:49

    Hi cakebaker,

    If Pages controller not exist in APP/controllers path then i have a problem (error message “No such file or directory: cake/app/contollers/pages_controller.php”)

    Here is a small patch for this:

    ==    function get() {
    ++        $paths=array();
    ==        $controllerClasses = Configure::listObjects('controller', &$paths);
    
    ++        $inc_paths = explode(":",ini_get('include_path'));
    ++        ini_set('include_path', implode(":",array_merge(array_diff($paths, $inc_paths), $inc_paths)));
    ...
    ==             $fileName = Inflector::underscore($controller) .'_controller.php';
    --              $file = CONTROLLERS.$fileName;
    --              require_once($file);
    ++            require_once($fileName);
    ==            $className = $controller . 'Controller';

    P.S. Sorry, I speak English but very badly.

    Best regards,
    Maxim

  • Maxim Tsyngaev November 19, 2008 at 21:54

    Oops! What is this? I don’t known.

    == function get() {
    ++ $paths=array();
    == $controllerClasses = Configure::listObjects(‘controller’, &$paths);

    ++ $inc_paths = explode(“:”,ini_get(‘include_path’));
    ++ ini_set(‘include_path’, implode(“:”,array_merge(array_diff($paths, $inc_paths), $inc_paths)));

    == $fileName = Inflector::underscore($controller) .’_controller.php’;
    – $file = CONTROLLERS.$fileName;
    – require_once($file);
    ++ require_once($fileName);
    == $className = $controller . ‘Controller’;

  • cakebaker November 21, 2008 at 12:10

    @Maxim: Thanks for your comment and your patch, I applied it in the article.

    I’m sorry that your comment wasn’t shown correctly, somehow the plugin I use to format source code ran amok… Anyway, your comment is now fixed.

    PS: You do not have to apologize for your English skills, there is nothing wrong with being a learner ;-)

  • chrispie January 04, 2010 at 01:22

    its better to use

    $Configure = &Configure::getInstance();
    	foreach($Configure->listObjects('controller') as $controller){ }

    for getting a list of all controller

  • cakebaker January 04, 2010 at 17:43

    @chrispie: Hm, and what is the advantage of this approach?

  • Nath March 01, 2010 at 12:52

    Thanks – perfect for what I need! However:

    $includePaths = explode(':', ini_get('include_path');
    

    is missing a parentheses:

    $includePaths = explode(':', ini_get('include_path'));
    

    Thanks

  • cakebaker March 01, 2010 at 17:32

    @Nath: Thanks, it is fixed now.

  • cetver May 17, 2010 at 11:06

    alternative:

    function get_cv() { //get_controllers_actions
    		$result = array();
    		$views  = array();
    		
    		$controllers_folder = new Folder( APP . 'controllers' );
    		$controllers = $controllers_folder->find('.*_controller\.php');
    		foreach( $controllers as $controller ) {
    			$controller_name = substr($controller, 0, strpos($controller, '_'));
    			if ( $controller_name !== 'app' ) {
    				$actions_folder = new Folder( APP . 'views' . DIRECTORY_SEPARATOR . $controller_name );
    				$actions = $actions_folder->find('.*\.ctp');
    				foreach( $actions as $action ) {
    					$action_name = substr($action, 0, strpos($action, '.'));					
    					array_push($views, $action_name);
    				}
    				$result[$controller_name] = $views;
    			}
    		}
    		return $result;
    	}
  • cetver May 17, 2010 at 14:18

    small fix:
    $controller_name = substr($controller, 0, strpos($controller, '_controller'));

  • cetver May 17, 2010 at 14:22
    //and final component :)
    <?php
    
    class ControllerListComponent extends Object {
    	function get() {
    		$result = array();
    		$controllers_folder = new Folder( APP . 'controllers' );
    		$controllers = $controllers_folder->find('.*_controller\.php');
    		foreach( $controllers as $controller ) {
    			$views  = array();
    			$controller_name = substr($controller, 0, strpos($controller, '_controller'));
    			if ( $controller_name !== 'app' ) {
    				$actions_folder = new Folder( APP . 'views' . DIRECTORY_SEPARATOR . $controller_name );
    				$actions = $actions_folder->find('.*\.ctp');
    				foreach( $actions as $action ) {
    					$action_name = substr($action, 0, strpos($action, '.'));					
    					array_push($views, $action_name);
    				}
    				$result[$controller_name] = $views;
    			}
    		}
    		return $result;
    	}
    }
    
    ?>
  • cakebaker May 19, 2010 at 10:58

    @cetver: Thanks for your component!

    However, there is a problem with your approach to detect the action names. It will only find actions with views…

  • Andrew K June 27, 2010 at 23:24

    Just wanted to point out that this code will not work for controllers that are not in the main controller directory (e.g. app/controllers/). I have some controllers in a sub-directory that are not getting picked up. I added that extra folder to my bootstrap config and this code errored out saying that the file could not be found, because it was looking only in the main controllers directory.

    I’d recommend changing:

    $fileName = Inflector::underscore($controller).'_controller.php';
                    require_once(CONTROLLERS.$fileName);

    to
    App::import('Controller', $controller);

    That way Cake will work it’s magic to find the file even if it’s in an secondary directory.

    P.S. I’ve only done this in version 1.2, but I have a feeling it should work in later versions.

  • cakebaker June 29, 2010 at 18:39

    @Andrew: Thanks for your fix, I modified the code accordingly.

Bake a comment




(for code please use <code>...</code> [no escaping necessary])

© daniel hofstetter. Licensed under a Creative Commons License