CakePHP administrator routing - cakephp

I am having a hard time to understand this admin-routing of CakePHP. From the Cookbook:
"You can map the url /admin to your admin_index action of pages controller using following route:"
Router::connect('/admin', array('controller' => 'pages', 'action' => 'index', 'admin' => true));
What is not clear to me is the "'admin' = true". What is the purpose of this? When you call logout-function, do you pass the argument "'admin' = false" to redirect function in AppController?
When you have multiple prefixes in use, do you use "'manager' = true" if you want to enable manager-prefix?

admin=true in a route means your url is prefixed with /admin.
So in your example, your /admin route actually connects to: /admin/pages/index, and is serviced by the admin_index() action in your pages controller (as opposed to the non-prefixed index() action).
You can either ensure all links to logout are created with admin=false so they map to the standard Users::logout() action, or create a new action admin_logout() which handles admin logouts.
Adding manager=true to a url (along with the associated prefix setting) means the url with begin with /manager, and will map to manager_...() functions in the controller.
You can use both (or more!) prefixes, but not in the same url.
/pages/index maps to: PagesController:index();
/admin/pages/index maps to: PagesController:admin_index();
/manager/pages/index maps to: PagesController:manager_index();

Router Prefixes
In CakePHP 2.x prefixes changed a little.
You can have multiple prefixes now, but they have to be declared in the core.php file.
Configure::write('Routing.prefixes', array('admin','api','json'));
That would declare 3 prefixes, and there is no need to modify the routing tables to make them work. The word prefix means it's placed before the name of the action when dispatching to the controller.
For example;
class DocumentsController extends AppController
{
public index() { ... }
public admin_index() { ... }
public api_index() { ... }
public json_index() { ... }
}
CakePHP will call the correct action when one of these URLs are request.
http://example.com/documents/index
http://example.com/admin/documents/index
http://example.com/api/documents/index
http://example.com/json/documents/index
What you can not do is use more than one prefix at a time. The following will NOT work.
http://example.com/admin/json/documents/index
That would require custom routing, because CakePHP doesn't know which action to call.
When an action is called you can which for prefixes in the request params.
public function beforeFilter()
{
if(isset($this->request->params['admin']))
{
// an admin prefix call is being made
}
}

Related

CakePHP 3 - Passing parameters to application with a single controller where all requests are routed to it

CakePHP 3.x
In the Routing documentation (https://book.cakephp.org/3.0/en/development/routing.html) it says:
If you have a single controller in your application and you do not want the controller name to appear in the URL, you can map all URLs to actions in your controller. For example, to map all URLs to actions of the home controller, e.g have URLs like /demo instead of /home/demo, you can do the following:
$routes->connect('/:action', ['controller' => 'Home']);
That's fine as it means I can do stuff like this in my src/Controller/HomeController.php:
public function foo()
{
// Accessible through URL "/foo"
}
public function bar()
{
// Accessible through URL "/bar"
}
But if I need to pass parameters to a function, e.g.
public function baz($a, $b, $c)
{
}
It will give me a "Missing Controller" error if I call any of the following URLs:
/baz/2
/baz/2/17
/baz/2/17/99
It's saying that I need to create "BazController".
All of these, will work, because they include the controller name:
/home/baz/2
/home/baz/2/17
/home/baz/2/17/99
Presumably this is a routing problem?
Interestingly, calling /baz without any parameters works ok.
The correct answer, as provided in a comment by #arilia is:
$routes->connect('/:action/*', ['controller' => 'Home']);
The URL's provided in my question will now work as follows. All of these will be mapped to HomeController.php and execute the baz() function:
/baz/2
/baz/2/17
/baz/2/17/99
The * (at the end of /:action/* in app/routes.php) is key to allowing any number of parameters passed in the URL.

calling plugin's controller from AppController in CakePHP 3

I have written a plugin for CakePHP 3.4.*.
This plugin will check for Database configuration has been set or not, if not then It you move user through a GUI interface to setup database configuration just like wordpress.
The plugin is working perfectly, but it has to be loaded manually by visiting url of the plugin
http://example.com/installer/install
where installer is the plugin name which is calling InstallController class inside plugins/Installer/src/Controller/ directory
Now what I want to check it automatically and redirect user to the Installation interface if database connection couldn't be established.
For that I have written a function inside InstallController of plugin's controller
public function installationCheck() {
$db = ConnectionManager::get('default');
if(!$db->connect()) {
if(Configure::read('Database.installed') == true) {
$this->Flash->error(__("Database connection couldn't be established. Please, re-configure it to start the application"));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__("Please configure your database settings for working of your application"));
return $this->redirect(['action' => 'index']);
}
}
return true;
}
Now the Question.
What is the easiest way to call this method from /app/src/Controller/AppController.php file of the main application?
Simple answer, you don't!
Shared controller logic belongs either in AppController itself, a Component or a Trait. The AppController should never being accessing methods defined in other controllers, these shouldn't be made accessible to it.
For what you're doing you probably want to do this in a component that you can load via your AppController or the relevant controller.
So your component would look something like:-
<?php
namespace Installer\Controller\Component;
use Cake\Controller\Component;
class InstallComponent extends Component
{
public function installationCheck()
{
// Method's logic
}
}
Which you would then load in the relevant controller:-
public function initialize()
{
parent::initialize();
$this->loadComponent('Installer.Install');
}
Then you can use the component's method from the controller like:-
$this->Install->installationCheck();
You should not Do that!
If you need to access another controller I recommend you to move that functionality to a Component, that are packages of logic that are shared between controllers.

Cakephp 3 - CRUD plugin - Use id from auth component

Currently, I'm using the CRUD v4 plugin for Cakephp 3. For the edit function in my user controller it is important that only a user itself can alter his or her credentials. I want to make this possible by inserting the user id from the authentication component. The following controller method:
public function edit($id = null){
$this->Crud->on('beforeSave', function(\Cake\Event\Event $event) {
$event->subject()->entity->id = $this->Auth->user('id');
});
return $this->Crud->execute();
}
How can I make sure I don't need to give the id through the url? The standard implementation requires the url give like this: http://domain.com/api/users/edit/1.json through PUT request. What I want to do is that a user can just fill in http://domain.com/api/users/edit.json and send a JSON body with it.
I already tried several things under which:
$id = null when the parameter is given, like in the example above. Without giving any id in the url this will throw a 404 error which is caused by the _notFound method in the FindMethodTrait.php
Use beforeFind instead of beforeSave. This doesn't work either since this isn't the appropriate method for the edit function.
Give just a random id which doesn't exist in the database. This will through a 404 error. I think this is the most significant sign (combined with point 1) that there is something wrong. Since I try to overwrite this value, the CRUD plugin doesn't allow me to do that in a way that my inserting value is just totally ignored (overwriting the $event->subject()->entity->id).
Try to access the method with PUT through http://domain.com/api/users.json. This will try to route the action to the index method.
Just a few checks: the controllerTrait is used in my AppController and the crud edit function is not disabled.
Does anyone know what I'm doing wrong here? Is this a bug?
I personally would use the controller authorize in the Auth component to prevent anyone from updating someone else's information. That way you do not have to change up the crud code. Something like this...
Add this line to config of the Auth component (which is probably in your AppController):
'authorize' => ['Controller']
Then, inside the app controller create a function called isAuthorized:
public function isAuthorized($user) {
return true;
}
Then, inside your UsersController you can override the isAuthorized function:
public function isAuthorized($user) {
// The owner of an article can edit and delete it
if (in_array($this->request->action, ['edit'])) {
$userId = (int)$this->request->params['pass'][0];
if ($user['id'] !== $userId) {
return false;
}
}
return parent::isAuthorized($user);
}

CakePHP : override BasicAuthenticate.php

I'm trying to override lib/Cake/Controller/Component/Auth/BasicAuthenticate.php,
because I need to change the unauthenticated() method.
So I copied and modified the file to app/Lib/Cake/Controller/Component/Auth/BasicAuthenticate.php (also tried without the 'Cake' folder), but the changes are not taken into account.
My edit works when modifying directly the core file but I'd rather not.
How should I do ?
I'm using Cake 2.5
Check if you really need to override a core class
To me this looks like you are on the wrong track, overriding the class shouldn't be neccesary unless for example you have no controler over where and how the basic authentication adapter is being used (for example in a plugin that doesn't offer configuration).
If you'd really need to overwrite the class, then the path should be
app/Lib/Controller/Component/Auth/BasicAuthenticate.php
and it should work just fine (it does for me, using CakePHP 2.5.6).
http://book.cakephp.org/2.0/en/core-utility-libraries/app.html#overriding-classes-in-cakephp
Use a custom authentication handler instead
If you have control over the adapter configuration, the I'd suggest that you extend the BasicAuthenticate class instead, and only override the unauthenticate() method, and finally make the auth component use the custom adapter.
Something like
app/Controller/Component/Auth/CustomBasicAuthenticate.php
App::uses('BasicAuthenticate', 'Controller/Component/Auth');
class CustomBasicAuthenticate extends BasicAuthenticate {
public function unauthenticated(CakeRequest $request, CakeResponse $response) {
// do something special
}
}
Controller
public $components = array(
'Auth' => array(
'authenticate' => array(
'CustomBasic'
)
)
);
See also the Creating Custom Authentication objects section in the Cookbook.

CakePHP Auth Module Tutorial--Why is it requesting user/login for these pages?

I have a custom routing setup (which might be cause of issue). As follows:
Router::connect('/o/*', array('controller' => 'open', 'action' => 'openinsert'));
Router::connect('/c/*', array('controller' => 'click', 'action' => 'clickinsert'));
Under UsersController, I have tried:
public function beforeFilter() {
parent::beforeFilter();
//$this->Auth->allow('add');
$this->Auth->allow('o/*');
$this->Auth->allow('clickinsert');
$this->Auth->allow('open');
}
clickinsert and openinsert are both apart of controller 'ClickController' and 'OpenController' respectively.
Nothing from above works. Is is possible to do what I'm wanting to do w/their default auth setup? Or will I need to make some major changes?
AuthComponent::allow
This method:
Takes a list of actions in the current controller for which authentication is not required, or no parameters to allow all actions.
It doesn't accept urls.
In particular this:
$this->Auth->allow('o/*');
Is neither the wildcard ( nothing or '*' depending on the version of CakePHP used) nor the name of an action, and this:
$this->Auth->allow('open');
Is apparently passing the name of a controller so neither of those method calls would do anything.
There's some confusion here
According to these two routes:
Router::connect('/o/*', array('controller' => 'open' <-
Router::connect('/c/*', array('controller' => 'click', <-
They point to two different controllers.
Under UsersController, I have tried:
Anything you put in your users controller is irrelevant for requests that don't use it. Either put the allow calls in the before filter for each controller - or put them in the beforeFilter for your App controller (which all controllers inherit from).

Resources