-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Using a service as a router resource #15742
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
*/ | ||
public function supports($resource, $type = null) | ||
{ | ||
return $type == 'service'; |
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.
Should be return 'service' === $type
.
* | ||
* @return RouteCollection | ||
*/ | ||
public function getRouteCollection(Loader $loader); |
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.
please don't typehint an implementation
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.
My problem is that the Loader
has import
on it, which is really what the user will want. The LoaderInterface
doesn't have this. What do you think about creating a new interface for loaders with import
to avoid type-hinting the implementation?
@stof I decided to type-hint against the existing I've taken all the feedback into account. This is still ready for review :) |
21cc518
to
6fb9d91
Compare
* | ||
* @return RouteCollection | ||
*/ | ||
public function getRouteCollection(LoaderInterface $loader); |
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've type-hinted the interface, but in reality, I'm passing in the concrete ServiceRouteLoader
itself. The difference is that ServiceRouteLoader
(like all loaders), has a really handy import()
method, while LoaderInterface
does not. I'd prefer to type-hint something with the import()
method - either the concrete Loader
or a new interface.
Ping @symfony/deciders |
Great! Would allow us to remove some magic regarding the router in API platform as well. |
interesting. would also allow for simple integration of dynamic resources. /cc @dbu |
👍 for this feature! |
👍 really great |
The test failure is because FrameworkBundle now depends on a new class inside Apart from fixing or finding out about the test, this is ready for feedback or voting. |
/** | ||
* A route loader that executes a service to load the routes. | ||
* | ||
* The depends on the DependencyInjection component |
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.
typo: "The depends on"
indeed, good idea! |
private function addClassResource(\ReflectionClass $class, RouteCollection $collection) | ||
{ | ||
do { | ||
$collection->addResource(new FileResource($class->getFileName())); |
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.
you should check for $class->getFileName()
not being false
(or null
for HHVM) to handle the case of classes extending a class defined in a PHP extension IMO.
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 part was stolen from ContainerBuilder
: https://github.com/symfony/symfony/blob/2.8/src/Symfony/Component/DependencyInjection/ContainerBuilder.php#L275. Is your comment still true? If so - that would be a problem to fix in both places.
@weaverryan The failure for the low tests looks strange. Does it make a difference when you mock the |
What I do not understand is the reason to have a container aware routing loader. Wouldn't it be easier if this loader were more like a registry where you could register |
@xabbuh I understand your thought, but the purpose of this is different. I'm not aiming to make it possible to have RouteLoaderInterface services automatically called because they're registered+tagged. I want to keep loading routes explicit, just add one more way to load routes - via PHP in a real method+service (currently, you can only add routes via PHP in a flat-file). |
@xabbuh yes - you were right about one of the tests - I've fixed that now. I believe the last failing test should be ignored: the functional tests in FrameworkBundle are not able to fetch the Thanks! |
@weaverryan I just mean that it would only be possible to use these services in your routing configuration if they have been registered that way before. The workflow would still be the same then with your approach. The only difference is that you would not try to locate the service in the container (the drawback might be that you then won't be able to lazy load the routing loader services though). |
@xabbuh Ah, I understand better now. I'd still prefer to keep it this way - it feels logical to me that I should only need to register a class as a service and point to it in the config - the tagging seems unnecessary. |
608f304
to
764722d
Compare
👍 (being able to load services lazily and not having to tag your services are good reasons for keeping the container awareness) |
c9e31aa
to
7ed22ce
Compare
Adding a comment that got covered up: We could remove the The difference would then be that we'd probably need to include the method name in the "resource" when using this: admin_routes:
resource: 'admin_route_loader::loadRoutes'
type: service That would solve the need for the RouteLoaderInterface on the "Micro" kernel. It's also consistent with how event listeners, for example, work. |
@weaverryan Indeed, that would be much better. IIRC, when using a service name, the convention is to use only one colon: |
#15990 has been updated to work with the new |
@fabpot I've already done work locally on this PR for the |
cd40cad
to
6b0a5c6
Compare
@fabpot a little later, but I just updated the PR: This uses the ping also @symfony/deciders |
👍 , can you update the CHANGELOG. |
done! |
$loaderObject = $this->getServiceObject($serviceString); | ||
|
||
if (!is_object($loaderObject)) { | ||
throw new \LogicException(sprintf('%s:getServiceObject() must return an object: %s returned', get_class($this), $loaderObject)); |
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.
Here, $loaderObject
can be anything; I would return the type instead.
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.
very good catch - that's what I had meant to do. Fixed now!
I made a small comment; 👍 |
comment addressed! |
I think this one is ready now, ping @symfony/deciders |
This PR was squashed before being merged into the 2.8 branch (closes #15778). Discussion ---------- Fluid interface for building routes in PHP | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | not yet... This - along with #15742 - attempts to making adding routes in PHP (via an actual class+method) not only possible, but also useful. The two classes - `Route` and `RouteCollectionBuilder` are based off of Silex's `Controller` and `ControllerCollection`. The `RouteCollectionBuilder` is basically a `RouteCollection` that's able to import other resources. Here are the goals: A) Import routes easily ```php $routes->import('routing.yml'); ``` B) Fluid addition of routes into the collection ```php $routes->add('/admin', 'AppBundle:Admin:index', 'admin_index') ->setMethods(['GET']); ``` C) Ability to create routes with auto-generating names D) Ability to add a "sub-collection" (kind of like an import, without pointing to another file). Included is the ability to set the controller class: ```php $blogRoutes = $routes->createBuilder('/blog') ->setControllerClass('AppBundle\Controller\BlogController'); $blogRoutes->add('/', 'indexAction'); $blogRoutes->add('/{id}', 'editAction'); $routes->addBuilder($blogRoutes); ``` E) The collection options can be set before or after the routes. With `RouteCollection`, if you set something - e.g. a prefix or a default - and THEN add more routes, those options are not passed to those routes. This is by design, but not ideal for building routes (e.g. in the previous code example, the controllerClass would not be applied using the opposite logic, since it's set before adding the routes). Thanks! Commits ------- 15ba2e8 Fluid interface for building routes in PHP
Thank you @weaverryan. |
Hi guys!
This adds the ability to use a service as a routing resource. In other words, instead of loading
routing.yml
, you could loadmy_route_loader
, and then a method would be called on your service to return a RouteCollection.Specifically, I'm interested in this because it would allow a user to point their main router resource to the kernel itself, making it possible to load routes inside the kernel (making a single-file full-stack app more possible).
Thanks!