phpcakephpcakephp-2.2cakephp-helpercakephp-routing

CakePHP, Routing for optional controller using generic controller otherwise


I'd like to make an application in CakePHP which manages exercises and users results. Users and results are not important in this question.

I want to have a possibility to add an exercise with adding only a specific table and line to .ini config file. Application should route to a GenericExercisesController if specific one doesn't exists. Controller should load a GenericExerciseModel if specific doesn't exists. I'd managed with model loading and partially with routing with controller like this:

In route.php

foreach(Configure::read('exercisesTables') as $exerciseName){
    if( App::import('Controller', Inflector::pluralize($exerciseName))){
        Router::connect('/exercises/'.Inflector::pluralize($exerciseName).'/:action', array('controller' => Inflector::pluralize($exerciseName)));
    }else{
        Router::connect('/exercises/'.Inflector::pluralize($exerciseName).'/:action', array('controller' => 'GenericExercises', 'fakeModel' => $exerciseName));
    }
}

So if I want to load an exercise Foo I should use address:

http://example.com/exercises/Foos/view

And this works fine, doesn't matter if specific controller exists.

Problem begins when I use reverse routing to generate links in views. If exercise Foo have specific controller this works correctly:

print $this->Html->url(array('controller' => Inflector::pluralize($exerciseName), 'action' => 'view')); produces: /exercises/Foos/view

But when exercise Bar doesn't have specific controller then the same code produces: /Bars

This causes a problem, there is no Bars Controller.

Temporarily I'm generating those links manually, but I don't think that this is the best solution:

print $this->Html->url("/".Configure::read('exerciseRoutingPrefix')."/".Inflector::pluralize($exerciseName)."/view");

Maybe someone of you know a better solution. Thank you for reading my question.

UPDATE 1:

Those are routes in route.php defined before foreach in order as they're in file:

Router::connect('/', array('controller' => 'questions', 'action' => 'regulations'));
Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
Router::connect('/help', array('controller' => 'pages', 'action' => 'faq'));

Solution

  • If I understood your question correctly, you could try making a "MyHtmlHelper" which does some magic before parsing your options to the HtmlHelper::link();

    You can do it like so:

    /app/View/Helpes/MyHtmlHelper.php

    Create a helper which extends the "HtmlHelper" like so:

    <?php
    App::uses('HtmlHelper', 'View/Helper');
    class MyHtmlHelper extends HtmlHelper {
        public function link($title, $url = null, $options = array(), $confirmMessage = false) {
            //do your magic here and change the $title, $url or $options accordingly.
            return parent::link($title, $url, $options, $confirmMessage);
        }
    }
    

    Now in your controller, you should include the Helper like so (aliasing) $helpers = array('Html' => array('className' => 'MyHtml')); instead of $helpers = array('Html');.

    I guess you can fill in the blanks on the public function link yourself. If not, feel free to ask more help :)

    Off course it is possible to do this with every other Helper or method you can think off. HtmlHelper::link() is just used as example.