-
Notifications
You must be signed in to change notification settings - Fork 276
[WIP] Replace Param converter with configurable argument resolvers #436
Conversation
Some usage examples: /**
* @Route("/posts/{slug}", name="blog_post")
* @Method("GET")
* @Arg("post", expr="repository.findOneBySlug(slug)")
* This @Arg is technically not needed, but here as a usage example
*/
public function postShowAction(Post $post)
{
} /**
* @param Post[] $posts
*
* @Route("/", defaults={"page": 1}, name="blog_index")
* @Route("/page/{page}", requirements={"page": "[1-9]\d*"}, name="blog_index_paginated")
* @Method("GET")
* @Cache(smaxage="10")
* @Arg("posts", expr="repository.findLatest(page)")
*/
public function indexAction($posts, $page)
{
} /**
* @Route("/{start}/{stop}", defaults={"stop": "2009-09-09"})
* @Template()
*
* @Arg("start", format="Y-m-d:H:i:s")
*/
public function indexAction(\DateTime $start = null, \DateTime $stop)
{
return ['start' => $start, 'stop' => $stop];
} |
'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\ParamConverterManager', | ||
)); | ||
if ($config['request']['resolvers']) { | ||
$annotationsToLoad[] = 'resolvers.xml'; |
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.
What if you want to use the @Arg
feature without using the default set of resolvers? If I'm not using doctrine nor the datetime resolver in a project, I don't really need them but would like to expand on the feature.
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.
Sure, performance/configurability/lazyness is something that needs to be done. This is an early PoC.
*/ | ||
class Arg | ||
{ | ||
private $arg; |
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 one is meant to be stand-alone and not a ConfigurationInterface
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.
Yes, it is handled in a total different and more flexible way (options are validated via OptionsResolver). I think ConfigurationInterface
should die :)
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.
It's still a pretty cool extension point and used by some packages. Anyway, this is probably a challenge for later on.
https://github.com/FriendsOfSymfony/FOSHttpCacheBundle/blob/master/Configuration/InvalidatePath.php
https://github.com/jaytaph/RateLimitBundle/blob/master/Annotation/RateLimit.php
https://github.com/mayeco/EasyAnnotationBundle
Google in:github.com "extends ConfigurationAnnotation"
|
||
if (!is_array($controller)) { | ||
return; | ||
} |
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.
In theory normal functions can be controllers too (afaik anonymous doesn't support docblocks but not sure), this would make it a string, I think the same goes for static declared methods in a string 'Foo::barAction'.
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.
Anonymous functions do support docblocks, but unfortunately, this is a known limitation of Doctrine Annotations: doctrine/annotations#83
throw new LogicException(sprintf('Wrong @Arg configuration for argument "%s" on "%s".', $name, $context), 0, $e); | ||
} | ||
|
||
$event->getRequest()->attributes->set($parameter->getName().'_options', $options); |
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.
Ah nice leveraging the use of the old setup here, that would prevent them from being executed unless they set a higher priority on their own converter. This should probably be documented as pitfall if they decide to use a higher priority and still try to use @Arg
.
@@ -69,26 +111,26 @@ public function apply(Request $request, ParamConverter $configuration) | |||
// find by identifier? | |||
} elseif (false === $object = $this->find($class, $request, $options, $name)) { | |||
// find by criteria | |||
// FIXME: to be done elsewhere for expr for instance | |||
$method = $method->isArray() ? 'findBy' : 'findOneBy'; |
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.
$method->isArray()
should probably be $metadata->isArray()
I think this case should only occur when guessing, otherwise it's simply a miss-configuration. If it's an array requested, it should probably never even hit findOne
cases and thus never have an identifier in the URL. That means it can only be findBy
and deserves its own case in this construction
This solution looks a lot more elegant than |
@iltar Having this part of Symfony is indeed a possibility that I've thought of. Creating a new HttpKernel |
Not just Drupal or Silex, pretty much every framework could use this ^^ |
How we can understand what repository to use when loading array of posts? By parsing param phpdoc? |
This is a huge BC break as long as the SecurityListener is not refactored to run after argument resolvers, as it would mean that the expression does not receive the converted arguments anymore. |
$method = new \ReflectionMethod($className, $controller[1]); | ||
$annotations = $this->reader->getMethodAnnotations($method); | ||
$context = $className.'::'.$controller[1].'()'; | ||
$argumentMetadata = $this->metadataFactory->createArgumentMetadata($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 was just thinking this might need some internal caching so that it's only generated once. Arguments will now be generated in this method and the HttpKernel.
Could probably store it in an array internally at least for the current request:
https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php
* | ||
* @author Fabien Potencier <fabien@symfony.com> | ||
*/ | ||
interface ParamConverterInterface |
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.
We should deprecate it first in 3.x, shouldn't we?
… resolvers (chalasr) This PR was merged into the 3.3-dev branch. Discussion ---------- [HttpKernel][FrameworkBundle] Lazy load argument value resolvers | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | n/a The ArgumentResolver resolves an arg using the first ArgumentValueResolver which `supports()` it (which can be complex, see e.g. [sensiolabs/SensioFrameworkExtraBundle#436](https://github.com/sensiolabs/SensioFrameworkExtraBundle/pull/436/files#diff-865d48d9369c4431bce36ba642834570R10)). Commits ------- 02b4aaa [HttpKernel] Lazy load argument value resolvers
Any ETA? |
Closing in favor of symfony/symfony#37829 |
needs symfony/symfony#20036 for more advanced usage.