Problem/Motivation
The block system in Drupal 8 supports contexts, so that configurable arguments can be passed to blocks. This system is not connected to contextual filters in views. Views with block displays can be created but will not work.
Proposed resolution
Expose the contextual filters to the block context system, so that block module but also page_manager and everything that builds on that can pass values for those contextual filters. That is for example very powerful combined with static contexts or dynamic arguments in page manager.
The primary complexity is that block context uses typed data to define the contexts, but views does not have type system, it only knows about argument plugin ids and argument validation.
The code uses a combination of those to attempt to e.g. map argument validation plugins to the corresponding typed data types but it is not possible to support any argument configuration.
Remaining tasks
User interface changes
Blocks that have required contextual filters will no longer be shown in the block UI if the necessary context does not exist. Currently those blocks are shown but then don't work.
API changes
Comment | File | Size | Author |
---|---|---|---|
#148 | 2287073-148.patch | 28.03 KB | Berdir |
Comments
Comment #1
BerdirHere's the patch.
Comment #2
BerdirComment #3
Berdir1: views-block-context-2287073-1.patch queued for re-testing.
Comment #4
dawehnerIn general I guess we have to also change the UI in views to explain that. Previously the UI told the user that blocks don't get arguments but just use "provide default argument from X".
The code should at least explain why we need to special case entities.
Oh, but we just expose entities here?
Comment #5
BerdirSure, this is certainly not done, happy for any feedback/ideas on how to improve it.
Note that the result for core will not change with the current block UI, as it's capabilities to inject something are very limited (no UI to configure mapping, and almost no context right now). So I need to figure out a reliable way to define when context is required and when not, based on default value/fallback configuration.
1. Really just a hack at the moment, it's simply becaus we define the needed context as entity objects but views wants their ID's, so I'm "casting them down" again.
2. Right. Entities are the only thing I can reliable identify right now, because they use the same type definition (which is not explicit but a side effect of typed data identifiers and validator plugin entity derivates using the same format). The problem is that views uses it's own implicit type system, it only knows about plugins, not what kind of context/typed data type that is. It's the same problem as you had when you worked on the entity views data controller, just the other way round.
We have a few options, from hardcoding a few known types (numeric => integer), EclipseGc suggested to go back to the data that we defined the views data on, but that's only possible if we do that in a generic way (no idea to do it even then) and only for entity/field based things. And honestly, injecting entities is exactly what I needed for my use case/project, so I started with that :)
Comment #6
rlmumfordIn views_content in drupal 7 you can specify which property of the entity you want to use.
You might have an contextual filter on the username column of the user table and then state that the required context is user and the property to use is the username.
Comment #7
BerdirRe-roll and also added an example of a static mapping of a non-validated argument.
Still very proof of concept.
@rlmunford: That's not the job of views but whatever passes the context to it, for example page manager. Which doesn't exist yet, if it could just specify it as integer and then etract the ID ouf of the term entity in the page manager/context UI.
Comment #8
rlmumford@Berdir, surely it's part of the Block Plugin? (proven by the fact that the ID is being extracted from the context in the block plugin). In drupal 7 the view decides what property it takes from the context using an Argument Input setting in the Display Settings section.
Comment #9
BerdirNow also supporting cache keys based on the provided context, useful in combination with #2344073: Support block caching.
@rmlumford: That is not how context in D8 works. There is no global context that views can access. The parent controls the available context and the mapping is for example on the page manager block edit form.
Comment #10
BerdirAnd.. another re-roll.
Comment #11
dasjohere's a re-roll
the interdiff is a bit weird to me, but i guess its alright? :)
Comment #12
dasjoi just tested this with the latest dev of page_manager and #2377757: Expose Block Context mapping in the UI
when you create a view with a node argument and set validation to check for the entity type
then the argument / contextual filter context will be exposed in page manager
pretty neat!
Comment #14
ArlaIf there is no value in the context, the argument passed to the view should not be NULL but rather the exception value ('all').
Comment #15
Wim Leers#2375695-22: Condition plugins should provide cache contexts AND cacheability metadata needs to be exposed pointed out that it's not clear how to make this set the right cache contexts. Could you explain in a bit more detail the obstacles you're seeing?
AFAICT this is the problem: Views determines which cache contexts to set when rendering a view display by looking at the cache contexts stored in the view's config entity. Those were calculated at save time. They're calculated at save time because it's extremely expensive to initialize all handlers & plugins to just calculate the cache contexts necessary: that'd make the cost of things you have to do *before* you can use the render cache so very expensive that the impact of render caching is relatively low.
But with this feature, that assumption is no longer correct, because we can only know all the cache contexts involved at run time, since it is a dynamic (because conditional) argument.
Assigning to dawehner to get his thoughts.
Comment #16
dawehnerI think it should be NULL ... you can still configure your view to show every entry, in case there is no argument specified, or do I see something wrong?
Regarding #15
As we have seen in #2381277: Make Views use render caching and remove Views' own "output caching" we don't need render array based caching, for blocks, because all what is needed there is to bubble up the cache metadata from the rendered array, so it can be leveraged the next time you render the view, using cache redirects.
Isn't all we need on runtime for context contexts to say which cache contexts they have and add that to the render array? This then would not have to involve views land at all, and all the expensiveness would not matter much?
Comment #17
ArlaAh, yes, you might be right. But then something else in the argument handling is wrong. For a view with two arguments, if I only pass something to the second, it will be NULL for the first (without #14). Then in ViewExecutable::_buildArguments(), the
if (isset($arg) || $argument->hasDefaultArgument())
condition is false (hasDefaultArgument() returns FALSE for the option "Display all results for the specified field" that you refer to) and then the second argument is not even considered. I am not familiar enough with the internals of Views to see what action is appropriate here, but one suggestion would be that the correspondingelse { break; }
should really beelse { continue; }
.Comment #18
Berdir@dawehner: Any feedback? :)
I had one more use case that I want to add and that's the numeric validator. That's another case where we can set the type.
Comment #19
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedBroken blocks is a major bug in context / plugin system, not a featutre request.
Comment #20
BerdirAs much as I'd like to see this in core, I'm not sure that argument is valid ;)
This isn't something that was ever possible before and it's not possible to use it with core blocks atm anyway.
It can also be implemented in contrib, either by providing alternative block displays or altering the existing ones and the used class.
Comment #21
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commented#20: That depends on the brokenness of things ;). We should not ship with unusable things that half-work; that only frustrates developers.
Comment #22
dsnopekIf this doesn't get fixed in core, we'll do it in CTools, for sure, because this was possible with the 'Content pane' display in CTools in D7 and we'll need to continue having that functionality.
Comment #23
BerdirYes, and it should be possible to do it there. ctools should be able to do this in a transparent way by altering and extending the derivate plugin definitions. And when core starts to support it, it can just stop doing that.
As much as I like see this, this is really not a bug :)
Comment #26
BerdirNeeds reroll.
Comment #27
thenchev CreditAttribution: thenchev at MD Systems GmbH commentedComment #28
BerdirSome small changes to catch up with context changes and the numeric validation thing.
Comment #29
thenchev CreditAttribution: thenchev at MD Systems GmbH commentedWorking on tests
Comment #32
dawehnerComment #33
piyuesh23 CreditAttribution: piyuesh23 at QED42 commentedRe-rolled the patch. Uploading an updated patch.
Comment #35
thenchev CreditAttribution: thenchev at MD Systems GmbH commentedReuploading patch
Comment #36
slashrsm CreditAttribution: slashrsm at MD Systems GmbH commentedGreat work! I was always amused how shamelessly blocks ignore view arguments.
Do we really need to call this here? It seems to do nothing useful or am I missing something?
s/plugins/Plugins
Should we remove first and last empty lines?
Comment #37
thenchev CreditAttribution: thenchev at MD Systems GmbH commented36.1 Removed doesn't seem to do anything
36.2 Good eyes
36.3 We should
Comment #40
thenchev CreditAttribution: thenchev at MD Systems GmbH commentedTested it on the latest core code and it applies.
Comment #43
thenchev CreditAttribution: thenchev at MD Systems GmbH commentedSo there is a case when you can "check" specify validation criteria but the validator is left at -basic validator-
Comment #44
slashrsm CreditAttribution: slashrsm at MD Systems GmbH commentedJust one nit-pik.
Let's do empty check on $argument['validate']['type'] as we actually use that in the code below.
Comment #45
marvin_B8 CreditAttribution: marvin_B8 as a volunteer and at comm-press commented#43 reroll
with the tiny fix from #44
Comment #46
marvin_B8 CreditAttribution: marvin_B8 as a volunteer and at comm-press commentedComment #49
thenchev CreditAttribution: thenchev at MD Systems GmbH commentedCorrected fix for #44
Comment #51
thenchev CreditAttribution: thenchev at MD Systems GmbH commentedYay for RC!
Comment #52
slashrsm CreditAttribution: slashrsm at MD Systems GmbH commentedLooks good to me. Great work everyone!
Comment #55
zaporylieLast patch needs to be re-rolled. I'm on it.
Comment #56
zaporylieRe-rolled #51. In conflict with #2533800: Remove all unused use statements from core
Comment #57
zaporylie... and back to RTBC.
Comment #58
BerdirI don't think this will get into 8.0.0, probably also not in a patch release.
As I commented above, as much as I'd like to see this (I've been using a version of this patch since June 14 on D8 sites), I don't think it is a bug, adding it is a feature or maybe a feature-task.
It is great to have test coverage and we should keep it working and ready, which should be easier now with RC but I think we should also open a separate issue to get this into ctools for the time being.
Comment #59
BerdirThis should be updated to:
elseif ($argument['default_action'] == 'ignore') {
So that we only set all if the view is configured to show all by default. Then default arguments and configuration to hide if missing and so on will still work.
Comment #60
thenchev CreditAttribution: thenchev at MD Systems GmbH commented#59 Fixed
Comment #61
slashrsm CreditAttribution: slashrsm at MD Systems GmbH commented8.1.x it is then.
Comment #62
alexpottCan we assume that integer contexts are correct for numeric arguments?
Also the test seems a bit light for all the conditions contained in the patch. The issue summary suggests that only entity support is added but the patch seems to add more.
Comment #63
no_angel CreditAttribution: no_angel as a volunteer commentedComment #64
BerdirJust a reroll for now.
Comment #65
dawehnerIs there a reason why we not use
$display->getHandler('arguments')
So the second parameter is some form of label. Should we rather use $argument->options['title'] instead?
This looks super weird to be honest :)
Any reason we don't use something descriptive like
'test_view_block_with_context'
?Comment #67
slashrsm CreditAttribution: slashrsm at MD Systems GmbH commentedAddressed #65.
Comment #68
tim.plunkettBe careful with the assignment here, AFAIK you need to wrap that first one in parens.
Comment #69
slashrsm CreditAttribution: slashrsm at MD Systems GmbH commentedComment #70
dawehnerIs it just me or does this patch have kind of a week test coverage? For example we don't test that the actual context passing actually works.
Tim is right about that, see https://3v4l.org/OdUV8
Comment #71
slashrsm CreditAttribution: slashrsm at MD Systems GmbH commentedAdded some test coverage.
Comment #72
tim.plunkettThat interdiff is incomplete. You fixed my point in #68 but it's not in there...
Did that test fail without the fix for #68?
Comment #73
slashrsm CreditAttribution: slashrsm at MD Systems GmbH commentedYour point from #68 was fixed in #69 and is also in the interdiff there.
I do get some notices when running test on pre #69 code locally. Uploading patch for testbot to check it.
#71 is strange as some .orig files sneaked in. Re-uploading.
Comment #76
slashrsm CreditAttribution: slashrsm at MD Systems GmbH commentedExpected fail.
Comment #77
dawehnerWhat happens if you have a non context argument first ... then $args have no values at all for those, maybe setting a NULL for those would be nice
Comment #78
slashrsm CreditAttribution: slashrsm at MD Systems GmbH commentedMakes sense. Added and updated the test.
Comment #79
dawehnerI think we no longer needs additional tests ...
I'm wondering whether it would be worth to replace this crazy nesting with setting
$args[$argument_name] = NULL
and then override it just for the cases we know?Comment #80
slashrsm CreditAttribution: slashrsm at MD Systems GmbH commentedComment #83
dawehnerNice, less crap to maintain
Comment #84
slashrsm CreditAttribution: slashrsm at MD Systems GmbH commentedThis should fix the failing test.
Comment #86
dawehnerThank you!
Comment #87
jibranSome minor issues.
CS is wrong here.
Maybe combine these two.
Comment #88
alexpottOther small things to fix.
Comment #89
andypostFix cs
#87.2 not sure it makes sense to combine them
Comment #90
slashrsm CreditAttribution: slashrsm at MD Systems GmbH commentedAgree about #87.2. It doesn't seem that we can easily do that.
Comment #91
andypostback to rtbc cos there's only cs changes
Comment #92
hkirsman CreditAttribution: hkirsman commentedI have a field of type "List (text)" in content type and it has 2 values. I can make a contextual filter in views and make it get the value from url but is it also possible to set some variable from page manager with this patch? Just that the url might change during the development and then the value wouldn't be ok anymore.
Comment #94
slashrsm CreditAttribution: slashrsm at MD Systems GmbH commentedComment #95
aspilicious CreditAttribution: aspilicious commentedI can confirm this works.
Tested it in combination with #2709235: Block fields don't make use of available contexts
Comment #96
kriboogh CreditAttribution: kriboogh at Calibrate commentedSo I need 83 and 89 to get this working? Can it be applied to 8.1.x ?
Comment #97
alexpottI think that this should be tested - there doesn't appear to be a test for this.
Comment #98
BerdirWe have generic test coverage for this, see \Drupal\block\Tests\BlockUiTest::testContextAwareUnsatisfiedBlocks() for example. And this adds a test that blocks do expose their contexts and can be used if contexts exist.
What exactly do we need to test on top of that?
Comment #99
BerdirUpdated the issue summary.
As discussed with @alexpott, I don't think we need test coverage for the block UI thing, that was a misunderstanding (I hope the issue summary explains it better now) but we need to check arguments with a default value if we don't already and also a numeric/non-entity example to have acceptable test coverage IMHO.
Comment #101
chx CreditAttribution: chx at Smartsheet, Migrate Rocks commentedWhile I can confirm this patch works it is of limited use currently until NodeRouteContext is refactored into a EntityRouteContext . Also some words on the UI that the argument needs to be validated would be nice.
Comment #102
dawehnerI cannot agree more with #23
@xjm @alexpott @cilefen @tim.plunkett agreed that this is not a bug, but a major task, of course.
Comment #103
alexpottComment #104
EclipseGc CreditAttribution: EclipseGc commentedActually, it's relatively difficult to change what the deriver does without altering every plugin in the system generated by the deriver. You can't get in front of that because of where and how it happens in plugin discovery currently. Just as an FYI, yes we COULD do something along these lines in CTools, but it would be super heavy handed. Prefer to see it in core if possible.
Eclipse
Comment #105
andypostWhat's left here? Tests are here and generic coverage for contexts in core
Comment #107
Thew CreditAttribution: Thew at Google Code-In commentedRerolled the patch
Comment #109
Thew CreditAttribution: Thew at Google Code-In commentedThe patch in #107 is wrong.
New rerolled patch file.
Comment #111
BerdirThanks for the reroll. updating the test view. This still needs more test coverage.
Comment #112
BerdirSome of the changes introduced a small bug for numeric arguments without validation. There is always an argument validator, sometimes it's just null. Then we didn't go into the else case and didn't expose the argument.
As I said before, needs another test at least for that case.
Comment #113
dawehnerSo let's be honest the status here :)
Comment #114
BerdirOk, here's a test only patch based on the patch in #111 that fails testing, see test-only interdifff. The combined patch is with the interdiff from #112 included.
Uploading so tests can run, have another idea that I want to try out. This is still based on the initial hacks that I put together to get this started. Going to try and delegate this to the argument plugins.
Comment #115
BerdirSo, this is like 3x as much code, but it's also *way* more flexible and we can easily expand it to other arguments, as can any contrib/custom argument plugin out there.
Wondering if we could also have a method that moves the logic in \Drupal\views\Plugin\Block\ViewsBlock::build() to the argument (validator/default) plugins, will have a look at that tomorrow.
Comment #117
andypostHaving interface is good idea.
But that will require to update all contrib
OTOH that never work before so better to get into 8.3
Comment #118
BerdirContrib isn't required to do anything at all, it *allows* them to do something, before it was impossible .
Comment #119
BerdirSome cleanup and using the actual API for argument_default plugins (I think that is their API, it's not like they have an interface or so that would describe it :p).
Not sure it's worth doing more about the logic in there for now.
Comment #120
Berdir2287073-116.patch isn't passing our tests, so it looks like that isn't doing the same, need to check again.
Comment #121
dawehnerIMHO we can skip the if.
getHandlers
will always return an array of values.I'm wondering whether this has to be part of this patch. The default argument values are handled somewhere in views at the moment, so I don't see why we strictly need it. I see the point of this change though, and I believe its actually a good one, but you now, its all about lowering the surface density.
It is a bit confusing to be honest that we don't have similar kind of code for strings etc ...
Comment #122
BerdirOk, figured out again why we need the ignore special case and yes, we indeed don't need normal defaults. You could work around that problem by making it a default with value "all" instead, but we have existing sites that rely on this, so I'd appreciate if we could keep that workaround.
Also changed the logic a bit, the test showed that the previous logic only worked on argument plugins that can be exposed as context and the Null examples don't.
Also added a context for string with some basic test coverage.
Comment #123
BerdirAs discussed, removing the interface again. Previous patch passed our distribution tests.
Comment #127
BerdirAh, that's why the if was there, but lets fix this in the mock instead..
Comment #128
dawehnerI'm wondering whether there is some way of empty context definition, empty as in providing nothing. In this case we could skip returning NULL. I just assume there isn't :)
Comment #129
EclipseGc CreditAttribution: EclipseGc commentedOk, I've looked through this a bit and REALLY like what's sitting here. (For whatever that's worth).
A few quick validating questions:
I've attached a minor interdiff that I THINK is an improvement over this both in intent and readability. I wouldn't un-rtbc this over it, but it might be worth incorporating.
Delegates to the Validator if there is one, and if not, returns an optional context definition for (in this case) numeric argument plugins.
That means that if there is not a corresponding value for this, AND it's not a required argument, then your block code will use "all" by default for the argument value. Right? If so, ++
So the argument validator here returns an optional context definition. Is that what we actually want to do? Shouldn't this ONLY be the case if we allow "all" to be passed for this argument? Same question for all the other validators in this patch.
Hope this seems sensible.
Eclipse
Comment #130
BerdirI think your interdiff would fail the test as it always uses $value, even when empty and therefore overrides the initial 'all' in case 'ignore' is used for an argument. If you wrap that also in an if ($value) then I'm fine with that change.
We currently never set contexts as required. That would be possible based on the configured action (use default value, ignore, deny access, ..). I'm not sure exactly what the implications would be. But yes, it basically works as you said, if you don't provide a context, it can fall back to a default value or ignore it, depending on your configuration. Or it can deny access to the block, also possible. I think views logic there is actually more flexible than a required yes/no (for example you can configure it to show the empty text), so I think it's fine like this for now.
3. See above. Having an argument validator that makes sure it is a node, which is exposed as a context but then also configure the argument to default to the current node from the URL is a perfectly valid configuration. The validator doesn't know if it's required or not.
Comment #131
EclipseGc CreditAttribution: EclipseGc commentedOk, sounds generally good, and certainly lightyears better than what we have today.
As for my interdiff, it checks if a particular context has a value object associated with it. If it does, of course you'd delegate to that value object. But since it's checking to see if the value object is there, if it is not there, it won't override the "all" you set just previous to this if statement.
TL:DR; The if statement only succeeds if there was a value object in the context object.
Eclipse
Comment #132
EclipseGc CreditAttribution: EclipseGc commentedWhat are the odds of this making it into 8.3.0? I can't underscore how great this is from a contrib perspective enough. It should have been part of 8.0.0.
Eclipse
Comment #134
BerdirUpdated the patch with the interdiff from #129.
I really hope we can get this into 8.3.x. As @dawehner I think mentioned, we've been using something like this successfully for 2 years now on our production sites and it is working well. And it has +1 from views and context/plugin maintainers, so I hope that's all we need :)
Possibly a change record. I'll try to write one tonight.
Comment #135
dawehnerBack to RTBC
Comment #136
BerdirRemoved resolved remaining tasks and created a change record: [#2846863]
Comment #138
BerdirThat doesn't work as there is sometimes no defined context for weird arguments like Null.
Re-uploading the previous patch, keeping at RTBC as that patch has been RTBC'd before. EclipseGc also +1'd to sticking with that in IRC.
Comment #139
andypostI think it needs a comment about why there's
EntityInterface
instead ofis_scalar()
for example?Comment #140
jibranShouldn't we be using some kind of interface?
Comment #141
Berdir#139: I don't understand how is_scalar() could replace the EntityInterface check? But I can add a comment, something like this should work: // Context values are often entities, but views arguments expect to receive just the entity ID, convert it.
#140: I introduced a new (optional) interface for it in #115 but discussed with @dawehner and we thought that it's not really necessary considering that argument (validator) plugins currently have no interface at all. I'd be open to adding it back, optional or not if more people think it should have one. We could also actually introduce interfaces for those and other plugins and just make it part of that.
Comment #142
andypost@Berdir please add this comment, it explains the check for EI, it makes clear what context value expected by views
Comment #143
BerdirAdded that comment.
Comment #144
jibranA couple of PHPCS fixes.
Comment #145
dawehnerJust use @return bool
Comment #146
jibranHere we go.
Comment #147
BerdirThis is completely unrelated to this patch and was not changed previously?
Comment #148
BerdirRemoved that again.
Edit: Interdiff is against #143.
Comment #149
alexpottAs far as I know there is no requirement to implement the base class. And since getContextDefinition() is not part of any interface - I think this has a chance of breaking. Shouldn't we have an optional interface that we can test for here?
Comment #150
jibranHow can we tackle multiple values arguments? AFAIK argument_validator doesn't play nice with multiple values.
Comment #151
jibranThese both should have @see to each other.
I think
argument_default
plugin should also set the context.Comment #152
BerdirI discussed #149 with Alex, he's OK with not adding an interface for now, as I mentioned, ArgumentPluginBase is a 1300 lines of code base class without interface, someone providing an alternative implementation is more than unlikely. We should however discuss opening issues to extract and define interfaces for various views plugin.
#150
I don't know, but I don't think we need to solve that here. We can always try to expand this in follow-ups, but I never had a use case for passing along a multi-value argument in the 2 years using this.
#151
1. We explicitly call that other method, that seems enouh reference for me, AFAIK we usually add @see if it' not something that is directly visible from the code.
2. Again, we're adding enough IMHO for a first isue. I don't really see why a default argument plugin would want to define what is passed in, they are about providing a fallback if nothing is provided.
Setting back to needs review to get some more feedback on this.
Comment #154
jibranFair point let's discuss that in follow-up and not a using doesn't imply that no one will use it. ;-)
The methods are not defined on the Interface therefore, it is difficult in the PHPStorm to navigate. That's why I asked. I still think it's an improvement.
The default argument plugins provide the exact value so it seems fair to provide the context as well. I'm fine with tackling that in follow-up as well.
Let's put it back to RTBC as #151 and #149 are addressed.
Comment #155
jibranAfter discussing the issue with @Berdir in IRC we agree that views default argument plugins provide the argument value so to use those to define the context would not make sense. However, we agreed that this functionality should not only be limited to validation argument plugins and argument plugins should have enough information using views data to create their own context and use validation argument plugin as a fallback so I created #2847644: Allow views contextual filters to expose the context.
Comment #156
xjmJust a note that the SQLite failure in https://www.drupal.org/pift-ci-job/597461 is #2806697: Random fail for AlreadyInstalledException, so not introduced by this patch.
Comment #157
alexpottCommitted and pushed 73312ef to 8.4.x and 3ed4f6e to 8.3.x. Thanks!
Thanks everyone for persisting with this one - it's important functionality.