-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Routing] Add PHP fluent DSL for configuring routes #24180
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
Conversation
0b4198c
to
836348f
Compare
I like this proposal! Thanks for contributing it. My only super tiny concern is about a variable name. I propose to rename // add something to the given route
$route->add(...);
// add a route to the given collection of routes
$routes->add(...); A more real comparison using your fixture: // BEFORE
return function (RoutingConfigurator $route) {
$route
->add('foo', '/foo')
->condition('abc')
->options(array('utf8' => true))
->add('buz', 'zub')
->controller('foo:act');
$route->import('php_dsl_sub.php')
->prefix('/sub')
->requirements(array('id' => '\d+'));
$route->add('ouf', '/ouf')
->schemes(array('https'))
->methods(array('GET'))
->defaults(array('id' => 0));
};
// AFTER
return function (RoutingConfigurator $routes) {
$routes
->add('foo', '/foo')
->condition('abc')
->options(array('utf8' => true))
->add('buz', 'zub')
->controller('foo:act');
$routes->import('php_dsl_sub.php')
->prefix('/sub')
->requirements(array('id' => '\d+'));
$routes->add('ouf', '/ouf')
->schemes(array('https'))
->methods(array('GET'))
->defaults(array('id' => 0));
}; |
836348f
to
a28e2c1
Compare
@javiereguiluz fixtures updated. |
The return function (RoutingConfigurator $routes) {
$news = $routes->collection('news_')
->prefix('news/')
->requirements(['id' => '\d+'])
;
// No need to repeat the name prefix, the requirements are applied here but
// are still part of the same file.
$news('show', '{id}')->controller('News:show');
$news('edit', '{id}/edit')->controller('News:edit');
$news('delete', '{id}/delete')->controller('News:delete');
}; |
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.
I like it! Thanks for this contribution.
} | ||
|
||
/** | ||
* Sets the prefix to add the path of all child 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.
This description looks like it's missing something. What about?
Sets the prefix added to the path of all child routes.
} | ||
|
||
/** | ||
* Sets the prefix to add the path of all child 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.
Same comment here.
* | ||
* @return $this | ||
*/ | ||
final public function schemes(array $schemes) |
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 consider using variadics here and in methods()
method too?
@@ -37,7 +38,21 @@ public function load($file, $type = null) | |||
$path = $this->locator->locate($file); | |||
$this->setCurrentDir(dirname($path)); | |||
|
|||
$collection = self::includeFile($path, $this); | |||
// the closure forbids access the private scope in the included file |
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.
forbids access the private scope
-> forbids accessing the private scope
or forbids access to the private scope
?
a28e2c1
to
f433c9a
Compare
@javiereguiluz thanks, comments addressed.
I wouldn't, for the same "least surprise" reason. If someone wants to push stronger for variadics, I suggest doing so in a dedicated PR, after this one (and the DI one) have been merged. |
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.
This looks great! 👍
class CollectionConfigurator | ||
{ | ||
use Traits\AddTrait; | ||
use Traits\RouteTrait; |
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.
shouldn't ->defaults
, ->requirements
and so on apply on $this->collection
in this class ?
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.
That's the case, but not on the collection yes, because it works reverse: calling on the collection means changing routes declared before. Here, we want first to configure the route prototype ($this->route
in this class), then use this prototype as a template for new routes added.
The goal is to make this work as expected:
$collection->defaults(['foo' => 'bar'])->add(...);
but I'm not sure doing $collection->add(...);
then $collection->defaults(['foo' => 'bar']);
and having it applied to previously declared routes in the collection is something we want to have.
…icolas-grekas) This PR was merged into the 3.4 branch. Discussion ---------- [Routing] Add PHP fluent DSL for configuring routes | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - If we add a PHP DSL for DI config (#23834), we must have a similar one for routing. Here it is. See fixtures. So, you always start with a `RoutingConfigurator $routes`, which allows you to: ```php $routes->add($name, $path); // adds a route $routes->import($resource, $type = null, $ignoreErrors = false); // imports routes $routes->collection($name = ''); // starts a collection, all *names* might be prefixed ``` then - for "import" and "collection", you can `->prefix($path)`; - for "add" and "collection", you can fluently "add" several times; - for "collection" you add sub"`->collection()`"; - and on all, you can configure the route(s) with: ```php ->defaults(array $defaults) ->requirements(array $requirements) ->options(array $options) ->condition($condition) ->host($pattern) ->schemes(array $schemes) ->methods(array $methods) ->controller($controller) ``` Commits ------- f433c9a [Routing] Add PHP fluent DSL for configuring routes
* | ||
* @return $this | ||
*/ | ||
final public function controller($controller) |
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.
I had an idea looking at the examples on the main blog post about this feature (which looks great btw): http://symfony.com/blog/new-in-symfony-php-based-configuration-for-services-and-routes
Is it possible, or does it make sense to change the api of this controller method (or even adding a new helper method), to enable use Controller::class
, without having to concatenate the results?
If I'm not mistaken, all other class methods accept a FQN class name as its parameter, so you can pass a string, or use the ::class
(which I find less error prone since it's IDE auto completed).
This one however, since it's following the config that already exist would end in being something like:
->add('homepage', '/')
->controller('App\Controller\DefaultController::index')
or
->add('homepage', '/')
->controller(DefaultController::class . '::index')
In the later scenario, would changing this method's parameters or adding a new method that would accept the method of the controller separately help, as far as DX goes?
as an example:
->add('homepage', '/')
->controller(DefaultController::class, 'index')
I understand that wouldn't match the full string config for a controller, in relation to other configurations, so that might be a con, and concatenating honestly doesn't look that bad either, just wanted to raise the case, since it's different from all others, in case it makes sense.
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.
@ajessu That's indeed a very good idea 👍 of course this action parameter should be optional as more advanced developers use a single action per controller (called Action controllers).
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.
that's already possible: just use the array-callable PHP syntax:
->controller([DefaultController::class, 'index'])
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.
Thanks @nicolas-grekas. That seems perfect. Wasn't aware that was a possibility here.
$routes
->add('foo', '/foo')
->condition('abc')
->options(array('utf8' => true))
->add('buz', 'zub')
->controller('foo:act'); This is too fluent, code formatters will break the indent-based semantics and the interface looks confusing at the first glance. |
If you don't want to be too fluent, you can just do:
It's up to you. |
Of course, I understand that, just wanted to share my first impressions; it is a welcome change after all. |
If we add a PHP DSL for DI config (#23834), we must have a similar one for routing. Here it is. See fixtures.
So, you always start with a
RoutingConfigurator $routes
, which allows you to:then
->prefix($path)
;->collection()
";