Chris Bell Chris Bell - 1 month ago 18
PHP Question

Symfony: Resolve service in route

I need to implement various meta data attributes for each route, and one idea I have is to apply the attributes directly onto each route, for an external meta data listener to make use of.

For example, the listener would use the service (

@example_title_resolver
) defined on the below route, to resolve into some form of page title.

example_route:
path: /blah/blah
defaults:
_controller: MyBundle:MyController:index
meta:
title:
resolver: '@example_title_resolver'
value: 'Example | %%value_to_be_resolved%% | %default_title_suffix%'


Unfortunately, whilst params get resolved, I've since discovered services do not. The only way around this approach right now would involve injecting the service container directly into the listener, and querying for the service, which is frowned upon.

Are there any better alternatives? Given I've really struggled to find out about resolving services from within routes, is this whole approach something to be avoided?

Answer

Here's the solution I ended up with:

Route

example_route:
    path:     /blah/blah
    defaults:
        _controller: MyBundle:MyController:index
        meta:
            title:
                resolver: '@example_title_resolver'
                value: 'Example | %%value_to_be_resolved%% | %default_title_suffix%'

Listener 1 - Service resolver. Invoked immediately after the RouterListener with a priority of 33 or higher, at this point the route params have been applied onto the request (@see https://github.com/symfony/http-kernel/blob/master/EventListener/RouterListener.php#L97).

The listener, attached to the kernel.request event inspected the request params, and resolved any services (string values prefixed with @) against the service container injected into the listener.

Listener 2 - Meta data listener. At this point, everything on the route/request has been resolved into services already. This decouples concerns, and allows further unrelated listeners to benefit from the ability to talk to services available directly on the request.

In this example, the meta data listener would look for specific params (resolved services) on the request. These objects would be used to resolve meta data values for example. A common mechanism would then be required to make this data available to the view.

Resolver classes Responsible in turning a given template string into the final value. Invoked by the meta data listener above.

Advantages

  • Separates concerns, making controllers more reusable.
  • Flexible, allowing lightweight resolver classes to be developed/discarded.
  • In the specific case of meta data, provides a nice way of future proofing when inevitably some routes will need new types of meta data adding. This would just be a new resolver, and a small addition to route(s).

Disadvantages

  • Unexpected magic to an unfamiliar eye.
  • Service resolving values from request could be a security concern if not dealt with properly.

I'm going to roll with this for now as the advantages are appealing. I'll check in when the solution no longer fits.

Comments