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 |
michalsn
left a comment
There was a problem hiding this comment.
Ok, this will be a nice feature for more complex routes.
MGatner
left a comment
There was a problem hiding this comment.
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.
Would you please add a test showing that queries are not included?
product/123/456?id=789
There was a problem hiding this comment.
@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
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.pngis 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: