-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
[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');
}; |
javiereguiluz
left a comment
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?
| $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. |
robfrawley
left a comment
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. |
… format for semantic configuration (nicolas-grekas) This PR was merged into the 7.4 branch. Discussion ---------- [Config][DependencyInjection] Deprecate the fluent PHP format for semantic configuration | Q | A | ------------- | --- | Branch? | 7.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | - | License | MIT This PR deprecates the fluent PHP format for semantic configuration introduced in Symfony 5.4 by `@Nyholm` (see #40600). It aims to replace it with the new array-based PHP config format (see #61490). The fluent PHP config format was a great experiment: - It helped us improve the Config component and the code generation of fluent config builders. - It confirmed the community’s interest in PHP-based configuration. - And it showed us its limits. Those limits are structural. Writing fluent config is difficult and full of edge cases. Its rigidity comes from having to match one canonical interpretation of the semantic config tree. Automatic code generation can’t capture the custom logic that before-normalizers introduce, yet those normalizers are essential for flexibility and backward compatibility. This rigidity makes fluent config fragile. How do we deal with this fragility as config tree authors? At the moment, we don't care. Maybe this format is too niche for you to have experienced this issue, but we cannot guarantee that simple upgrades won't break your fluent PHP config. The new array-based PHP format builds directly on the same code used for loading YAML configs. That means: - trivial conversion between YAML and PHP arrays, - identical flexibility and behavior, - and support for auto-completion and static analysis through generated array shapes. The generated array shapes are rigid too, but that rigidity is non-breaking: even if your config no longer matches the canonical shape, your app keeps working. Static analyzers might warn you: that’s an invitation to update, not a failure. I'm submitting this PR a bit l late for 7.4 but I think it's important to merge now. Deprecating the fluent PHP config format will: - prevent new code from relying on a fragile approach, - make room in the documentation for the array-based format, - and consolidate Symfony’s configuration story around a robust PHP-based format. Fluent PHP for semantic config served us well but it's time to retire it. ```diff -return function (AcmeConfig $config) { - $config->color('red'); -} +return new AcmeConfig([ + 'color' => 'red', +]); ``` PS: there's another fluent config format for services and routes (see #23834 and #24180). This other format is handwritten. It doesn't have the issues listed above and it is *not* deprecated. It's actually the recommended way *for bundles* to declare their config (instead of XML, see #60568). Commits ------- 332b4ac [Config][DependencyInjection] Deprecate the fluent PHP format for semantic configuration
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()";