0

I have a pretty straightforward question. I am using Slim 3 to build a RESTfull api.

How come this works:

$app->get('/news[/{params:.*}]', function ($request, $response, $args) {
    $params = explode('/', $request->getAttribute('params'));
    $response->write("news!");
    return $response;

});

But not this:

$app->get('/news[/{params:.*}]/details', function ($request, $response, $args) {
$params = explode('/', $request->getAttribute('params'));
        $response->write("news details");
        return $response;

});

In fact the latter does not compile.

1 Answers1

7

Using unlimited Optional segments, implies that each consequent segment is reserved.

In your defined route for /news[/{params:.*}] the following paths qualify:

/news
/news/foo
/news/foo/bar
/news/foo/bar/... 

So adding an extra fixed segment /details won't work if you add it after the square brackets.

When you define it as /news[/{params:.*}/details] with the /details segment within the square brackets it does work for details, but not in combination with the first route & will break. You can still go with your first route & check the last param, or with an optional param within:

$app->get('/news[/{params:.*}[/details]]', function ($request, $response, $args) {

    $params = explode('/', $request->getAttribute('params'));

    if (end($params) != 'details') {

        $response->write("news!");

    } else {

        // $params for details;
        array_pop($params);

        $response->write("news details");
    }

    // $params is an array of all the optional segments
    var_dump($params);

});

Update:

The actual problem here seems like a colliding definition in the routes, where for example the unlimited optional segments would always match the second defined route. It can be solved by defining the routes with a route regex & enclosing them in a route group prior to non-colliding matches:

$app->group('/news', function () {

    $this->map(['GET'], '', function ($request, $response, $args) {

        $response->write("news w/o params");

    })->setName('news');

    // Unlimited optional parameters not ending with "/details"
    $this->get('/{params:[[:alnum:]\/]*[^\/details]}', function ($request, $response, $args) {
        $params = explode('/', $request->getAttribute('params'));

        var_dump($params);

        $response->write("news!");
    });

    // Unlimited optional parameters ending with "/details"
    $this->get('/{params:[[:alnum:]\/]*\/details}', function ($request, $response, $args) {
        $params = explode('/', $request->getAttribute('params'));
        array_pop($params); // fix $params

        var_dump($params);

        $response->write("news details");
    });

});
baikho
  • 5,203
  • 4
  • 40
  • 47
  • 1
    I need something to differentiate between /news[/{params:.*}]/details and /news[/{params:.*}]. I can always write it as /news/details[/{params:.*}] but it wont be self explanatory anymore... –  Sep 13 '16 at 17:55
  • I see, I have updated the answer but actually your first route does comply for both routes since a last segment as "/details" does match your first route as well. – baikho Sep 13 '16 at 23:35
  • Did you try this? –  Sep 14 '16 at 05:16
  • I just updated the answer again. It may be closer to what you wish to achieve. – baikho Sep 14 '16 at 17:31
  • I will try a verson of this but I'll give it to you. It is not exactly want I want but it is a good hint. –  Sep 14 '16 at 18:44