-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: [Routing] add option to pass multiple URI segments to one Controller parameter #8348
Conversation
cb6d227
to
59937ca
Compare
For what it's worth, |
No, there is no bug. For example, define the following: $routes->get('products/([a-z]+)/(.+)', 'Products::show/$1/id_$2'); And navigate to the URI
But it is intuitive that |
Ok, so this PR will give us the ability to change the way we pass the parameters to the controller? Now, if I have a route like this: $routes->get('product/(:any)', 'Catalog::product/$1'); Then my controller may look like this: class Catalog extends BaseController
{
public function product($seg1 = false, $seg2 = false, $seg3 = false)
{
d($seg1, $seg2, $seg3);
}
} Then calling the URLs will return:
With this change I will have option to build my controller like this: class Catalog extends BaseController
{
public function product($segments)
{
d($segments);
}
} Then calling the URLs will return:
If that's correct then I'm not sure if we need this feature. I believe using _remap should handle such a scenario already. // Edit |
With this option, the
|
I see, thanks. Still, this can be handled with |
This changes the number of route parameters. |
I'm not quite sure what you're referring to. To the changes in this PR or to I'm referring to the use of In |
Without using $routes->get('product/(:any)', 'Catalog::product/$1'); When I first saw the above route, I interpreted the $routes->get('product/(:segment)/(:any)', 'Catalog::product/$1/$2'); I thought this means the first parameter is the value of Am I the only one who thinks this interpretation is intuitive? |
LOL... you're right! I missed this one 😅
Ok, with this example, I get your point a little better now. Although it's nothing that can't be handled right now. We can simply use: public function product(...$segments)
{
d(array_shift($segments));
dd(implode(',', $segments));
} Or even move it to the The question is how common this problem is. I agree that this PR would simplify the handling of such cases, but is it worth complicating the router code? // Edit |
I think this option's behavior is easier to understand than the current behavior. For example, $routes->get('img/(:img)', 'Image::display/$1'); If Also, I don't know if this use case actually exists, but if you want a route that uses two or more placeholders that match multiple segments, it may be impossible to get the placeholder values with $routes->get('foo/(.+)_(.+)', 'Foo::bar/$1/$2'); |
First off, I don't know that I'm a fan of this as a default. I understand why it makes sense, but when you compare it to the others it kind of doesn't. Here's why: Each placeholder defines a single string that it matches and passes the controller. Changing the behavior means that now this one placeholder has a different function than the rest. It was never intended to return each segment, it was to return from the beginning of the segment to the end of the URI's path. Which is what it does. This is useful if you know you need to capture other URIs in a segment, or whatever special codes you use that might include a forward slash. So, this isn't a bug. It operates as designed. This is not something worth being a BC break in the future for, I don't think. To me this is something that has affected very few people (this is the first I've heard someone bring it up personally). And it's something that they can easily change in the _remap method as discussed, or in the designated method with a simple I think we need to be more discerning in what we add or change, based in large part on how much its causing a problem and how much it impacts current devs. |
I understand that enabling this option will certainly change the behavior and that we should be cautious about changing the default settings. However, the following explanation is not clear to me what you are trying to say.
Placeholders are regular expression patterns. So the string it captures will be not changed when this option is enabled. The idea is to change the behavior that the captured value is currently passed to the controller as multiple parameters to always pass it as a single parameter. To be sure, I put a sample of the differences in behavior. $routes->get('img/(:any)', 'Image::display/$1'); <?php
namespace App\Controllers;
use App\Controllers\BaseController;
use CodeIgniter\HTTP\ResponseInterface;
class Image extends BaseController
{
public function display(...$params)
{
dd($params);
}
} Navigate to The current behavior:
The option is enabled:
|
Oh, wait, I think I've been looking at this backward the whole time for some reason. I thought the current implementation was to pass it as a single string and it was being changed to pass it in as an array. In that case I approve because I'm pretty sure that's how it functioned when I wrote it years ago and not sure when it got changed to split it up. At least that's what I recall my intent being. :) So thank you for the detailed examples. |
The current implementation passes the captured value to the controller after splitting it with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, this will be a nice feature for more complex routes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One small request.
Also, are there any security implications to this? Trying to think of places that a slash delimitation could be used to inject route code... not coming up with anything though.
$this->assertSame('\Catalog', $router->controllerName()); | ||
$this->assertSame('productLookup', $router->methodName()); | ||
$this->assertSame(['123/456'], $router->params()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you please add a test showing that queries are not included?
product/123/456?id=789
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MGatner Do you mean $router->handle('product/123/456?id=789');
?
If so, it is not needed, because the parameter is $uri
. It never contains queries.
In other words, if product/123/456?id=789
is passed to handle()
, the client code is wrong.
CodeIgniter4/system/CodeIgniter.php
Lines 826 to 830 in b28af1d
$uri = $this->request->getPath(); | |
$this->outputBufferingStart(); | |
$this->controller = $this->router->handle($uri); |
59937ca
to
792df9e
Compare
792df9e
to
43227c3
Compare
vendor/bin/phpstan analyze --generate-baseline phpstan-baseline.php
Can I merge this? |
Let's wait until the weekend. If @MGatner does not raise further questions I think we can consider that doubts are clarified. |
Description
See https://forum.codeigniter.com/showthread.php?tid=88954&pid=414392#pid414392
The current implementation passes the captured value to the controller after splitting it with
/
.So if
path/to/image-file.png
is captured, the controller will receive three parameters.This PR adds an option to pass it to one parameter.
E.g.,
Navigate to
http://localhost:8080/img/path/to/image-file.png
.The current behavior:
The option is enabled:
Checklist: