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

CommentFileSizeAuthor
#148 2287073-148-interdiff.txt620 bytesBerdir
#148 2287073-148.patch28.03 KBBerdir
#146 expose_contextual-2287073-146.patch28.31 KBjibran
#146 interdiff.txt615 bytesjibran
#144 expose_contextual-2287073-144.patch28.32 KBjibran
#144 expose_contextual-2287073-144-do-not-test.patch28.11 KBjibran
#144 interdiff.txt1.21 KBjibran
#143 2287073-143-interdiff.txt671 bytesBerdir
#143 2287073-143.patch28.36 KBBerdir
#138 2287073-127.patch28.23 KBBerdir
#134 2287073-134.patch28.22 KBBerdir
#129 2287073-129.interdiff.txt1002 bytesEclipseGc
#127 2287073-127-interdiff.txt752 bytesBerdir
#127 2287073-127.patch28.23 KBBerdir
#123 2287073-123-interdiff.txt6.37 KBBerdir
#123 2287073-123.patch27.5 KBBerdir
#122 2287073-121-interdiff.txt7.21 KBBerdir
#122 2287073-121.patch28.75 KBBerdir
#122 2287073-121-test-only.patch27.73 KBBerdir
#119 2287073-116-interdiff.txt1.57 KBBerdir
#119 2287073-116.patch25.13 KBBerdir
#115 2287073-115-interdiff.txt7.9 KBBerdir
#115 2287073-115.patch25.12 KBBerdir
#114 2287073-114.patch20.13 KBBerdir
#114 2287073-114-test-only-interdiff.txt5.74 KBBerdir
#114 2287073-114-test-only.patch20.09 KBBerdir
#112 2287073-112-interdiff.txt1.15 KBBerdir
#112 2287073-112.patch16.26 KBBerdir
#111 2287073-111-interdiff.txt701 bytesBerdir
#111 2287073-111.patch16.22 KBBerdir
#109 2287073-109.patch16.22 KBThew
#107 2287073-107.patch16.49 KBThew
#89 2287073-89.patch16.33 KBandypost
#89 interdiff.txt2.68 KBandypost
#84 interdiff.txt1.75 KBslashrsm
#84 2287073_83.patch16.44 KBslashrsm
#80 interdiff.txt1.8 KBslashrsm
#80 2287073_80.patch16.35 KBslashrsm
#78 interdiff.txt3.1 KBslashrsm
#78 2287073_78_TEST_ONLY.patch16.22 KBslashrsm
#78 2287073_78.patch16.37 KBslashrsm
#73 2287073_test_with_pre_69.patch15.26 KBslashrsm
#73 2287073_73.patch15.27 KBslashrsm
#71 interdiff.txt7.65 KBslashrsm
#71 2287073_71.patch15.81 KBslashrsm
#69 interdiff.txt840 bytesslashrsm
#69 2287073_69.patch12.83 KBslashrsm
#67 interdiff.txt8.51 KBslashrsm
#67 2287073_67.patch12.83 KBslashrsm
#64 views-block-context-2287073-64.patch11.66 KBBerdir
#60 interdiff.txt508 bytesthenchev
#60 views-block-context-2287073-60.patch11.47 KBthenchev
#56 views-block-context-2287073-56.patch11.42 KBzaporylie
#51 views-block-context-2287073-51.patch11.57 KBthenchev
#49 interdiff.txt808 bytesthenchev
#49 views-block-context-2287073-49.patch11.57 KBthenchev
#45 views-block-context-2287073-45.patch11.56 KBmarvin_B8
#43 views-block-context-2287073-43.patch11.56 KBthenchev
#43 interdiff.txt918 bytesthenchev
#40 views-block-context-2287073-40.patch11.46 KBthenchev
#37 views-block-context-2287073-37.patch11.46 KBthenchev
#37 interdiff.txt1.72 KBthenchev
#35 views-block-context-2287073-35.patch11.52 KBthenchev
#35 interdiff.txt7.72 KBthenchev
#33 views-block-context-2287073-33.patch3.83 KBpiyuesh23
#29 views-block-context-2287073-29.patch11.47 KBthenchev
#29 interdiff.txt7.72 KBthenchev
#28 views-block-context-2287073-27-interdiff.txt1.68 KBBerdir
#28 views-block-context-2287073-27.patch3.72 KBBerdir
#26 views-block-context-2287073-26.patch3.42 KBBerdir
#14 views-block-context-2287073-14.interdiff.txt469 bytesArla
#14 views-block-context-2287073-14.patch4.17 KBArla
#12 views_conditional_arguments_page_manager.png216.2 KBdasjo
#12 views_conditional_arguments_nid.png284.73 KBdasjo
#11 views-block-context-2287073-11.patch4.14 KBdasjo
#10 views-block-context-2287073-10.patch4.13 KBBerdir
#9 views-block-context-2287073-9-interdiff.txt1.02 KBBerdir
#9 views-block-context-2287073-9.patch2.39 KBBerdir
#7 views-block-context-2287073-7.patch1.73 KBBerdir
#1 views-block-context-2287073-1.patch2.82 KBBerdir
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Berdir’s picture

Issue summary: View changes
Status: Active » Needs review
FileSize
2.82 KB

Here's the patch.

Berdir’s picture

Issue tags: +Page Manager
Berdir’s picture

dawehner’s picture

In 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".

  1. +++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php
    @@ -28,7 +27,25 @@ class ViewsBlock extends ViewsBlockBase {
    +          $value = $this->context[$argument_name]->getContextValue();
    +          if ($value instanceof EntityInterface) {
    +            $value = $value->id();
    +          }
    +          $args[] = $value;
    

    The code should at least explain why we need to special case entities.

  2. +++ b/core/modules/views/src/Plugin/Derivative/ViewsBlock.php
    @@ -105,9 +105,22 @@ class ViewsBlock implements ContainerDerivativeInterface {
    +                if (strpos($argument['validate']['type'], 'entity:') !== FALSE) {
    

    Oh, but we just expose entities here?

Berdir’s picture

Sure, 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 :)

rlmumford’s picture

+++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php
@@ -28,7 +27,25 @@ class ViewsBlock extends ViewsBlockBase {
+            $value = $value->id();

In 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.

Berdir’s picture

Re-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.

rlmumford’s picture

@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.

Berdir’s picture

Now 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.

Berdir’s picture

And.. another re-roll.

dasjo’s picture

here's a re-roll

$ patch -p1 < views-block-context-2287073-10.patch 
patching file core/modules/views/src/Plugin/Block/ViewsBlock.php
patching file core/modules/views/src/Plugin/Derivative/ViewsBlock.php
Hunk #2 succeeded at 106 with fuzz 2.

the interdiff is a bit weird to me, but i guess its alright? :)

$ diff views-block-context-2287073-10.patch views-block-context-2287073-11.patch 
44c44
< @@ -114,4 +132,23 @@ class ViewsBlock extends ViewsBlockBase {
---
> @@ -114,4 +132,23 @@ public function getMachineNameSuggestion() {
69c69
< index cec9895..9164688 100644
---
> index 3ebfc5f..134e269 100644
80c80
< @@ -105,9 +106,27 @@ class ViewsBlock implements ContainerDeriverInterface {
---
> @@ -105,9 +106,27 @@ public function getDerivativeDefinitions($base_plugin_definition) {
82c82
<                'entity' => array(
---
>                'config' => array(
dasjo’s picture

i 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!

Arla’s picture

If there is no value in the context, the argument passed to the view should not be NULL but rather the exception value ('all').

Wim Leers’s picture

Assigned: Unassigned » dawehner

#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.

dawehner’s picture

If there is no value in the context, the argument passed to the view should not be NULL but rather the exception value ('all').

I 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?

Arla’s picture

I 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?

Ah, 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 corresponding else { break; } should really be else { continue; }.

Berdir’s picture

@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.

Fabianx’s picture

Category: Feature request » Bug report
Priority: Normal » Major

Broken blocks is a major bug in context / plugin system, not a featutre request.

Berdir’s picture

As 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.

Fabianx’s picture

#20: That depends on the brokenness of things ;). We should not ship with unusable things that half-work; that only frustrates developers.

dsnopek’s picture

If 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.

Berdir’s picture

Yes, 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 :)

Status: Needs review » Needs work

The last submitted patch, 14: views-block-context-2287073-14.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review
Issue tags: +Needs tests
FileSize
3.42 KB

Needs reroll.

thenchev’s picture

Title: Expose conditional arguments of views blocks as context » Expose contextual filters of views blocks as context
Issue summary: View changes
Berdir’s picture

Some small changes to catch up with context changes and the numeric validation thing.

Status: Needs review » Needs work

The last submitted patch, 29: views-block-context-2287073-29.patch, failed testing.

The last submitted patch, 29: views-block-context-2287073-29.patch, failed testing.

dawehner’s picture

Assigned: dawehner » Unassigned
piyuesh23’s picture

Status: Needs work » Needs review
FileSize
3.83 KB

Re-rolled the patch. Uploading an updated patch.

Status: Needs review » Needs work

The last submitted patch, 33: views-block-context-2287073-33.patch, failed testing.

thenchev’s picture

Status: Needs work » Needs review
FileSize
7.72 KB
11.52 KB

Reuploading patch

slashrsm’s picture

Status: Needs review » Needs work

Great work! I was always amused how shamelessly blocks ignore view arguments.

  1. +++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php
    @@ -30,7 +30,25 @@ class ViewsBlock extends ViewsBlockBase {
    +    $this->view->display_handler->getOption('arguments');
    

    Do we really need to call this here? It seems to do nothing useful or am I missing something?

  2. +++ b/core/modules/views/src/Tests/Plugin/ContextualFiltersBlockContextTest.php
    @@ -0,0 +1,65 @@
    + * Contains \Drupal\views\Tests\plugins\ContextualFiltersBlockContextTest.
    

    s/plugins/Plugins

  3. +++ b/core/modules/views/src/Tests/Plugin/ContextualFiltersBlockContextTest.php
    @@ -0,0 +1,65 @@
    +  public function testBlockContext() {
    +
    +    $this->drupalLogin($this->drupalCreateUser(array('administer views', 'administer blocks')));
    +    $this->drupalGet('admin/structure/block');
    +    $this->clickLinkPartialName('Place block');
    +    $this->drupalPlaceBlock('views_block:test_view_block3-block_1', array('label' => 'test_view_block3-block_1:1'));
    +
    +    $definition = \Drupal::service('plugin.manager.block')->getDefinition('views_block:test_view_block3-block_1');
    +
    +    $this->assertTrue($definition['context']['nid']);
    +
    +  }
    

    Should we remove first and last empty lines?

thenchev’s picture

Status: Needs work » Needs review
FileSize
1.72 KB
11.46 KB

36.1 Removed doesn't seem to do anything
36.2 Good eyes
36.3 We should

Status: Needs review » Needs work

The last submitted patch, 37: views-block-context-2287073-37.patch, failed testing.

The last submitted patch, 37: views-block-context-2287073-37.patch, failed testing.

thenchev’s picture

Status: Needs work » Needs review
FileSize
11.46 KB

Tested it on the latest core code and it applies.

Status: Needs review » Needs work

The last submitted patch, 40: views-block-context-2287073-40.patch, failed testing.

The last submitted patch, 40: views-block-context-2287073-40.patch, failed testing.

thenchev’s picture

Status: Needs work » Needs review
FileSize
918 bytes
11.56 KB

So there is a case when you can "check" specify validation criteria but the validator is left at -basic validator-

slashrsm’s picture

Status: Needs review » Needs work

Just one nit-pik.

+++ b/core/modules/views/src/Plugin/Derivative/ViewsBlock.php
@@ -111,9 +112,33 @@ public function getDerivativeDefinitions($base_plugin_definition) {
+                if (!isset($argument['validate'])) {
+                  continue;
+                }

Let's do empty check on $argument['validate']['type'] as we actually use that in the code below.

marvin_B8’s picture

#43 reroll

with the tiny fix from #44

marvin_B8’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 45: views-block-context-2287073-45.patch, failed testing.

The last submitted patch, 45: views-block-context-2287073-45.patch, failed testing.

thenchev’s picture

Status: Needs work » Needs review
FileSize
11.57 KB
808 bytes

Corrected fix for #44

Status: Needs review » Needs work

The last submitted patch, 49: views-block-context-2287073-49.patch, failed testing.

thenchev’s picture

Status: Needs work » Needs review
FileSize
11.57 KB

Yay for RC!

slashrsm’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me. Great work everyone!

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 51: views-block-context-2287073-51.patch, failed testing.

Status: Needs work » Needs review
zaporylie’s picture

Assigned: Unassigned » zaporylie
Status: Needs review » Needs work
Issue tags: +Needs reroll

Last patch needs to be re-rolled. I'm on it.

zaporylie’s picture

Assigned: zaporylie » Unassigned
Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
11.42 KB
zaporylie’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs tests

... and back to RTBC.

Berdir’s picture

I 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.

Berdir’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php
@@ -28,7 +29,23 @@ class ViewsBlock extends ViewsBlockBase {
+        else {
+          $args[] = $argument['exception']['value'];
+        }

This 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.

thenchev’s picture

#59 Fixed

slashrsm’s picture

Version: 8.0.x-dev » 8.1.x-dev
Status: Needs review » Reviewed & tested by the community

8.1.x it is then.

alexpott’s picture

Status: Reviewed & tested by the community » Needs review
+++ b/core/modules/views/src/Plugin/Derivative/ViewsBlock.php
@@ -111,9 +112,33 @@ public function getDerivativeDefinitions($base_plugin_definition) {
+                elseif ($argument['validate']['type'] == 'numeric') {
+                  $this->derivatives[$delta]['context'][$argument_name] = new ContextDefinition('integer', $argument_name, FALSE);
...
+                  case 'numeric':
+                    $this->derivatives[$delta]['context'][$argument_name] = new ContextDefinition('integer', $argument_name, FALSE);

Can 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.

no_angel’s picture

Status: Needs review » Needs work
Berdir’s picture

Status: Needs work » Needs review
FileSize
11.66 KB

Just a reroll for now.

dawehner’s picture

  1. +++ b/core/modules/views/src/Plugin/Derivative/ViewsBlock.php
    @@ -111,9 +112,33 @@ public function getDerivativeDefinitions($base_plugin_definition) {
    +          if ($arguments = $display->getOption('arguments')) {
    +            foreach ($arguments as $argument_name => $argument) {
    +              if (!empty($argument['specify_validation'])) {
    

    Is there a reason why we not use $display->getHandler('arguments')

  2. +++ b/core/modules/views/src/Plugin/Derivative/ViewsBlock.php
    @@ -111,9 +112,33 @@ public function getDerivativeDefinitions($base_plugin_definition) {
    +                  $this->derivatives[$delta]['context'][$argument_name] = new ContextDefinition('integer', $argument_name, FALSE);
    ...
    +                    $this->derivatives[$delta]['context'][$argument_name] = new ContextDefinition('integer', $argument_name, FALSE);
    

    So the second parameter is some form of label. Should we rather use $argument->options['title'] instead?

  3. +++ b/core/modules/views/src/Tests/Plugin/ContextualFiltersBlockContextTest.php
    @@ -0,0 +1,63 @@
    +  public static $modules = array('block',
    +    'block_test_views', 'views_ui', 'node'
    +  );
    

    This looks super weird to be honest :)

  4. +++ b/core/modules/views/src/Tests/Plugin/ContextualFiltersBlockContextTest.php
    @@ -0,0 +1,63 @@
    +  public static $testViews = array('test_view_block3');
    

    Any reason we don't use something descriptive like 'test_view_block_with_context'?

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.0-beta1 was released on March 2, 2016, which means new developments and disruptive changes should now be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

slashrsm’s picture

Addressed #65.

tim.plunkett’s picture

+++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php
@@ -24,19 +24,17 @@ class ViewsBlock extends ViewsBlockBase {
+      if ($argument = $this->view->display_handler->getHandler('argument', $context_name) && $value = $context->getContextValue()) {

Be careful with the assignment here, AFAIK you need to wrap that first one in parens.

slashrsm’s picture

dawehner’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

Is 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.

Be careful with the assignment here, AFAIK you need to wrap that first one in parens.

Tim is right about that, see https://3v4l.org/OdUV8

slashrsm’s picture

Status: Needs work » Needs review
FileSize
15.81 KB
7.65 KB

Added some test coverage.

tim.plunkett’s picture

That interdiff is incomplete. You fixed my point in #68 but it's not in there...
Did that test fail without the fix for #68?

slashrsm’s picture

Your 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.

The last submitted patch, 73: 2287073_73.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 73: 2287073_test_with_pre_69.patch, failed testing.

slashrsm’s picture

Status: Needs work » Needs review

Expected fail.

dawehner’s picture

+++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php
@@ -23,10 +24,24 @@ class ViewsBlock extends ViewsBlockBase {
+    $args = [];
+    foreach ($this->getContexts() as $context_name => $context) {
+      /** @var \Drupal\views\Plugin\views\argument\ArgumentPluginBase $argument */
+      if (($argument = $this->view->display_handler->getHandler('argument', $context_name)) && ($value = $context->getContextValue())) {
+        if ($value instanceof EntityInterface) {
+          $value = $value->id();
+        }
+        $args[] = $value;
+      }
+      elseif ($argument->options['default_action'] == 'ignore') {
+        $args[] = $argument->options['exception']['value'];
+      }
+    }
+

What 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

slashrsm’s picture

Makes sense. Added and updated the test.

dawehner’s picture

Issue tags: -Needs tests

I think we no longer needs additional tests ...

+++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php
@@ -25,16 +25,24 @@ public function build() {
     $args = [];
-    foreach ($this->getContexts() as $context_name => $context) {
+    foreach ($this->view->display_handler->getHandlers('argument') as $argument_name => $argument) {
       /** @var \Drupal\views\Plugin\views\argument\ArgumentPluginBase $argument */
-      if (($argument = $this->view->display_handler->getHandler('argument', $context_name)) && ($value = $context->getContextValue())) {
-        if ($value instanceof EntityInterface) {
-          $value = $value->id();
+      if (!empty($this->context[$argument_name])) {
+        if (($value = $this->context[$argument_name]->getContextValue())) {
+          if ($value instanceof EntityInterface) {
+            $value = $value->id();
+          }
+          $args[] = $value;
+        }
+        elseif ($argument->options['default_action'] == 'ignore') {
+          $args[] = $argument->options['exception']['value'];
+        }
+        else {
+          $args[] = NULL;
         }
-        $args[] = $value;
       }
-      elseif ($argument->options['default_action'] == 'ignore') {
-        $args[] = $argument->options['exception']['value'];
+      else {
+        $args[] = NULL;
       }
     }

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?

slashrsm’s picture

The last submitted patch, 78: 2287073_78.patch, failed testing.

The last submitted patch, 78: 2287073_78_TEST_ONLY.patch, failed testing.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Nice, less crap to maintain

slashrsm’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
16.44 KB
1.75 KB

This should fix the failing test.

The last submitted patch, 80: 2287073_80.patch, failed testing.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Thank you!

jibran’s picture

Some minor issues.

  1. +++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php
    @@ -23,10 +24,28 @@ class ViewsBlock extends ViewsBlockBase {
    +          } elseif ($argument->options['default_action'] == 'ignore') {
    

    CS is wrong here.

  2. +++ b/core/modules/views/src/Plugin/Derivative/ViewsBlock.php
    @@ -106,9 +109,31 @@ public function getDerivativeDefinitions($base_plugin_definition) {
    +                $this->derivatives[$delta]['context'][$argument_name] = new ContextDefinition('integer', $argument->adminLabel(), FALSE);
    ...
    +                  $this->derivatives[$delta]['context'][$argument_name] = new ContextDefinition('integer', $argument->adminLabel(), FALSE);
    

    Maybe combine these two.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
FILE: ...sk/dev/drupal/core/modules/views/src/Plugin/Block/ViewsBlock.php
----------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------
 38 | ERROR | [x] Expected newline after closing brace
----------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------

Time: 37ms; Memory: 5Mb


FILE: ...v/drupal/core/modules/views/src/Plugin/Derivative/ViewsBlock.php
----------------------------------------------------------------------
FOUND 0 ERRORS AND 1 WARNING AFFECTING 1 LINE
----------------------------------------------------------------------
 9 | WARNING | [x] Unused use statement
----------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------

Time: 41ms; Memory: 5.25Mb


FILE: ...les/views/src/Tests/Plugin/ContextualFiltersBlockContextTest.php
----------------------------------------------------------------------
FOUND 0 ERRORS AND 1 WARNING AFFECTING 1 LINE
----------------------------------------------------------------------
 5 | WARNING | [x] Unused use statement
----------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------

Time: 34ms; Memory: 4.75Mb

Other small things to fix.

andypost’s picture

slashrsm’s picture

Agree about #87.2. It doesn't seem that we can easily do that.

andypost’s picture

Status: Needs review » Reviewed & tested by the community

back to rtbc cos there's only cs changes

hkirsman’s picture

I 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.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 89: 2287073-89.patch, failed testing.

slashrsm’s picture

Status: Needs work » Reviewed & tested by the community
aspilicious’s picture

I can confirm this works.
Tested it in combination with #2709235: Block fields don't make use of available contexts

kriboogh’s picture

So I need 83 and 89 to get this working? Can it be applied to 8.1.x ?

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs tests

Blocks with required context will not be visible anymore on the core block layout UI, but they're already not working right now..

I think that this should be tested - there doesn't appear to be a test for this.

Berdir’s picture

We 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?

Berdir’s picture

Issue summary: View changes

Updated 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.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

chx’s picture

While 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.

dawehner’s picture

Category: Bug report » Task

I cannot agree more with #23
@xjm @alexpott @cilefen @tim.plunkett agreed that this is not a bug, but a major task, of course.

alexpott’s picture

Issue tags: +Triaged core major
EclipseGc’s picture

Actually, 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

andypost’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests

What's left here? Tests are here and generic coverage for contexts in core

Status: Needs review » Needs work

The last submitted patch, 89: 2287073-89.patch, failed testing.

Thew’s picture

Status: Needs work » Needs review
FileSize
16.49 KB

Rerolled the patch

Status: Needs review » Needs work

The last submitted patch, 107: 2287073-107.patch, failed testing.

Thew’s picture

Status: Needs work » Needs review
FileSize
16.22 KB

The patch in #107 is wrong.
New rerolled patch file.

Status: Needs review » Needs work

The last submitted patch, 109: 2287073-109.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review
Issue tags: +Needs tests
FileSize
16.22 KB
701 bytes

Thanks for the reroll. updating the test view. This still needs more test coverage.

Berdir’s picture

Some 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.

dawehner’s picture

Status: Needs review » Needs work

So let's be honest the status here :)

Berdir’s picture

Ok, 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.

Berdir’s picture

So, 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.

The last submitted patch, 114: 2287073-114-test-only.patch, failed testing.

andypost’s picture

Having interface is good idea.
But that will require to update all contrib
OTOH that never work before so better to get into 8.3

Berdir’s picture

Contrib isn't required to do anything at all, it *allows* them to do something, before it was impossible .

Berdir’s picture

Some 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.

Berdir’s picture

Status: Needs review » Needs work

2287073-116.patch isn't passing our tests, so it looks like that isn't doing the same, need to check again.

dawehner’s picture

  1. +++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php
    @@ -23,10 +24,31 @@ class ViewsBlock extends ViewsBlockBase {
    +    if ($this->view->display_handler->getHandlers('argument')) {
    

    IMHO we can skip the if. getHandlers will always return an array of values.

  2. +++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php
    @@ -23,10 +24,31 @@ class ViewsBlock extends ViewsBlockBase {
    +          }
    +          elseif ($argument_default = $argument->getPlugin('argument_default')) {
    +            if ($argument_default_value = $argument_default->getArgument()) {
    +              $args[$argument_name] = $argument_default->getArgument();
    +            }
    

    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.

  3. +++ b/core/modules/views/src/Plugin/views/argument_validator/NumericArgumentValidator.php
    @@ -12,10 +15,17 @@
    +   * {@inheritdoc}
    +   */
    +  public function getContextDefinition() {
    +    return new ContextDefinition('integer', $this->argument->adminLabel(), FALSE);
    +  }
    +
     }
    

    It is a bit confusing to be honest that we don't have similar kind of code for strings etc ...

Berdir’s picture

Ok, 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.

Berdir’s picture

As discussed, removing the interface again. Previous patch passed our distribution tests.

The last submitted patch, 122: 2287073-121-test-only.patch, failed testing.

The last submitted patch, 122: 2287073-121.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 123: 2287073-123.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review
FileSize
28.23 KB
752 bytes

Ah, that's why the if was there, but lets fix this in the mock instead..

dawehner’s picture

Status: Needs review » Reviewed & tested by the community
+++ b/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php
@@ -1326,6 +1326,17 @@ public function calculateDependencies() {
+   * Returns a context definition for this argument.
+   *
+   * @return \Drupal\Core\Plugin\Context\ContextDefinitionInterface|null
+   *   A context definition that represents the argument or NULL if that is
+   *   not possible.
+   */
+  public function getContextDefinition() {
+    return $this->getPlugin('argument_validator')->getContextDefinition();
+  }

I'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 :)

EclipseGc’s picture

FileSize
1002 bytes

Ok, I've looked through this a bit and REALLY like what's sitting here. (For whatever that's worth).

A few quick validating questions:

  1. +++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php
    @@ -23,10 +24,28 @@ class ViewsBlock extends ViewsBlockBase {
    +      if (!empty($this->context[$argument_name])) {
    +        if ($value = $this->context[$argument_name]->getContextValue()) {
    +          if ($value instanceof EntityInterface) {
    +            $value = $value->id();
    +          }
    +          $args[$argument_name] = $value;
    +        }
    +      }
    

    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.

  2. +++ b/core/modules/views/src/Plugin/views/argument/NumericArgument.php
    @@ -124,4 +125,17 @@ public function getSortName() {
    +    if ($context_definition = parent::getContextDefinition()) {
    +      return $context_definition;
    +    }
    +
    +    // If the parent does not provide a context definition through the
    +    // validation plugin, fall back to the integer type.
    +    return new ContextDefinition('integer', $this->adminLabel(), FALSE);
    

    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, ++

  3. +++ b/core/modules/views/src/Plugin/views/argument_validator/Entity.php
    @@ -228,4 +229,11 @@ public function calculateDependencies() {
    +    return new ContextDefinition('entity:' . $this->definition['entity_type'], $this->argument->adminLabel(), FALSE);
    

    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

Berdir’s picture

I 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.

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, ++

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.

EclipseGc’s picture

Ok, 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

EclipseGc’s picture

What 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

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 127: 2287073-127.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review
FileSize
28.22 KB

Updated 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.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Back to RTBC

Berdir’s picture

Issue summary: View changes

Removed resolved remaining tasks and created a change record: [#2846863]

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 134: 2287073-134.patch, failed testing.

Berdir’s picture

That 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.

andypost’s picture

+++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php
@@ -23,10 +24,28 @@ class ViewsBlock extends ViewsBlockBase {
+        if ($value = $this->context[$argument_name]->getContextValue()) {
+          if ($value instanceof EntityInterface) {
+            $value = $value->id();

I think it needs a comment about why there's EntityInterface instead of is_scalar() for example?

jibran’s picture

+++ b/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php
@@ -1326,6 +1326,17 @@ public function calculateDependencies() {
+  public function getContextDefinition() {

+++ b/core/modules/views/src/Plugin/views/argument_validator/ArgumentValidatorPluginBase.php
@@ -104,6 +104,15 @@ public function validateArgument($arg) { return TRUE; }
+  public function getContextDefinition() { }

Shouldn't we be using some kind of interface?

Berdir’s picture

#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.

andypost’s picture

@Berdir please add this comment, it explains the check for EI, it makes clear what context value expected by views

Berdir’s picture

Added that comment.

dawehner’s picture

+++ b/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php
@@ -105,7 +105,7 @@ public function exceptionTitle() {
-   * @return TRUE/FALSE
+   * @return TRUE or FALSE

Just use @return bool

jibran’s picture

Here we go.

Berdir’s picture

+++ b/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php
@@ -105,7 +105,7 @@ public function exceptionTitle() {
    *
-   * @return TRUE/FALSE
+   * @return bool
    */

This is completely unrelated to this patch and was not changed previously?

Berdir’s picture

alexpott’s picture

+++ b/core/modules/views/src/Plugin/Derivative/ViewsBlock.php
@@ -106,9 +107,18 @@ public function getDerivativeDefinitions($base_plugin_definition) {
+          foreach ($display->getHandlers('argument') as $argument_name => $argument) {
+            /** @var \Drupal\views\Plugin\views\argument\ArgumentPluginBase $argument */
+            if ($context_definition = $argument->getContextDefinition()) {

+++ b/core/modules/views/src/Plugin/views/argument/StringArgument.php
--- a/core/modules/views/src/Plugin/views/argument_validator/ArgumentValidatorPluginBase.php
+++ b/core/modules/views/src/Plugin/views/argument_validator/ArgumentValidatorPluginBase.php

As 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?

jibran’s picture

How can we tackle multiple values arguments? AFAIK argument_validator doesn't play nice with multiple values.

jibran’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php
    @@ -1326,6 +1326,17 @@ public function calculateDependencies() {
    +  public function getContextDefinition() {
    
    +++ b/core/modules/views/src/Plugin/views/argument_validator/ArgumentValidatorPluginBase.php
    @@ -104,6 +104,15 @@ public function validateArgument($arg) { return TRUE; }
    +  public function getContextDefinition() { }
    

    These both should have @see to each other.

  2. +++ b/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php
    @@ -1326,6 +1326,17 @@ public function calculateDependencies() {
    +    return $this->getPlugin('argument_validator')->getContextDefinition();
    

    I think argument_default plugin should also set the context.

Berdir’s picture

Status: Needs work » Needs review

I 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.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

jibran’s picture

Status: Needs review » Reviewed & tested by the community

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.

Fair point let's discuss that in follow-up and not a using doesn't imply that no one will use it. ;-)

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

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.

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.

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.

jibran’s picture

Title: Expose contextual filters of views blocks as context » Allow views contextual filters to expose the context using argument validation plugins

After 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.

xjm’s picture

Just 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.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: +8.3.0 release notes

Committed 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.

  • alexpott committed 73312ef on 8.4.x
    Issue #2287073 by Berdir, slashrsm, Denchev, jibran, Thew, dasjo,...

  • alexpott committed 3ed4f6e on 8.3.x
    Issue #2287073 by Berdir, slashrsm, Denchev, jibran, Thew, dasjo,...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.