CakePHP: make global routes for admin plugin - php

So I started making the admin panel of our site as a plugin. And I'm able to redirect every request like domain.com/admin or domain.com/admin/users or domain.com/admin/pages/edit/5 - to the appropriate controller and action from the admin plugin.
Like this one:
Router::connect('/admin', array('plugin' => 'admin', 'controller' => 'index', 'action' => 'index'));
Router::connect('/admin/users/list', array('plugin' => 'admin', 'controller' => 'users', 'action' => 'list'));
But this means I will have to write separate route for almost each URL ??? or actually - for each action ... So - is there a way to set it globally? ...
For example:
Router::connect('/admin/users/*', array('plugin' => 'admin', 'controller' => 'users'));
Or even better:
Router::connect('/admin/*', array('plugin' => 'admin'));
Cause the last two examples didn't work at all ...
EDIT: the CakePHP version is the latest at the moment - 2.4.1.

Normally you shouldn't have to create such routes for plugins, the basic mapping works out of the box, ie in case your plugin is named admin, then /admin/users/list will automatically map to plugin admin, controller users, action list.
An exception is your /admin route, by default this would look for index on AdminController. So for mapping to index on IndexController you'll need a custom route like the one in your question:
Router::connect('/admin', array('plugin' => 'admin', 'controller' => 'index', 'action' => 'index'));
Apart from this it should work as is in case you don't have any other preceding rules that will override the default behaviour, something like Router::connect('/*', ...) for example. In case there are such rules and you cannot change them, then this is how you could connect the basic routes of your plugin:
Router::connect('/admin/:controller/:action/*', array('plugin' => 'admin'));
Router::connect('/admin/:controller/*', array('plugin' => 'admin', 'action' => 'index'));
Router::connect('/admin/*', array('plugin' => 'admin', 'controller' => 'index', 'action' => 'index'));
Note that this needs to be placed before the other routes that override the default behaviour!
See also http://book.cakephp.org/2.0/en/development/routing.html
On a side note, list as an action name will probably not work, this should throw a parser error as list is a reserved keyword. So you in case this isn't just an example you'll need an extra route for that if you want to use /list as the action name in the URL.

Related

CakePHP 2.x language prefix in routing rules are not being applied when user requests a missing controller

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';
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:
In Production mode:
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.

Cakephp 3 - Routes being either an action or a parameter

I would like to know how to route the following scenario: I have a controller called Users, in this controller I have many actions, one of those is "profile".
I want my address being like that: mysite.com/users/NameOfTheUser OR mysite.com/users/edit-profile OR mysite.com/users/edit-photo, etc.
When you go for "edit-profile" you will be redirected to the edit_profile action, but here goes the trick, when you go to "NameOfTheUser" I want to redirect to the "profile" action, passing "NameOfTheUser" as a parameter.
Is there a way to do so without routing every action manually?
EDIT
I used the code that Yosi Azwan said, it works but I have to create a new route for each other page in the controller users.
Router::connect( '/users/:name', ['controller' => 'Users', 'action' => 'profile'], ['pass' => ['name']] );
Maybe this is what you are looking for
Router::connect( '/users/:name', ['controller' => 'Users', 'action' => 'profile'], ['pass' => ['name']] );
and read this for complete documentations http://book.cakephp.org/3.0/en/development/routing.html

cakephp routing issue after adding a plugin

I added CakeDC-Users plugin in app/plugins.
Now for http://example.com/ , if i click the home/index link, it wrongly redirects to http://example.com/users/posts/index , but it should be http://example.com/posts/index .
Why is the plugin 'users' always added before the respective controller?
If i delete the CakeDC-Users plugin from app/Plugin and delete that line CakePlugin::loadAll(); from bootstrap.php then i get normal link/route
routes.php:
Router::connect('/', array('controller' => 'posts', 'action' => 'index'));
How can i fix that problem addin CakeDC-Users plugin
That is the way how plugins are accessed. You can define your custom route this way
Router::connect('/posts', array('controller' => 'posts', 'plugin' => 'users'));
This will be done in app/Config/routes.php
if it is for link issues you will have to explicitly specify
echo $this->Html->link('link', array(
'controller' => '',
'action' => '',
'plugin' => false)
);
But i would prefer custom routing.
For more info, you can always sneak into the CookBook

Kohana 3 Route :: Make a second default route?

I tried this:
Route::set('default_controllers', '(<controller>(/<action>(/<id>)))')
->defaults(array(
'controller' => 'welcome',
'action' => 'index',
));
Route::set('default', '<uri>')
->defaults(array(
'controller' => 'cms',
'directory' => 'cms',
'action' => 'render',
));
But actually I want the 'default' (with the render action) to come first than the default_controllers.
I want it to first check any controllers, and if there is nothing then it should run the second default, render. Render checks the uri in the database and returns the page if exists or else it throws a error.
If i switch on the two's route position, so the 'default' route come before 'default_controllers' then it works fine with the cms pages, but not with the controllers (since it does not look for further routes, after the render function has thrown an error that the page does not exists.)
What do i do here? How can i make them both work?
You basically have two catchall routes here. You should remove one of them, and make your routes more specific. The (<controller>(/<action>(/<id>))) route is actually very bad, and is only provided as an example.
To get this to work, you have to specifically tell the route which controllers to load.
Route::set('default_controllers', '(<controller>(/<action>(/<id>)))', array(
'controller' => 'controller|anotherController|etcController'
))
->defaults(array(
'controller' => 'welcome',
'action' => 'index',
));
If you wanted to, you could write a class to go look for the controllers and cache the result as to not increase load times. You'd then pass this value into the value for the controller key in the array.
Your other route can remain how you had it:
Route::set('default', '<uri>')
->defaults(array(
'controller' => 'cms',
'directory' => 'cms',
'action' => 'render',
));

Kohana 3.1 routes with default subdirectories

I have an application that is essentially working until I tried to implement an Auth module for logins and registrations.
My application directory structure is basically:
application
-- classes
-- controller
-- admin
(admin area)
-- block
(blocks to display within pages)
-- page
(default pages)
By default I want to have URL's such as http://www.testsite.com/test which would access the Controller_Page_Test class. Or to explicitly call admin or block pages http://www.testsite.com/admin/test which would access the Controller_Admin_Test class. To further complicate matters it also needs to handle optional actions and id's.
I said at the top that this is basically working correctly - until I've tried to add in the Auth module. The Auth module calls http://www.testsite.com/user/login but instead of accessing the module's path via the default, it looks in the page directory.
To overcome this I placed a higher level route but now this has become my default page handler. Explicit calls still get through.
My routes currently look like this:
Route::set('user', '(<controller>(/<action>(/<id>)))', array('controller' => 'user|admin_user'))
->defaults(array(
'controller' => 'user',
'action' => 'index',
'id' => NULL,
));
Route::set('with_dir', '<directory>/<controller>(/<action>(/<id>))', array('directory' => 'block|admin'))
->defaults(array(
'directory' => 'page',
'controller' => 'home',
'action' => 'index',
));
Route::set('just_id', '<controller>(/<id>)', array('id' => '\d+'))
->defaults(array(
'directory' => 'page',
'controller' => 'home',
'action' => 'index',
));
Route::set('auto_dir', '<controller>(/<action>(/<id>))', array('id' => '\d+'))
->defaults(array(
'directory' => 'page',
'controller' => 'home',
'action' => 'index',
));
Route::set('default', '(<controller>(/<action>(/<id>)))')
->defaults(array(
'controller' => 'prototype',
'action' => 'index',
));
Can this be cleaned up any better? And how do I get this module to only kick in when I need it to?
Yes, it can be cleaned better. Kohana developers encourage people using this framework to add as much routes as needed. You can even specify them for each action which will enable you to change URLs in the future (eg. instead of /user/login you may wish to have /signin), if you use proper methods to generate links etc. (eg. Route::url() helper).
Now, saying that, here is the other way to specify the user route:
Route::set('user', '<controller>(/<action>(/<id>))', array('controller' => '(user|admin_user)'))
->defaults(array(
'controller' => 'user',
'action' => 'index',
));
Which will match only the requests, where the first part of the URI is given and is equal either to user or admin_user. Previously the controller part was optional, thus was also matching calls to / URI.

Categories