Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flexible dynamic routing with Silex

Is it possible to have an unknown number of arguments for a get request?

Example, this works but isn't ideal.

$app->get('/print/{template}/{arg1}/{arg2}', function ($template, $arg1, $arg2) use ($app)     {
  $str = $template . " " . $arg1 . " " . $arg2;
  return $str;
})
->value('template', FALSE)
->value('arg1', FALSE)
->value('arg2', FALSE);

What I'd like is an array of arguments returned for anything entered after the template variable. I can't see more than 4 or 5 arguments being required, but i'd like this to be as flexible as possible from the start.

Ideally i'd like to do something like this,

$app->get('/pdf/{template}/{args[]}', function ($template, $args) use ($app) {
  $str = $template;;
  foreach($args as $arg)
    $str .= " " . $arg;

  return $str;
});
like image 455
shapeshifter Avatar asked Nov 28 '22 17:11

shapeshifter


2 Answers

If you really want to, you can easily work around this limitation by relaxing the requirement on the variable. You can do that by using assert:

$app->get('/pdf/{template}/{args}', function ($template, $args) {
    ...
})
->assert('args', '.*')
->convert('args', function ($args) {
    return explode('/', $args);
});

By making the $args regex more permissive, it will match the rest of the string, even if it includes slashes. The param converter then splits that matched string into segments.

In general I agree with @Sgoettschkes' suggestion to use query string arguments for this. If you need highly dynamic routing with flexible segments, you're doing something wrong in most cases. And query string is usually the better fit for those params.

like image 162
igorw Avatar answered Dec 06 '22 05:12

igorw


The routing component does not support this out of the box as far as I know.

I looked into the code a bit and came to the conclusion that adding such a route would be difficult. The way the routing works is that each route gets registered before the matching is done, so the route has to exist. This means there can not be a "wildcard route".

I don't know if you took this into account, but you can always pass as much information as you like through "real" get parameters:

/print/template?optional1=arg&optional2=arg

This would solve your problem and would work without any modification.

Another way you could handle this is by registering a before event, looking at the request yourself and modifying it. You could for example split the whole url by /, see if the pattern matches your expected format and then put all the optional arguments into one argument with a special character spacer in between. I would not suggest this, but it is possible.

Another way of handling this is by registering your own ControllerCollection, getting the actual request and registering a route which matches the actual request in case there are optional arguments. This would be a bit cleaner I guess.

One way of solving this could be the front-end. If your request always puts additional parameters into the last parameter with a special character in between, you could read those last parameter, split it and work with that:

/print/template/arg:arg

Your could would be something like:

$app->get('/print/{template}/{args}', function ($template, $args) use ($app)     {
  $args = explode(':', $args);
  $str = $template . " " . $args[0] . " " . $args[1];
  return $str;
})
->value('template', FALSE)
->value('args', FALSE);

I would go for idea 1 or 4, mainly because 2 and 3 will be very confusing over time and appear to be not very clean.

like image 29
Sgoettschkes Avatar answered Dec 06 '22 05:12

Sgoettschkes