How to stop execution of a Request in Kohana? - php

Let's say I have a controller template with a before function like so...
public function before()
{
parent::before();
if ($this->request === Request::instance())
{
// its a main request, throw an exception or redirect
Request::instance()->redirect('/');
}
else
{
// ok
}
}
But let's say I don't want to redirect, I want to stop the Request flow, and do nothing.
Does an exception do this? Is there a simple way, like Request::die();?
EDIT:: I actually don't want to halt the Request flow, just prevent this controller from doing anything. It's likely that this controller was called from another controller, and I want to pass the control back to the calling controller.'
Thanks!

1.Use exceptions (not tested yet):
try
(
Request->instance()->execute();
}
catch (MyRequest_Exception $e)
{
// do what you want
}
echo Request->instance()->send_headers->response();
// somewhere in before()
if ($error)
{
throw new MyRequest_Exception($errortext);
}
Change action name:
$this->request->action('oblivion'); // redirects to an "oblivion" action that does nothing

You can set a class variable in before() say:
$this->execute = false;
Then in your action:
public function action_example()
{
if (!$this->execute) return;
// etc
}

Related

Laravel - return a redirectResponse selectively generated in a function

Part of my application is a multi-stage checkout process; during the latter pages of this I first run a sanity check on each request to verify the user actually has some items in their basket: if not they're sent back to the beginning.
I have a controller function like this which is called from multiple routes for DRY purposes.
private function checkBasketFull($request)
{
if (self::isBasketEmpty($request)) {
return redirect('/')->with('status', config('app.empty_basket_message'));
}
}
When I call it, I can't just do:
self::checkBasketFull($request);
because without a return the redirect doesn't fire, only the session data is sent.
And I can't do:
return self::checkBasketFull($request);
because that will give an error if there's no redirect or abort the method if checkBasketFull returns anything else.
My current (working) code is:
$check = self::checkBasketFull($request);
if ($check) {
return $check;
}
Is there an alternative way of writing this on a single line, or modifying the checkBasketFull function, so the redirect will occur if the basket is empty but execution will continue as normal if it isn't?
Either use this:
if ($redirect = self::checkBasketFull($request)) return $redirect;
Or throw an error and catch it in the global error handler.
However, instead of returning and checking that for a redirect like that, I'd much rather keep it as two completely separate methods:
public function someRoute(Request $request)
{
if ($this->isBasketEmpty($request)) return $this->redirectBasketEmpty();
// Continue processing this request...
}
protected function isBasketEmpty(request)
{
// run your login here...
}
protected function redirectBasketEmpty()
{
return redirect('/')->with('status', config('app.empty_basket_message'));
}
Feels cleaner to me.

Laravel redirect from private method with errors

I have the following code:
public function store(Request $request)
{
$this->validateData($request->all());
// store something
return redirect()->action('controller#index')->withMessage( 'Saved Successfully' );
}
private function validateData($requestParams)
{
try
{
$validator->validate( $requestParams );
}
catch ( ValidationException $e )
{
redirect()->action('controller#create')->withInput()->withErrors( $e->get_errors() )->send();
exit(); // this causes the withErrors to not be there
}
}
If I remove the exit();, the error messages will appear, but also the store function will be executed (see // store something). I know I can rewrite my code like:
if($this->validateData($request->all()))
{
// store something
return redirect()->action('controller#index')->withMessage( 'Saved Successfully' );
}
But I don't want the ugly if statement here. There must be a way to redirect with the flash messages without it.
tl;dr
Update your private method code like this to make the redirection work with $errors variable visible:
private function validateData($requestParams)
{
try
{
$validator->validate( $requestParams );
}
catch ( ValidationException $e )
{
$resp = redirect()->action('WelcomeController#index')->withInput()->withErrors($e->get_errors());
\Session::driver()->save();
$resp->send();
exit();
}
}
explaination
When exiting in the middle of your controller, there are some job which is executed in the application termination will not be execute anymore. In your case, the Session middleware terminate method will not be called. Let see its content (ref):
public function terminate($request, $response)
{
if ($this->sessionHandled && $this->sessionConfigured() && ! $this->usingCookieSessions())
{
$this->manager->driver()->save();
}
}
Now, look at the save method of our Session driver (ref)
public function save()
{
$this->addBagDataToSession();
$this->ageFlashData();
$this->handler->write($this->getId(), $this->prepareForStorage(serialize($this->attributes)));
$this->started = false;
}
As you can see, your flash data is only be saved when the Session middleware terminates successfully. With your old code, the flash data will be lost!
What I do with my code is calling the save method manually before sending our response to the browser. However, I still recommend you bring the redirection to the public controller method.
Well I don't see any problem using the if statement there. Basically you do not stop the code execution, so thats why the store function is executed, even if your validation fails. The redirect function just sends a header with the redirect location, it does not abort the code after it to be executed. It works with exit(), because it sends the redirect headers and stop the rest of the code to be exeuted.
This is not ugly, it is clean and clear and I suggest you to use this. This is a good example of right if statement usage - if one conditions i met then do this. In your case if your validation passes, just store the object. (Just remember to modify your validate function to return true or false)
if($this->validateData($request->all()))
{
// store something
return redirect()->action('controller#index')->withMessage( 'Saved Successfully' );
}
The other possible solution is to use try .. catch block like this
public function store(Request $request)
{
try {
$this->validateData($request->all());
// store something
return redirect()->action('controller#index')->withMessage( 'Saved Successfully' );
} catch ( ValidationException $e ) {
return redirect()->action('controller#create')->withInput()->withErrors( $e->get_errors() );
}
}
private function validateData($requestParams)
{
// Your validation logic here
$validator->validate( $requestParams );
}
you just forgot to 'return' after the validation exception :D, then you would not have to 'exit;'

_redirect still continues php code execution

I'm working on custom module and in my IndexController.php I'd written this function to add user to database
public function addAction() {
if($this->getRequest()->getParam('name', '') == ''){
$this->_redirect('etech/user');
//die; or exit;
}
$form = $this->getRequest()->getParams();
$user = Mage::getModel('test/test');
foreach ($form as $key => $val){
$user->setData($key, $val);
}
try{
$user->save();
}catch(Exception $e){
print_r($e);
}
$this->_redirect('etech/user', array('msg'=>'success'));
}
I want to prevent users from accessing this url directly as www.example.com/index.php/etech/user/add/. For this I'd made a check if($this->getRequest()->getParam('name', '') == ''){}. The redirect is working well except the code in there keeps executing and user sees a success message which should not be seen. For this, I'd used old fashioned exit or die to stop executing the code then it doesn't even redirect.
What is the magento way to handle it? Also, as I'm using getRequest()->getParams(), it return both parameters either in get or post. Isn't any way out to get only post parametrs?
It is correct to use $this->_redirect(), but you must follow it up with a return, ideally return $this;. You could also use exit or die, as you have been doing, but as I'm sure you know it would be better to let Magento do whatever it wants to do before redirecting you.
As long as you return immediately after $this->_redirect(), you won't have any issues.
Edit: And as for the request params question, I think you can call something like $this->getRequest()->getPostData() (that was false). The general convention is to use getParams() regardless of whether the data was sent via GET or POST, because technically your code shouldn't be concerned about that.
Edit #2:
If the general convention doesn't apply and you desperately need to restrict access to your page based on POST vs. GET, here's a handy snippet from Mohammad:
public function addAction()
{
if ($this->getRequest()->isPost()) {
// echo 'post'; do your stuff
} else {
// echo 'get'; redirect
}
}

Zend _forward doesn't work after failed authentication (Zend Framework 1)

After a user fails authorisation I'd like to forward them back to the login page. Currently the _forward method causes Zend to hang and give a time out error (30 seconds exceeded).
The code for the login page handles both a login and signup form, and forwards to the authorisation controller:
public function indexAction() {
if ($this->_request->isPost()) {
$formData = $this->_request->getPost();
if (array_key_exists('signUp', $formData)) {
$authAction = 'signup';
$form = 'signupForm';
} elseif (array_key_exists('logIn', $formData)) {
$authAction = 'login';
$form = 'loginForm';
}
if ($this->$form->isValid($formData)) {
$this->_forward($authAction, 'user-auth', null, $formData);
} else {
$this->$form->populate($formData);
}
}
}
This works fine and redirects to the auth controller successfully. The code inside the login action of the auth controller is as such:
public function loginAction() {
$formData = $this->_request->getPost();
$authAdapter = new My_Auth_Adapter();
$authAdapter->setTableName('user')
->setIdentity($formData['username'])
->setCredential($formData['password'])
->setIdentityColumn('username')
->setCredentialColumn('password');
$result = $authAdapter->authenticate();
if ($result->isValid()) {
// success, all good
} else {
$this->_forward('index', 'login', 'default', $formData);
}
}
We arrive here fine, and a successful authorisation works as expected. However in my else statement placing another forward back to the original login controller (I wish to populate the username as well as post back an error message) causes the program to hang, although a redirect works fine.
I thought it may be because the login controller is re-detecting the post data and I'm getting caught in an infinite loop, but removing the $formData as the last argument of the forward doesn't change anything.
I've also tried $formData['errMsg'] = 'whatever' above the forward and then checking if the key exists or if it is set in the login controller, but that doesn't change a thing either.
Interestingly, the time out error I receive references the Auth DbTable Adapter:
Fatal error: Maximum execution time of 30 seconds exceeded in /Applications/MAMP/MampServer/mysite/library/Zend/Auth/Adapter/DbTable.php on line 174
Any ideas as to what may be happening?
I think you are infinity looping between loginAction() and indexAction().
Check out the difference between the calls to forward() and redirect() action helpers. The former, forward() internally will change the $request->isDispatched() == false - This means that the front controller will execute the targeted controller action without a new HTTP request.
The outcome of this is that $this->_request->isPost() will always be true and therefore $this->$form->isValid($formData) again will also be true, meaning your going around in circles.
I know the below is a very different to your approach, however I believe it is a more conventional separation of concerns for Zend 1 controllers.
// ... SomeController.php
public function getLoginForm();
public function getSignupForm();
protected function authenticate($username, $password)
{
$authAdapter = new My_Auth_Adapter();
$authAdapter->setTableName('user')
->setIdentity($username)
->setCredential($password)
->setIdentityColumn('username')
->setCredentialColumn('password');
$result = $authAdapter->authenticate();
return ($result->isValid()) ? true : false;
}
public function indexAction()
{
$form = $this->getLoginForm();
$request = $this->getRequest();
if ($request->isPost()) {
if ($form->isValid($request->getPost())) {
if ($this->authenticate($form->getValue('username'), $form->getValue('username'))) {
$this->redirect('/members'); // Successfully logged in
}
}
}
$this->view->form = $form;
}
public function signupAction()
{
// stuff only for signups!
}
Edit To elaborate: forward() is a controller action helper. Its job is simply to modify the Zend_Controller_Request_Http instance. The Zend_Controller_Request_Http class is the one returned when you call $this->getRequest() within a controller.
The Request instance encapsulates all access to $_POST, $_GET and stores then as values within the object. Calls such as $request->setParam('someparam', 123) set or get these values rather than the standard direct access to $_POST['someparam'] or $_GET['someparam'].
The special case is with the values module,controller,action and dispatched. These are the key's used by the Zend_Controller_Front and the Dispatcher when trying to determine the correct controller to instantiate and action method to execute.
A simplified example of how the dispatch loop works:
while(! $request->isDispatched()) {
$request->setDispatched(true);
// If at any point here we change setDispatched(true)
// perhaps in a controller action with a call to forward()
// then the whole dispatch loop will be called again
// perhaps creating a different controller
$controllerName = $request->getControllerName();
$actionName = $request->getActionName();
$controller = new $controllerName();
$controller->$actionName();
}
In the else block:
$this->_redirect($this->url(array('login' => $formData['username'], 'nameOfYourRoute'));
Added a new get variable 'login' to your route and populate your forms login with this variable.

Dealing with URI after $dispatcher->forward() execution

I've set form submission URI to something like '/register' and in controller action $dispatcher->forward() to '/profile'. When submitting form, it forwards to the right page, but URI shown in browser is '/register'. Is there a way to set URI to those defined in $dispatcher->forward() method and not in form action?
You can use the redirect method.
So in your controller you could do:
public function registerAction()
{
$this->view->disable();
if ($this->request->isPost()) {
// Here is where you process the POST and check the user
if ($user) {
// Valid user
$this->flash->success('OK');
return $this->response->redirect('profile');
} else {
$this->flash->error('Oops! Something went wrong.');
}
}
}

Categories