-1

A bit of an obscure question, I think. I've successfully set up the language prefix for all of my routing rules to handle localization. Everything works great when a user requests controllers which exist in the application (e.g., /users/view/4). In my AppController I have the application successfully redirect any requests which are missing a language prefix to a URL that has one (e.g., /eng/users/view/4).

The problem arises when a user requests an invalid controller. In this case, after redirecting the user, the application throws a MissingControllerException (which it should) but the controller it reports as invalid is the language prefix itself (e.g., "EngController"), not the controller that comes after this in the request URI.

This indicates that in an error state, the Cake application is no longer aware of any language prefix routing. This suggests I need to add another routing rule to routes.php which will target only invalid requests and ensure that a language prefix is expected and accounted for in any requests which are handled by CakeErrorController (which is the core controller that takes over for these types of exceptions).

But I've scoured the docs and I have no idea how to craft such a route. Thanks for your help!

Here is my routes.php file:

<?php


$lang_settings = ['language' => '[a-z]{3}'];

// ElStats homepage vs tenant homepage
if(!TENANT) {
    Router::connect('/', ['controller' => 'pages', 'action' => 'index'], $lang_settings);
    Router::connect('/:language', ['controller' => 'pages', 'action' => 'index'], $lang_settings);
} else {
    Router::connect('/', ['controller' => 'pages', 'action' => 'index'], $lang_settings);
    Router::connect('/:language', ['controller' => 'pages', 'action' => 'index'], $lang_settings);
}


/**
 * ...and connect the rest of 'Pages' controller's urls.
 */

Router::connect('/:language/pages/*', ['controller' => 'pages', 'action' => 'display'], $lang_settings);
Router::connect('/:language/contests/search/*', ['controller' => 'contests', 'action' => 'index'], $lang_settings);
Router::connect('/:language/contests/:action/*', ['controller'=>'contests'], $lang_settings);
Router::connect('/:language/contests/*', ['controller' => 'contests', 'action' => 'index'], $lang_settings);
Router::connect('/:language/candidates/search/*', ['controller' => 'candidates', 'action' => 'index'], $lang_settings);
Router::connect('/:language/candidates/:action/*', ['controller'=>'candidates'], $lang_settings);
Router::connect('/:language/candidates/*', ['controller' => 'candidates', 'action' => 'index'], $lang_settings);

// If just /search, then default to /contests/search
Router::connect('/:language/search/*', ['controller' => 'contests', 'action' => 'index'], $lang_settings);

/**
 * Full-controller alias routing
 */

// ballot_questions => contests
Router::connect('/:language/ballot_questions/search/*', ['controller' => 'contests', 'action' => 'index'], $lang_settings);
Router::connect('/:language/ballot_questions/:action/*', ['controller'=>'contests'], $lang_settings);
Router::connect('/:language/ballot_questions/*', ['controller'=>'contests', 'action' => 'index'], $lang_settings);
Router::connect('/:language/admin/ballot_questions/:action/*', ['controller'=>'contests','admin' => true], $lang_settings);
Router::connect('/:language/admin/ballot_questions/*', ['controller'=>'contests', 'action' => 'index', 'admin' => true], $lang_settings);

// Admin Routing
Router::connect('/:language/admin', [ 'controller'=> 'pages', 'action'=> 'index','admin' => true], $lang_settings);
Router::connect('/:language/admin/:controller/:action/*',['admin' => true], $lang_settings);
Router::connect('/:language/admin/:controller/*',['admin' => true], $lang_settings);
Router::connect('/:language/:controller/:action/*',[], $lang_settings);

require CAKE . 'Config' . DS . 'routes.php';
Adam Friedman
  • 520
  • 6
  • 20

1 Answers1

0

Edit: Update your router to use negative lookahead with the languages your application supports.

Here's an example regex:

^(?!(spa|eng|swe)){1}.*

Any route that doesn't start with spa, eng or swe, you'd redirect that to the controller and action of your choice.

In your router

Router::connect(
    '/:language/candidates/search/*',
    array('controller' => 'errorController', 'action' => 'index'),
    array('language' => '^(?!(spa|eng|swe)).*')
);

You'll need this route pattern for every section, since the number of url parameters vary.

You can test regex with various online Regex tools. Here's that pattern with some test data to validate that it works.


Original post:

You may be overthinking this.

Your question is on how to add a route for a MissingController, something that only occurs when you're developing (in debug mode). In production this turns into 404.

Since showing a developer what is missing is what any proper framework should do (CakePHP included), you should not set out to write your own ExceptionHandler for these cases to try shoehorning some bizarre solution.

Instead, you should look at handling that 404, since, as said, once in production, that error message will default to a 404 error message.

Here's how the same (missing) route looks:

In Development mode:

enter image description here

In Production mode:

enter image description here

Handling a 404

If you want to display a 404, this question explains on how to customize this error page.

If you want to redirect somewhere when a 404 happens, you can do something like this (from this question):

To catch and handle 404 errors, you need to extend the ErrorHandler class and override the error404 method. To do that, create the file app/app_error.php with the following code:

class AppError extends ErrorHandler {
    function error404($params) {
        // redirect to homepage (or any page you like)
        $this->controller->redirect('/');
    }
}

Couple of side notes

To make it easier for yourself down the line, consider changing your routing schemes to include the language as a request parameter instead. Otherwise search engines (if your site should be indexes), will crawl your language pages, and that's not always what you want.

That means you'll have routes like this:

candidates/list?lang=eng

That way you can easier manage handling invalid language codes by doing i.e:

    // In a controller
    $languageCode = $this->request->params['lang'];
    if (!in_array($language, ['eng', 'spa', 'swe'])) {
      // Set a variable to view to display a wrong error code, or redirect to a default language.
    }

Hope any of this made sense.

Coreus
  • 5,360
  • 3
  • 35
  • 50
  • Thanks for your answer. I am very specifically asking about fixing the debug mode error so as to have errors which are not misleading for me and other developers working on the application. (I am well aware that these are 404s in production.) I have a feeling there is a one-liner I could add to routes to fix this. I would rather not refactor my routing paradigm. – Adam Friedman Aug 27 '19 at 18:32
  • The answer had originally a portion regarding writing custom exception handlers for MissingControllerExceptions, but the idea is so horrible that I removed it. You can read on how to make your own exception handlers here: https://book.cakephp.org/2.0/en/development/exceptions.html#creating-your-own-application-exceptions – Coreus Aug 27 '19 at 23:51
  • @AdamFriedman The answer now has an updated suggestion. Have a look at that, and if that helped you, please accept the answer. – Coreus Aug 28 '19 at 07:57