CakePHP 3 Component - Request Data or Query being Cleared - cakephp

I have come across and issue that I need help with. I cannot figure out why this is happening.
I am creating a component in CakePHP that intercepts the data being passed to /users/login and depending on the parameter, it acts on it. However, the issue is that, when that data gets there, it is empty. To also note, is that it worked good on remote machines as well as local. The issue started happening when I used Composer to upload this plugin into another site. I wonder if the issue is because Composer installs the plugin under the vendor directory.
This is my code inside the component's startup method
public function initialize(array $config)
{
// ....
$this->Controller = $this->_registry->getController();
// ...
}
public function startup(Event $event)
{
// Here I try to get the [test] parameter that is pass in url
// http://www.domain.com/users/login?test=testing
$test = $this->Controller->request->query('test');
}
If I attempt to print the controller request inside the startup(Event $event) with debug($this->Controller->request); I get the following array:
object(Cake\Network\Request) {
params => [
'plugin' => null,
'controller' => 'Users',
'action' => 'login',
'_ext' => null,
'pass' => [],
'isAjax' => false
]
data => []
query => []
url => 'users/login'
base => ''
webroot => '/'
here => '/users/login'
trustProxy => false
[protected] _environment => [
'SCRIPT_FILENAME' => '/var/www/domain.com/website/webroot/index.php',
'SCRIPT_NAME' => '/index.php',
'REQUEST_URI' => '/users/login?test=testing',
'DOCUMENT_URI' => '/index.php',
The REQUEST_URI shows clearly that test is being passed.
What could be causing CakePHP not to take in the test parameter when it was installed using Composer? Why is params['pass'] => [], data => [] and query => [] empty?

After hours of trying to figure this out, it had to take my 8-year old daughter to point me out in the right direction. A simple, but time-wasting, head-scratching mistake that was causing all the issues. The issue was located inside my NGINX configuration file.
I had the following:
location / {
try_files $uri $uri/ /index.php$args;
}
but I should have had the following:
location / {
try_files $uri $uri/ /index.php?$args;
}
Because I had ommitted the ? after index.php, NGINX was simply not taking any parameters that were passed in the url.
Lesson learned!

Related

requestAction is not working in CakePHP 2 Shell Command

I'm attempting to make a cron shell, and the ability to use requestAction for this is crucial.
For testing purposes, I've reduced my Shell down to just this:
class CronShell extends AppShell {
public $uses = array('Cron');
public function main() {
$this->requestAction(['controller' => 'events', 'action' => 'play', 38, 0, true, true]);
echo "This will never be printed to the console, it dies before this.";
}
}
And I still cannot get this to run. It simply returns apparently successful, but actually is not. (no error) Another SO question suggested beforeFilter (and other lifecycle methods) could be the cause, but I've ensured that is not the case. See update.
Other controller/actions aren't working.
Removing the action's return doesn't help.
Using a string-based call doesn't help.
CakePHP v2.10.22
Update: It looks like the Auth component may be causing a redirect to users/login in AppController. Yet I see nowhere that I tell it to redirect here, and I don't even have a users/login action. When I remove 'Auth' from my public $components = [], the shell runs on CLI.
But I'm having trouble disabling Auth! I've tried allow(*), setting the redirects to false, even trying $this->components = ['']. I am always redirected to users/login.
Well, it turns out it wasn't the beforeFilter or any part of the lifecycle, but I did have the Auth component on my AppController like such:
public $components = [
'Session',
'Auth' => [
'loginRedirect' => ['controller' => 'worlds', 'action' => 'route']
]
];
Which, no matter what I did to allow or simulate Auth for cli, just constantly redirected me. So I reduced the above to just Session and wrote under,
if (php_sapi_name() !== 'cli')
This:
$this->components = [
'Session',
'Auth' => [
'loginRedirect' => ['controller' => 'worlds', 'action' => 'route']
]
];
And that removed Auth from the equation. I would have expected CakePHP 2's console to give me any indication it was being redirected, but I'll bet future versions handle it better.

How to have different dashboards based on roles with cakedc plugins / users & acl

I am using CakeDC Users & ACL plugins in my CakePhp app. I have different roles for my users in my app and I would like to have different dashboards based on roles after login.
I extend the plugin with my own table and controller based on the documentation here, so I have MyUsersController and MyUsersTable which override the initial files of the plugin, UsersController and UsersTable. Everything works fine. I create an event in my events.php file which contains:
use CakeDC\Users\Controller\Component\UsersAuthComponent;
use Cake\Event\Event;
use Cake\Event\EventManager;
EventManager::instance()->on(
UsersAuthComponent::EVENT_AFTER_LOGIN,
['priority' => 99],
function (Event $event) {
if ($event->data['user']['role_id'] === 'bbcb3031-ebed-445e-8507-f9effb2de026') //the id of my client role{
return ['plugin' => 'CakeDC/Users', 'controller' => 'MyUsers', 'action' => 'index', '_full' => true, 'prefix' => false];
}
}
);
But it seems like the override is not working because I have an error:
Error: CakeDC/Users.MyUsersController could not be found.
In my URL I have /users/my-users instead of /my-users and I don't know why. I have test with a template file which is include in the plugin and the Users controller like this:
function (Event $event) {
if ($event->data['user']['role_id'] === 'bbcb3031-ebed-445e-8507-
f9effb2de026') //the id of role{
return ['plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => 'profile';
}
And it works. My URL redirect after login as a client is /profile.
Could someone help me to understand? Please tell me if it's not clear enough and if it's missing parts of codes that might be important to understand my problem.
I specify that I am beginner with Cake.
Your custom controller doesn't live in the CakeDC/Users plugin, hence you must disable the plugin key accordingly, so that the correct URL is being generated (assuming your routes are set up correctly) that connects to your controller, like this:
[
'plugin' => null,
'controller' => 'MyUsers',
'action' => 'index',
'_full' => true,
'prefix' => false
]
That would for example match the default fallback routes, generating a URL like /my-users.
See also:
Cookbook > Routing > Creating Links to Plugin Routes

how to configure routes for child controllers in CakePHP 3.x?

Reports has many ReportInstances
ReportInstances belongs to Reports
I would like to have the url /reports/:report-id/instances to point to the action index_by_report_id inside ReportInstancesController.php
How do I configure the routes.php accordingly?
UPDATE:
I tried nested resources as described here:
http://book.cakephp.org/3.0/en/development/routing.html#creating-nested-resource-routes
Here are my routes
$routes->resources('Reports', [
'map' => [
'standard' => [
'action' => 'standard',
'method' => 'GET',
]
]
]);
$routes->resources('Reports', function ($routes) {
$routes->resources('ReportInstances');
});
When I do a /reports/1/instances, it goes to ReportsController looking for action 1.
Please advise.
Do this in your routes.php
$routes->resources('Parents', function ($routes) {
$routes->resources('Children');
});
$routes->resources('Children');
In ChildrenController.php,
protected function _prepareConditions() {
$parentId = isset($this->request->params['parent_id']) ? $this->request->params['parent_id'] : null;
if ($parentId == null) {
return [];
}
return [
'Children.parent_id' => $parentId
];
}
public function index()
{
$conditions = $this->_prepareConditions();
$this->paginate = [
'contain' => ['Parents'],
'conditions' => $conditions
];
// ... and so on
You will be able to do the following:
/parents/1/children
/parents/1/children.json
/children
/children.json
Why this works?
http://book.cakephp.org/3.0/en/development/routing.html#creating-nested-resource-routes
tells us that basically to basically retrieve the parent id from the request params.
What it does not say explicitly is that, the routes will then reuse the basic 5 functions: index, add, view, delete, edit even when you nest them under a parent url.
Why do you still have a separate resources route for the Children?
This allows the /children and /children.json to work if you need them as well.
What about add?
I haven't tried that but I do not foresee any issues with using that as
/parents/1/children/add
/parents/1/children/add.json
/children/add
/children/add.json

Plugin route to a prefixed controller

I'm creating a plugin for my application (using CakePHP 2.6.0) that allows users to login into a user area using the same model as for the admin area, so I'm trying to get the same type of URI scheme as the admin area e.g. /admin/users/login but then for /special/users/login. I have the following route in my plugin's Config/routes.php and added the 'special' prefix to the 'Routing.prefixes' configuration:
Router::connect('/special/:controller/:action', array(
'special' => true,
'prefix' => 'special',
'plugin' => 'Special',
'controller' => ':controller',
'action' => ':action',
));
With above route and entering the following url /special/users/login it would make sense to me right now if Cake went for Plugin/Controller/SpecialUsersController.php (considering namespace conflicts with the main application's UsersController) but instead I get an error Error: Create the class UsersController.
Is there a built-in way for it to load a prefixed controller (without changing the url) based on the plugin? Or is there a better way to neatly extend my main application? Am I going about this the wrong way?
I could not find a built-in way for it to work as I wanted to so I changed the way URL strings were parsed using a custom route class in my app's Routing/Route/ folder:
App::uses('CakeRoute', 'Routing/Route');
/**
* Plugin route will make sure plugin routes get
* redirected to a prefixed controller.
*/
class PluginRoute extends CakeRoute {
/**
* Parses a string URL into an array.
*
* #param string $url The URL to parse
* #return bool False on failure
*/
public function parse($url) {
$params = parent::parse($url);
if($params && !empty($params['controller']) && !empty($params['plugin'])) {
$params['controller'] = $params['plugin'] . ucfirst($params['controller']);
}
return $params;
}
}
And then setting up my plugin routes as:
App::uses('PluginRoute', 'Routing/Route');
Router::connect('/special/:controller/:action', array(
'special' => true,
'prefix' => 'special',
'plugin' => 'Special',
'controller' => ':controller',
'action' => ':action',
), array(
'routeClass' => 'PluginRoute'
));
This resulted in /special/users/login creating the controller I wanted Plugin/Controller/SpecialUsersController.php, no side effects so far. Extending the main application's UsersController is a different story though.
Maybe someone else has use of this or knows a better solution?

How to connect a post rest request to the add controller action using prefixes in cakephp

I code a client/server application.
Server side is powered by CakePHP 2.4.7.
Client side run with angularjs and cordova on mobile devices
I use several cakephp route prefixes whose 'admin' and 'mobile'.
(And I use $resource and $httpInterceptor angularjs factories to setup my REST requests.)
I want to call /website/mobile/posts/add.json with json data posting.
So I call /website/mobile/posts.json with a POST ajax query:
The problem is here: the called controller action is 'index', not 'add';
On top of my PostsController, I added this:
echo $this->request->params['action'] . "\n";
echo ($this->request->isPost())? "post" . "\n" : "not post";
die;
and the response is always:
mobile_index
post
So the ajax request seems correct but cakephp don't map it to the add action, but index one.
However my configuration seems good too; here is a fragment of my routes.php
Router::mapResources(array('users','posts'));
Router::parseExtensions('json');
Any idea ?
Prefix routing doesn't work with REST routing out of the box
REST routing works by automatically creating routes for the controllers passed to Router::mapResources(). Prefix routing pretty much does the same, it creates default routes including the prefixes defined in Routing.prefixes.
However both functionalities don't know about each other, they both create separte routes, so Router::mapResources() will connect to URLs without prefixes (the prefix option for this method is not using actual prefix routing, it will just add the value of that option to the beginning of the URL to connect to!), and therefore your request to /mobile/... doesn't actually use REST routing but only prefix routing.
Defining prefixed REST routes manually
There is no simple fix for this problem like using an option or something, instead you'll have to define the REST routes manually so that the prefix option is included, simple enough though.
See Modifying the default REST routes and Custom REST Routing.
A sample Route could look like this:
Router::connect(
'/mobile/users',
array(
'prefix' => 'mobile',
'mobile' => true,
'controller' => 'users',
'action' => 'add',
'[method]' => 'POST'
)
);
This would connect POST requests to /mobile/users to UsersController::mobile_add(). Similary you'll have to do this for all other methods like GET and PUT, with and without passing an id, etc.
Note that when connecting manually you can ditch the Routing.prefixes option and of course the call to Router::mapResources().
Automated mapping
Defining all those routes by hand is kinda exhausting, you're better of automating it with respect to the Router::resourceMap() configuration.
Here's an example on how to do that, it's somewhat similar to Router::mapResources(), but it accepts a prefix option that actually makes use of prefix routing:
function mapResources(array $controllers) {
$resourceMap = Router::resourceMap();
foreach($controllers as $controller => $options) {
if(!is_array($options)) {
$controller = $options;
$options = array();
}
$options += array(
'prefix' => null,
'plugin' => null,
'id' => Router::ID . '|' . Router::UUID
);
foreach($resourceMap as $params) {
$url = '';
if($options['prefix']) {
$url .= '/' . $options['prefix'];
}
if($options['plugin']) {
$url .= '/' . $options['plugin'];
}
$url .= '/' . $controller;
if($params['id']) {
$url .= '/:id';
}
Router::connect(
$url,
array(
'prefix' => $options['prefix'],
$options['prefix'] => !!$options['prefix'],
'plugin' => $options['plugin'],
'controller' => $controller,
'action' => $params['action'],
'[method]' => $params['method']
),
array(
'id' => $options['id'],
'pass' => array('id')
)
);
}
}
}
You would call it like this:
mapResources(array(
'books' => array(
'prefix' => 'mobile'
)
));
and it would map all the REST routes for your books controller using the mobile prefix.

Resources