KnpMenuBundle icon indicating copy to clipboard operation
KnpMenuBundle copied to clipboard

SF 4.3 - Voters - theirs order and auto-registration as service

Open sabat24 opened this issue 6 years ago • 2 comments

I created new Voter in src\Menu\Voter directory. I was surprised that I didn't have to register that voter as a service in services.yaml. Voter just started to work without any other action from my side. Is it normal behaviour because I used menu directory name which is the directory of the bundle?

My Voter was added before default one to the matcher's voters array. I would like to change the order of voters that my voter would be the last one. Is it possible? If not can I (I mean is it good solution) just create new class and extends Knp\Menu\Matcher\Matcher class to overwrite constructor and reverse order of already added voters?

sabat24 avatar Aug 26 '19 19:08 sabat24

if you autoconfigure your services (which is the default behavior in a Symfony 4 app based on the Flex skeleton), a service implementing the VoterInterface will automatically be tagged as knp_menu.voter. This is why it is working without extra config. It is not related to the namespace of the class.

Voters can have a priority to control their order. But that will require tagging them as voter explicitly (the autoconfigured tag does not specify a priority, which makes it fallback to 0, as done in Symfony core). Extending the Matcher and reversing the order of voters is not the supported way of doing that (and would break the lazy-loading of voters performed by the injected iterator).

stof avatar Aug 27 '19 11:08 stof

Thank you for clarification.

Based on what you have described, I have to manually tagged voters and provide priority order. However when I tagged voters manually in services.yaml my custom voter in matcher is duplicated.

app.voter.route:
    class: Knp\Menu\Matcher\Voter\RouteVoter
    arguments: [ '@request_stack' ]
    tags:
      - { name: knp_menu.voter, priority: 0 }
  app.voter.parent:
    class: App\Menu\Voter\RequestParentRouteVoter
    arguments: [ '@request_stack' ]
    tags:
      - { name: knp_menu.voter, priority: 1 }

When matcher is initialized there are 3 voters:

App\Menu\Voter\RequestParentRouteVoter <- custom voter App\Menu\Voter\RequestParentRouteVoter <- custom voter Knp\Menu\Matcher\Voter\RouteVoter <- default voter

edited I figured it out where was the problem. Autoconfigured voters are always added to array with index 0. So I have to add default RouteVoter with priority 2 and my custom voter with priority 1. So the behaviour of duplicated voters is correct. However it leads to a situation that 0-indexed voters could be run twice. Is it ok, because responses of prioritized voters are cached by matcher and there are no performance issues?

The order of voters for item which wasn't recognized is as follows: (first voter to be run is on top)

  1. Knp\Menu\Matcher\Voter\RouteVoter - default voter defined in services with priority 2 - returns null;
  2. App\Menu\Voter\RequestParentRouteVoter - my custom voter defined in services with priority 1 - returns null;
  3. App\Menu\Voter\RequestParentRouteVoter - my custom voter autoloaded by symfony with priority 0 - returns null;
  4. Knp\Menu\Matcher\Voter\RouteVoter - default voter autoloaded by symfony with priority 0 - returns null;

sabat24 avatar Aug 27 '19 12:08 sabat24