I'm trying to use the CakeResponse to create and download a file from a string in one of my Components (App/Controller/Component) but I got this error
Call to a member function body() on a non-object
with the code below
$this->response->body("toto");
$this->response->download("titi.txt");
return $this->response;
How can I access to the CakeResponse from a component ?
Thanks for your help and sorry for my english.
Doesnt the error tell you whats wrong?
It clearly says that the object is not available this way.
Usually, you would try to access it via the controller object inside the component callbacks:
public function initialize(Controller $controller) {
$controller->response->body(...);
...
}
etc
If you need it in other methods you can pass it to local attribute in initialize() and reuse it then later:
$this->Controller = $controller;
Related
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.
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.
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);
}
I have an API wirtten and gave out instructions how to us it.
For example to test the login you can call /api/login
Now I see in the logs, that someone keeps calling /API/LOGIN and gets an error 500.
Is there somehow a way, to catch such errors ONLY when calling /api/ controller functions?
In that case, I would like to send back a response like Error. Wrong function call.
I do not want to send this in general when a error 500 happens. Really only when /api/ related.
The error which gets reported belongs to the fact that I am calling in AppController::beforeFilter() a function like
$this->Api->check($username)
And I get this error when debug=2
Call to a member function check() on a non-object
When I call /api/login/ the functions works perfect.
I look forward for any tips! Thanks!
The problem you are facing isn't the casing of the action (in PHP method names are case insensitive), but the casing of the controller. It won't find APIController and therefore throw an missing controller exception. Your AppController is then being invoked as it is being extended by CakeErrorController which is used on errors.
I can only assume that $this->Api refers to a model, and since the actual controller is CakeErrorController, that model of course isn't being loaded, hence the non-object error.
There are various ways to solve this problem, personally I'd probably hook in to the Dispatcher.beforeDispatch event and throw an exception or define an appropriate response if necessary, something like this:
// app/Config/bootstrap.php
App::uses('CakeEvent', 'Event');
App::uses('CakeEventManager', 'Event');
CakeEventManager::instance()->attach(
function(CakeEvent $event) {
$controller = $event->data['request']->params['controller'];
$action = $event->data['request']->params['action'];
if(strtolower($controller) === 'api') {
$response = $event->data['response'];
if($controller !== 'api') {
$response->statusCode('403');
$response->body('Invalid controller message');
return $response;
}
if(strtolower($action) !== $action) {
$response->statusCode('403');
$response->body('Invalid action method');
return $response;
}
}
},
'Dispatcher.beforeDispatch',
array('priority' => 11)
);
This would enforce using lowercase for the controller and the action in case the api controller is being targeted.
However as already mentioned, method names are case insensitive, so forcing lowercase for actions isn't necessary from a technical point of view. Anyways, it's just an example...
I'm using spark_plug plugin on cakephp, this plugin provides an authentication-acl system for register and admin users in cakephp. I want to add some new code and functionalities to the user's controller but I don't want to change the "main" plugin files.
I was thinking if it is possible leave the "main" plugin controller as it (unchanged) "\app\plugins\spark_plug\controllers\users_controller.php" and create a secondary controller with all the new code and functionalities, something like this "\app\controllers\users_controller.php" and extends the plugin "main" controller.
Is that possible? and how achieve that?
Or do you think is there any other way to do what I want?
Thanks!
You could perhaps use composition rather than inheritance? I.e. create a "app\controllers\users_controller" that has inside it an instance of the plugin's controller. The UsersController passes through any unmodified actions via stubs, eg:
class UsersController extends AppController {
...
var spark_plug_users_controller;
...
public function __construct() {
parent::__construct();
App::import('Controller', 'SparkPlug/Users'); // this is probably wrong.
$this->spark_plug_users_controller = new UsersController; // as is this.
$this->spark_plug_users_controller->constructClasses();
}
...
//example non-overridden method
function login() {
return $this->spark_plug_users_controller->login();
}
...
}
your problem would be accessing protected/private methods within the spark_plug Users controller. But if you did not need to, this may work.