Slim: Is it possible to dispatch a route internally? (Slim 3)

Created on 17 Jul 2015  路  25Comments  路  Source: slimphp/Slim

Can I dispatch a route in my controller and capture the response?

Most helpful comment

The ""problem"" is here: https://github.com/slimphp/Slim/blob/3.x/Slim/App.php#L494-L496
If you don't provide a response object to subRequest, the function will use the response from container(not a good thing, we know, see #1780 for more).
So, if you don't want that 'body overriding' you must provide a new response object like:

$res = $app->subRequest('GET', '/hello', '', [], [], '', new \Slim\Http\Response());

Hope this help you :)

All 25 comments

Yes you can do this using subRequest.

$app->get('/', function ($request, $response, $args) {
    $res = $this->subRequest('GET', '/hello');
    return $res;
});

$app->get('/hello', function ($request, $response, $args) {
    $response->write("Hello, World");
    return $response;
});

For some reason, when I do subReques I get empty response body.

class ListAction extends BaseAction
{
    public function dispatch(Request $request, Response $response, $args)
    {
        if (empty($args['filter'])) {
            $args['filter'] = 'running';
        }
        /** @var Docker $docker */
        $docker = $this->getService('docker');
        $query = [];
        if ($args['filter'] == 'all') {
            $query['all'] = 1;
        }
        $containers = $docker->getContainerManager()->findAll($query);
        foreach ($containers as &$container) {
            $container = $container->getData();
        }

        return $response->withJson($containers);
    }
}

If I hit this route view browser, I get the correct json, but if I do

$response = $this->app->subRequest($method, $uri);
var_dump($response->getBody()->getContents()

I get
""

Are you calling that inside of a route, your code doesn't show context as to where you are calling this.

I tested the code below and it works:

$app->get('/', function ($request, $response, $args) {
    $res = $this->subRequest('GET', '/hello');
    return $res;
});

$app->get('/hello', function ($request, $response, $args) {
    $response->withJson(['hello' => 'World']);
    return $response;
});

In order to var_dump the value you need to cast getBody to a string.

var_dump((string) $res->getBody());

I am not using closure for the routes. I have Action classes for each route which app is injected in.
So my routes are
$app->get('/', 'App\Action\HomeAction:dispatch')->setName('homepage');
and
$app->get('/list[/[{filter:all|running}]]', ListAction::class . ':dispatch');

And in HomeAction I call

$response = $this->app->subRequest('GET','/list');
var_dump($response->getBody()->getContents()

And I get the empty string

Ahhh!
var_dump((string) $res->getBody()); did the trick :) I thought ->getContents() would return a string

Yeah it seems stream_get_contents behave differently depending on which version of PHP it is used in, if no byte length and offset is set.

Might need to change the code in getContents to take this into consideration.

Thanks for your time!

Thank you @silentworks for the solution, But when i use subRequest i get 404 page claiming that the page doesn't exist, by the way i'm using slim 3.0 .

@ismnoiet
This code should work:

$app = new \Slim\App();

$app->get('/', function ($request, $response, $args) use ($app) {
    $res = $app->subRequest('GET', '/hello');
    return $res;
});

$app->get('/hello', function ($request, $response, $args) {
    $response->write("Hello, World");
    return $response;
});

$app->run();

$this on route closure is now the container(was App on beta) and subRequest is on App.

Thank you @mathmarques changing $this to $app worked for me :).

What if i just want to grab the result returned from an internal route call(using subRequest) and use it inside the current route WITHOUT having the the internal route response added to the current route response object.

Example:

$app = new \Slim\App();

$app->get('/hello', function ($request, $response, $args) {
    $response->write("Hello, World");
    return $response;
});

$app->get('/', function ($request, $response, $args) use ($app) {
   $res = $app->subRequest('GET', '/hello');
   //here use  $res   ...

   // i don't want $res to be added to the current $response object  
   return $response;
});
$app->run();

@ismnoiet that should already be the case, Response objects are immutable so the data shouldn't be passed over to $response unless you explicitly tell it to do so.

@silentworks i'm not getting this human expected behavior i don't know why!
Instead it is prepended to the current route response object to be specific.

I have the same issue, adding $response = new $reponse; in the master route does the work

Thanks @acidvertigo for your answer and really it did work!,
but would you like to give us a reason why overriding the $response with new $response is needed
here?.

The ""problem"" is here: https://github.com/slimphp/Slim/blob/3.x/Slim/App.php#L494-L496
If you don't provide a response object to subRequest, the function will use the response from container(not a good thing, we know, see #1780 for more).
So, if you don't want that 'body overriding' you must provide a new response object like:

$res = $app->subRequest('GET', '/hello', '', [], [], '', new \Slim\Http\Response());

Hope this help you :)

@mathmarques :+1:

FYI; getContents() does not reset the pointer in the stream and only returns the remaining content. When casting to string it does reset the pointer so the whole body content is returned.

Oh nvm, this seems to be an old issue ...

Hello everyone, i'm try this

$this->post('/advertiser/', function ($request, $response) use ($app) {

                $dataset        = $request->getParsedBody();

                $result         = $this->subRequest('POST', '/programmatic/advertisers/create/', $dataset, [], [], '', $response);
                $result         = json_decode($result->getBody(), TRUE);

                return $result;

})->setName('programmatic-create-advertiser');

but return

Call to undefined method Slim\Container::subRequest()

$app->subRequest

however subRequest will be deprecated in slim 4.

@geggleto how do I go about implementing a subRequest when slim4 is out or interestingly any thoughts. Currently developing with slim3 and my need for subRequest is to prevent n+1 request. eg. GET /me?include=likes,hobies,friends of which i would like to avoid doing GET /me/likes .... and others. Actually using subRequest makes its slow has it would be faster to just implement an internal handler.

@sojimaxi perhaps you can solve it via middleware?

Is $app->subRequest available in Slim 2?

I keep getting Call to undefined method SlimSlim::subRequest()

Was this page helpful?
0 / 5 - 0 ratings

Related issues

akrabat picture akrabat  路  22Comments

l0gicgate picture l0gicgate  路  27Comments

Bilge picture Bilge  路  28Comments

odahcam picture odahcam  路  18Comments

konskript picture konskript  路  62Comments