Response behaviour from within classes

My controller uses Response so that php return $response->withRedirect($url); redirects fine.
From within my controller class I injected php (Response $response) into the constructor of a new class, then assigned it as php $this->response = $response for use within my Private methods.
I expected that php return $this->response->withRedirect($url); would redirect, as it does from within the controller, but it does not do anything.

Is this normal behaviour?

You wouldn’t need to inject the Response object because its passed to the callable, so if your method inside of your Controller is the callable then it will be passed the Response object.

e.g.

Session.php

class Session
{
    public function __invoke(Request $request, Response $response, array $args)
    {
        return $response->withRedirect('your_url_here');
    }
}

Your apps entry point index.php

$app = new Slim\App();
$app->get('/login', Session::class);
$app->run();

or if you want to have a multiple methods in a class then it could look like

Session.php

class Session
{
    public function login(Request $request, Response $response, array $args)
    {
        // Login logic

        // redirect
        return $response->withRedirect('/');
    }

    public function logout(Request $request, Response $response, array $args)
    {
        // Login logic

        // redirect
        return $response->withRedirect('/login');
    }
}

Your apps entry point index.php

$app = new Slim\App();
$app->get('/login', 'Session:login');
$app->get('/logout', 'Session:logout');
$app->run();

My App is like this:

$app = new Slim\App();
$app->get('/login', 'Session::class');
$app->run();

class Session
{
    public function __invoke(Request $request, Response $response, array $args)
    {
        $oauthHelper = new OauthHelper($response);
        $auth = $oauthHelper->someMethod();
        return $response->withRedirect('my_url_here');
    }
}

class OauthHelper
{
       private $response;

       public function __construct(Response $response)
       {
              $this->response = $response;
       }

       public function someMethod() {
               return $this->response->withRedirect('my_other_url_here');
       }
}

So, it is the
return $this->response->withRedirect('my_other_url_here');
that is not working, while
return $response->withRedirect('my_url_here');
is working fine.

Shouldn’t you return the result of OAuthHelper::someMethod()? As in:

$auth = $oauthHelper->someMethod();
return $auth;

Note that the response object is immutable.

Yes you are right, I was abbreviating. The “Session” class actually is more akin to:

class Session
{
    public function __invoke(Request $request, Response $response, array $args)
    {
        $oauthHelper = new OauthHelper($response);
        $auth = $oauthHelper->someMethod();
        if ( some_test ) {
            return $auth;
        } else {
            return $response->withRedirect('my_url_here');
        }
    }
}

I realise that this is not ideal, and I have changed the design so that the controller class actually does the redirect. My question is more theoretical now. Is it possible for the $response object to be returned back to the controller with a withRedirect() or even a withStatus(), so that the controller then performs the redirect?

I am confused by what you are asking, look at my example. Session is my controller as you would call it, that is where the redirect is happening. Can you show your full code as my example answer what you asked initially.

Here is a test app to illustrate my problem:

<?php

define('INC_ROOT', dirname(__DIR__));
require INC_ROOT . '/vendor/autoload.php';

use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;

$configuration = [
    'settings' => [
        'displayErrorDetails' => true,
    ],
];
$c = new \Slim\Container($configuration);

class CallableClass
{
    public function __invoke(Request $request, Response $response, array $args)
    {
        $second = new SecondClass($request, $response);
        $second->someMethod();
        // return $response->withRedirect('/some_route1');
        echo "Should not display, if redirect had worked";
    }
}

class SecondClass
{
    private $response;
    private $request;

    public function __construct(Request $request, Response $response)
    {
        $this->request = $request;
        $this->response = $response;
    }

    public function someMethod()
    {
        return $this->response->withRedirect('/some_route2');
    }
}

$app = new Slim\App($c);
$app->get('/login', CallableClass::class);
$app->run();

At the moment, the phrase Should not display, if redirect had worked is displayed, although I expected it to redirect to /some_route2. If i uncomment the line return $response->withRedirect('/some_route1'); in the CallableClass, then the redirect does happen, and the text is not displayed.
So, my question is:
Why does return $this->response->withRedirect('/some_route2'); not work in the SecondClass, even though I have injected the $repsonse object into its constructor?

Because you have not returned it in your first class. If you do return $second->someMethod();, then that should work. Not sure why you would need to do the redirect in the next SecondClass rather than in the CallableClass.

1 Like

the withRedirect method does not redirect immediately, it just sets the appropriate response headers to the response. The true redirect only happens at the end of the application run and the response has been sent to the client.

1 Like