Problem/Motivation
#2238217: Introduce a RouteMatch class removed the getActiveTheme() method. Granted, not much was using it, but that is changing (two patches in the queue were using it and broke).
Now instead of calling that method, a properly injected class also has to inject a RouteMatch and pass it along. All I want is the current theme, I should be able to ask the theme negotiator for that.
In addition to all of the other classes implementing ThemeNegotiatorInterface, ThemeNegotiator is the one acting as the service, and has the extra methods to gather and delegate. This is similar to BreadcrumbManager, which implements BreadcrumbBuilderInterface.
Proposed resolution
Remove the RouteMatch parameter from determineActiveTheme(), so it serves as a replacement for getActiveTheme().
Inject RouteMatch into any theme negotiators that need it via their service definition.
Additionally, add a new interface for the addNegotiator() method that only ThemeNegotiator (the service class) implements.
Remaining tasks
N/A
User interface changes
N/A
API changes
ThemeNegotiatorInterface::determineActiveTheme() no longer takes any parameters.
Theme negotiators that need a route match should inject it via their service definition.
Comment | File | Size | Author |
---|---|---|---|
#46 | interdiff_44-46.txt | 1.79 KB | vacho |
#46 | 2292217-46.patch | 8.19 KB | vacho |
#44 | 2292217-44.patch | 7.49 KB | vacho |
#38 | 2292217-35-reroll.patch | 6.92 KB | joelpittet |
#35 | 2292217-35.patch | 6.33 KB | joelpittet |
Comments
Comment #1
tim.plunkettComment #2
Crell CreditAttribution: Crell commentedThe naming is consistent with both ChainRouter and ChainBreadcrumbBuilder, so +1 from me.
Having the extra getActiveTheme() method worries me a little, though. I can see why it's there and what the advantage is, but one of the nice things about reusing the interface directly is that I can wire a single router or a single breadcrumb builder up directly, without any negotiation, and everything still works. With this additional method that's not the case here.
Comment #3
tim.plunkettHere's a completely different approach. This way we don't need another new method.
Also, quite a few of the negotiators don't need the route match anyway, it seems weird to pass it to both methods.
Comment #4
effulgentsia CreditAttribution: effulgentsia commentedBack in #2134513: [policy, no patch] Trait strategy, some people expressed concerns about both trait proliferation and setter injection. I'm not really all that bothered by either, but this probably needs a review from someone who is.
Comment #5
Crell CreditAttribution: Crell commentedIf we're going to remove RouteMatch from the passed parameters, just make it a constructor parameter for the negotiators that care. #3 makes the negotiators non-stateless, even though they're registered as services. We can simplify it by just passing the RouteMatch to each negotiator in its constructor, no trait, no muss, no fuss. And then we have a unified interface between the negotiators and the aggregating service, which we want; we probably should still add a chain interface anyway as with breadcrumbs and the patch from #1, it just won't need anything but an addX() method.
Comment #6
tim.plunkettMight as well. Thanks!
Comment #7
tim.plunkettComment #9
tim.plunkettMissed a spot.
Comment #10
Crell CreditAttribution: Crell commentedNo chain interface?
Other than that, #9 looks good to me.
Comment #11
tim.plunkettAdded, and rerolled after the kernel and current theme condition issues.
Comment #12
tim.plunkettMissing the interface.
Comment #13
Crell CreditAttribution: Crell commentedAssuming the bot is still happy I think we're good here. Thanks, Tim!
Comment #14
Dries CreditAttribution: Dries commentedThe issue summary and issue title seems wrong. The patch itself looks good to me though.
Comment #15
tim.plunkettGood point, that was about 3 approaches ago :)
Thanks, updated!
Comment #16
xjmUpdated a relevant change record for this and the original RouteMatch issue: https://www.drupal.org/node/2158619/revisions/view/6783809/7398343
Comment #17
dawehnerDoesn't that mean that there should be two separate interfaces? As a developer of a theme negotiator I cannot imagine that you would ever NOT want to have the route match passed along.
Comment #18
effulgentsia CreditAttribution: effulgentsia commented@dawehner: What's the benefit of having it passed-in vs. constructor injected. I don't see a down side to #12's approach of using CI, but I'd like to understand your concern more.
Comment #19
sunThe way I see it, it's about explicit configuration/state of a service vs. call-time/context-specific arguments.
When moving
RouteMatch
+Request
into the constructor of theme negotiators, the same change must include a new architectural facility to "reset" all theme negotiators, so that a new context can be negotiated.Thus far, I interpreted theme negotiators as simple I/O processing plugins. You get some context in, you return a result based on that context.
I should be able to trigger theme negotiation for an arbitrary/ad-hoc request/context and get a proper result back. This may happen and be necessary for rendering e.g. theme-specific blocks (HTML fragments) as part of a master request. Sub-requests may or may not be another use-case.
I was confused by the previous
Request
→RouteMatch
injection change already. (Even more so by losing access to the currentRequest
that corresponds to the matchedRoute
- which doesn't make sense.)Already in HEAD, the most trivial theme negotiator implementation possible (
?theme=
GET query parameter matching) suddenly had to (1) introduce state and (2) became considerably more complex:IMHO, theme negotiators should be atomic I/O plugins; i.e.:
RouteMatch
context in, it returns the negotiation result (if any).RouteMatch
context supplies access to all ancestors + related data, including theRequest
.Comment #20
tim.plunkettThere was a major regression for anyone trying to find out what the current active theme is.
If my (working) approach is going to be held up, it'd be nice if someone else proposed an alternative.
Comment #21
dawehnerNoone disagrees with that and the route match issue was one of these "let's push it through because this is one less beta blocker" issues.
I hope it is okay to criticize other people.
Well, my approach would be: Have one interface for the main theme negotiator and one for the sub-thingies. Here is an excerpt of HEAD april 23th (last checkout on this machine):
Comment #22
tim.plunkettSo you mean the patch I had in #1?
Comment #23
dawehnerI think we should certainly change internal details of our code if it helps both the DX of end-developers and if needed add complexity, but yeah I am so out of things that I would not trust me.
Comment #24
effulgentsia CreditAttribution: effulgentsia commentedI continue to think the patch in #12 is correct, and I disagree with dawehner and sun.
We have multiple independent services for getting data about or derived from a request: 'request_stack', 'current_route_match', 'current_user', etc. At an API level, these are independent, meaning none of them have methods for getting another. ThemeNegotiatorInterface doesn't know which of these a particular negotiator will base its decision on. Therefore, it makes no sense to pass any of them to determineActiveTheme(). Instead, each negotiator can specify what it uniquely needs in its constructor.
What's an actual use case of that?
Is there any legitimate use case to do the first without a sub-request? As to sub-requests, constructor injection is 100% compatible with that. Each negotiator's *.services.yml entry can either wire in services that are internally sub-request compatible ('request_stack', 'current_route_match', 'current_user' all are), or else, the service can be flagged as having request scope, in which case it will be automatically re-instantiated for subrequests (this is no longer Symfony's recommended approach, but it's an available option for contrib to use if necessary).
Comment #25
Dries CreditAttribution: Dries commentedI agree with the reasoning in comment #24. It doesn't feel like now is the time to dramatically change RouteMatch (per #19.2). It is certainly worth exploring having a 'mega object' that gives access to everything, but maybe that is best left for Drupal 9?
Comment #28
dawehnerData always wins. Here is a list of theme negotiators in core (left out the test ones and ThemeNegiator itself):
As said before, the common case is to decide based upon the current request, so information coming from the $request and extracted into the $route_match. Applying your point the other way round, you could also argue that access(AccountInterface $account) should NOT get passed in the account. We added the $account on there because it is the better DX
Comment #29
effulgentsia CreditAttribution: effulgentsia commentedThe table in #28 is for the applies() method. However, for determineActiveTheme() there's no such pattern. AdminDemo uses $route_match, Batch and Ajax use $request, and the rest use nothing. So, how about we continue to pass $route_match to applies(), but remove it from determineActiveTheme(), which is all this issue title is asking for anyway?
Comment #30
dawehnerReviewing #1 because it is my favourite
We should explicit document that this method is the method you want to use instead of determineActiveTheme
ups
Should we maybe store the result somewhere?
Comment #31
star-szrStealing @webchick's tag, seems appropriate here.
Comment #32
andypost#29 makes sense
Comment #34
joelpittetRe-roll of #1 though it looks like part of this has been done already because there is no
current_route_match
oncache_context.theme
service.Comment #35
joelpittetComment #38
joelpittetA quick re-roll due to a coding standards change.
#2776975: March 3, 2017: Convert core to array syntax coding standards for Drupal 8.3.x RC phase
Comment #39
dawehnerI'm quite confused as the title doesn't match the actual patch at all!
Note: #2472337: Provide a lazy alternative to service collectors which just detects service IDs will also make this interface not useful anymore.
Comment #42
markhalliwellComment #44
vacho CreditAttribution: vacho at Skilld commentedRerolling this patch
Comment #45
andypostBtw https://www.drupal.org/pift-ci-job/1153771 shows
7 coding standards messages
Comment #46
vacho CreditAttribution: vacho at Skilld commentedSolved, problems with coding standard.
Comment #56
joachim CreditAttribution: joachim at Factorial GmbH commented> I'm quite confused as the title doesn't match the actual patch at all!
I'm seeing several approaches in patches. Tagging for this.
Comment #57
joachim CreditAttribution: joachim at Factorial GmbH commentedThis also seems to be potentially at odds with #2471657: Just load the theme negotiators which are needed, which says:
> Convert \Drupal\Core\Theme\ThemeNegotiatorInterface::applies to accept a route object