Problem/Motivation

ContextHandler::getMatchingContexts() is used to find which context objects match the needs of a given plugin.

Part of this filtering process includes checking the constraints of the available contexts, and comparing them to the any constraints on the definitions specified by the plugin.

For example, a Block plugins can specify that it requires an Article node.

If one of the available context objects provides an Article node, then it will be included in the result.
If another available context object provides a Basic Page node, it will be excluded.


Scenario:
A Block plugin specifies that it will accept a either a Book node or an Article node.
An available context object provides an Article node.
ContextHandler::getMatchingContexts() is called

Expectation:
The result will include the available context object, as it satisfies the requirements of the plugin

Result:
The result is empty, because the available context object was not both a Book and an Article

This is due to the following line:

if ($context_definition->getConstraint($constraint_name) != $constraint) {
  return FALSE;
}

An additional problem:
Constraints expect to evaluate against a corresponding data object. In the case of bundles (and a few other constraint validators) that would be an entity. The context system can be functional before we have a given entity (say during administration), so constraints need to be validated against contextual metadata. If a context is available that documents it is a member of a particular bundle, our validators should be able to use that information.

Proposed resolution

Fix the ContextHandler to delegate Constraint validation to the validation system and allow constraints to be directly validated against a real or generated data object.

Remaining tasks

TBD

User interface changes

None

API changes

API addition to ContextDefinitionInterface, but with a 1:1 addition to ContextDefinition, so no BC issue

Data model changes

None

CommentFileSizeAuthor
#90 2671964-context-90.patch28.68 KBtim.plunkett
#90 2671964-context-90-interdiff.txt1.14 KBtim.plunkett
#82 2671964-context-82.patch28.56 KBtim.plunkett
#82 2671964-context-82-interdiff.txt1.22 KBtim.plunkett
#79 ContextDefinitionInterface.png35.66 KBlarowlan
#68 2671964-context-68.patch28.56 KBtim.plunkett
#68 2671964-context-68-interdiff.txt1.65 KBtim.plunkett
#66 2671964-context-66.patch28.32 KBtim.plunkett
#66 2671964-context-66-interdiff.txt6.45 KBtim.plunkett
#65 2671964.interdiff.txt1.49 KBeclipsegc
#65 2671964-65.patch27.62 KBeclipsegc
#64 2671964.interdiff.txt751 byteseclipsegc
#64 2671964-64.patch26.99 KBeclipsegc
#61 2671964-61.patch26.87 KBeclipsegc
#61 2671964-61.interdiff.txt1.49 KBeclipsegc
#59 2671964-context-59.patch26.79 KBtim.plunkett
#59 2671964-context-59-interdiff.txt4.49 KBtim.plunkett
#55 2671964-context-55.patch26.82 KBtim.plunkett
#55 2671964-context-55-interdiff.txt6.79 KBtim.plunkett
#54 2671964-context-54.patch26.42 KBtim.plunkett
#52 2671964-52.patch16.49 KBeclipsegc
#48 interdiff-46-48.txt2.64 KBjofitz
#48 2671964-48.patch25.95 KBjofitz
#46 2671964-46.interdiff.txt25.13 KBeclipsegc
#46 2671964-46.patch25.89 KBeclipsegc
#43 2671964-43.patch22.57 KBjofitz
#43 interdiff-41-43.txt1.66 KBjofitz
#41 2671964-41.patch22.67 KBjofitz
#34 2671964-34.patch23.46 KBeclipsegc
#33 2671964.interdiff.txt1.4 KBeclipsegc
#33 2671964-33.patch24.03 KBeclipsegc
#31 2671964.interdiff.txt898 byteseclipsegc
#31 2671964-31.patch23.72 KBeclipsegc
#30 2671964.interdiff.txt7.62 KBeclipsegc
#30 2671964-30.patch23.93 KBeclipsegc
#20 2671964-20.FAIL_.patch1.52 KBeclipsegc
#17 2671964.interdiff.txt8.03 KBeclipsegc
#17 2671964-17.patch24.28 KBeclipsegc
#16 2671964.interdiff.txt4.43 KBeclipsegc
#16 2671964-16.patch24.03 KBeclipsegc
#11 2671964-11.patch24.35 KBeclipsegc
#6 2671964-6.patch7.41 KBeclipsegc
#3 2671964-3.patch2.9 KBeclipsegc

Comments

EclipseGc created an issue. See original summary.

eclipsegc’s picture

Issue summary: View changes

Sooo I investigated being able to validate directly against other definitions, unfortunately the RecursiveContextualValidator::validateNode() makes a direct call to TypedDataInterface::getCanonicalRepresentation() which it then passes the result of as what we're validating from that point forward. This means that I'm not going to be able to reliable use the validator without inventing the data type generator stuff I suggested as the other potential possibility... YUCK.

Eclipse

eclipsegc’s picture

StatusFileSize
new2.9 KB

Ok, so after a bit more digging, TypedDataManager::getCanonicalRepresentation only attempts to get the value if the data definition specifies that things be unwrapped. All the data types I looked at don't specify this, which means they unwrap by default, thus value, so I've introduced a new data type that does not unwrap for canonical representation, and that means our data type itself is passed to the constraint validators. This is great because it means that we can type check the passed object and validate for both the object or a representation of the object. I'm not sure where to put the data type right now and I have overridden the Bundle, EntityChanged and EntityType validators. I'll provide all that code shortly, but I wanted to put the ContextHandler changes up first just for sanity.

Eclipse

imalabya’s picture

Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, 3: 2671964-3.patch, failed testing.

eclipsegc’s picture

Status: Needs work » Needs review
StatusFileSize
new7.41 KB

This patch introduces a new data type plugin which will not unwrap for validation. Updated some of the entity constraint validators to compensate properly for this. Others may need updating. Let's see what testbot says.

Eclipse

Status: Needs review » Needs work

The last submitted patch, 6: 2671964-6.patch, failed testing.

tim.plunkett’s picture

Issue tags: +Needs tests

This really needs tests to prove the bug.

eclipsegc’s picture

Yeah, I'm working on tests now. Unfortunately the existing test make up non-existent constraints, so it's not immediately clear how things break unless you're familiar with how constraints are intended to be used. We weren't when we were working on the ContextHandler. :-(

I'm working on tests now.

Eclipse

dsnopek’s picture

EclipseGC explained this to me on IRC, and I sort of get it. But the issue summary here is really difficult for me to parse, and I think should be updated to describe the problem more clearly.

eclipsegc’s picture

Status: Needs work » Needs review
Issue tags: +Contributed project blocker
StatusFileSize
new24.35 KB

Ok, let's see how testbot likes this!

Eclipse

eclipsegc’s picture

Issue summary: View changes
eclipsegc’s picture

Issue summary: View changes
tim.plunkett’s picture

  1. +++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
    @@ -29,124 +31,92 @@ class ContextHandlerTest extends UnitTestCase {
    -    $data[] = array(array(), array(), TRUE);
    -    $data[] = array(array(), array($requirement_any), FALSE);
    -    $data[] = array(array(), array($requirement_optional), TRUE);
    -    $data[] = array(array(), array($requirement_any, $requirement_optional), FALSE);
    -    $data[] = array(array($context_any), array($requirement_any), TRUE);
    -    $data[] = array(array($context_constraint_mismatch), array($requirement_specific), FALSE);
    -    $data[] = array(array($context_datatype_mismatch), array($requirement_specific), FALSE);
    -    $data[] = array(array($context_specific), array($requirement_specific), TRUE);
    ...
    +    $this->assertTrue($this->contextHandler->checkRequirements([], []));
    +    $this->assertFalse($this->contextHandler->checkRequirements([], [$requirement_any]));
    +    $this->assertTrue($this->contextHandler->checkRequirements([], [$requirement_optional]));
    +    $this->assertFalse($this->contextHandler->checkRequirements([], [$requirement_any, $requirement_optional]));
    +    $this->assertTrue($this->contextHandler->checkRequirements([$context_any], [$requirement_any]));
    +    $this->assertFalse($this->contextHandler->checkRequirements([$context_constraint_mismatch], [$requirement_specific]));
    +    $this->assertFalse($this->contextHandler->checkRequirements([$context_datatype_mismatch], [$requirement_specific]));
    +    $this->assertTrue($this->contextHandler->checkRequirements([$context_specific], [$requirement_specific]));
    +    $this->assertTrue($this->contextHandler->checkRequirements([$context_specific], [$requirement_complex]));
    +    $this->assertTrue($this->contextHandler->checkRequirements([$context_constraint_mismatch], [$requirement_complex]));
    

    If this were a new test, I would say "this should use a @dataProvider". But you're removing one instead! Why?

  2. +++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
    @@ -29,124 +31,92 @@ class ContextHandlerTest extends UnitTestCase {
    -    $data = array();
    -    // No context will return no valid contexts.
    -    $data[] = array(array(), $requirement_any);
    -    // A context with a generic matching requirement is valid.
    -    $data[] = array(array($context_any), $requirement_any);
    -    // A context with a specific matching requirement is valid.
    -    $data[] = array(array($context_specific), $requirement_specific);
    -
    -    // A context with a mismatched constraint is invalid.
    -    $data[] = array(array($context_constraint_mismatch), $requirement_specific, array());
    -    // A context with a mismatched datatype is invalid.
    -    $data[] = array(array($context_datatype_mismatch), $requirement_specific, array());
    ...
    +    $this->assertEquals([], $this->contextHandler->getMatchingContexts([], $requirement_any));
    +    $this->assertEquals([$context_any], $this->contextHandler->getMatchingContexts([$context_any], $requirement_any));
    +    $this->assertEquals([$context_specific], $this->contextHandler->getMatchingContexts([$context_specific], $requirement_specific));
    +    $this->assertEquals([], $this->contextHandler->getMatchingContexts([$context_constraint_mismatch], $requirement_specific));
    +    $this->assertEquals([], $this->contextHandler->getMatchingContexts([$context_datatype_mismatch], $requirement_specific));
    

    Same here!

eclipsegc’s picture

It was easier for me to read, I figured it would be for others too. I'm happy to go back to one, but a big array of arrays is kinda hard to read. That's all.

Eclipse

eclipsegc’s picture

StatusFileSize
new24.03 KB
new4.43 KB

Switched it back.

Eclipse

eclipsegc’s picture

StatusFileSize
new24.28 KB
new8.03 KB

Ok, added docs to all the things, tried to not rewrite all the methods and parameters in the test methods. Should be a little cleaner.

Eclipse

eclipsegc’s picture

tim.plunkett’s picture

  1. +++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
    @@ -56,40 +71,33 @@ public function providerTestCheckRequirements() {
    -    $data[] = array(array(), array(), TRUE);
    ...
    +    $data[] = [[], [], TRUE];
    
    @@ -111,41 +119,28 @@ public function testGetMatchingContexts($contexts, $requirement, $expected = NUL
    -    $data[] = array(array(), $requirement_any);
    ...
    +    $data[] = [[], $requirement_any];
    

    Sure, we all love the new array syntax. But now I can't tell what you're changing here. Or in this entire test.

  2. +++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
    @@ -174,56 +157,52 @@ public function testFilterPluginDefinitionsByContexts($has_context, $definitions
         // No context and no plugins, no plugins available.
    -    $data[] = array(FALSE, $plugins, array());
    -
    -    $plugins = array('expected_plugin' => array());
    -    // No context, all plugins available.
    -    $data[] = array(FALSE, $plugins, $plugins);
    -
    -    $plugins = array('expected_plugin' => array('context' => array()));
    -    // No context, all plugins available.
    -    $data[] = array(FALSE, $plugins, $plugins);
    -
    -    $plugins = array('expected_plugin' => array('context' => array('context1' => new ContextDefinition('expected_data_type'))));
    -    // Missing context, no plugins available.
    -    $data[] = array(FALSE, $plugins, array());
    -    // Satisfied context, all plugins available.
    -    $data[] = array(TRUE, $plugins, $plugins);
    -
    -    $mismatched_context_definition = (new ContextDefinition('expected_data_type'))->setConstraints(array('mismatched_constraint_name' => 'mismatched_constraint_value'));
    -    $plugins = array('expected_plugin' => array('context' => array('context1' => $mismatched_context_definition)));
    -    // Mismatched constraints, no plugins available.
    -    $data[] = array(TRUE, $plugins, array());
    -
    -    $optional_mismatched_context_definition = clone $mismatched_context_definition;
    -    $optional_mismatched_context_definition->setRequired(FALSE);
    -    $plugins = array('expected_plugin' => array('context' => array('context1' => $optional_mismatched_context_definition)));
    -    // Optional mismatched constraint, all plugins available.
    -    $data[] = array(FALSE, $plugins, $plugins);
    -
    -    $expected_context_definition = (new ContextDefinition('expected_data_type'))->setConstraints(array('expected_constraint_name' => 'expected_constraint_value'));
    -    $plugins = array('expected_plugin' => array('context' => array('context1' => $expected_context_definition)));
    -    // Satisfied context with constraint, all plugins available.
    -    $data[] = array(TRUE, $plugins, $plugins);
    -
    -    $optional_expected_context_definition = clone $expected_context_definition;
    -    $optional_expected_context_definition->setRequired(FALSE);
    -    $plugins = array('expected_plugin' => array('context' => array('context1' => $optional_expected_context_definition)));
    -    // Optional unsatisfied context, all plugins available.
    -    $data[] = array(FALSE, $plugins, $plugins);
    -
    -    $unexpected_context_definition = (new ContextDefinition('unexpected_data_type'))->setConstraints(array('mismatched_constraint_name' => 'mismatched_constraint_value'));
    -    $plugins = array(
    -      'unexpected_plugin' => array('context' => array('context1' => $unexpected_context_definition)),
    -      'expected_plugin' => array('context' => array('context2' => new ContextDefinition('expected_data_type'))),
    -    );
    -    // Context only satisfies one plugin.
    -    $data[] = array(TRUE, $plugins, array('expected_plugin' => $plugins['expected_plugin']));
    

    All of this can't be 100% wrong...

eclipsegc’s picture

StatusFileSize
new1.52 KB

Sure, it wasn't wrong for the old ContextHandler, but the new class is actually going to delegate validation of constraints to the actual system that does that job, so made up constraints won't work any longer, and since the specific problem I wanted coverage for happens within entity bundles, I had to convert to real data types as well, so "wrong" is a strong word, but it's not going to work in the new version of the class appropriately.

I've included a FAIL version of the test against the old ContextHandler just to illustrate the sort of thing that SHOULD work but currently doesn't.

Eclipse

Status: Needs review » Needs work

The last submitted patch, 20: 2671964-20.FAIL_.patch, failed testing.

eclipsegc’s picture

Status: Needs work » Needs review
fago’s picture

This is all done via the constraint system, however the ContextHandler essentially expects the contexts we have and the required contexts on a plugin to be equal.

Yeah, this needs to do proper matching. We are working on having a type-inheritance systems for that in Rules.

Constraints expect to evaluate against the corresponding data object. In the case of bundles (and a few other constraint validators) that would be an entity. The context system can be functional before we have a given entity (say during administration), so constraints need to be validated against contextual metadata.

While the premise is correct, I don't think the conclusion is. Constraints are for validating data, yes. When you need to check whether some context definition matches with some data definition that's something different - I don't think it makes sense to use the constraints which already have the logic for checking the data, for checking the metadata also.

I think the case of bundles can be covered perfectly fine with a data type based matching system, e.g. entity:node:page matches entity:node. Regarding, the more complex case you suggested, having a context plugin that requires a node with a certain field X: I'm not convinced we need to have support for that, e.g. Rules never was able to express that, still the matching was powerful enough in D7.
However, one thing that we had in d7 which we are not use it's required to bring back in D8 is support for having a contextual plugin with a context that must be of a list of allowed types, e.g. entity:node:page OR entity:node:article. Instead of that, constraints might be a good way to express that as you can specify that you require a node with the constraints of having either an article or page bundle. However, the specified constraints are metadata, so I could see us making use of them for matching metadata in combination with metadata assertions. Metadata assertions, are what Rules makes for pre-computing the available context: e.g. when you have a node type condition checking for a page, it can provide the assertion that after its execution the passed context is not only of type entity:node but also has the constraint "Bundle is page" fulfilled. Given we have that in place (we are working on assertions still) we could leverage that for context-fullfillment-checking by matching the asserted constraints with the needed constraints.

Thus, we'd not execute the constraints at configuration time, we'd only match the arrays of known, asserted constraints with the array of required constraints. Would that meet your use-case also?

@core-context-handler:
We actually do not use that, it has not much logic we could re-use, so it was simpler to have our own code for mapping context for now. FYI: It's no service, but a (re-usable) trait: https://github.com/fago/rules/blob/8.x-3.x/src/Context/ContextHandlerTra... Would be good if we could merge those approaches in the long run though.

fago’s picture

Besides, I've troubles finding issues related to the Context System in core. Should we start using some tag at least to make those issue findable? Having a component would probably be better, but that doesn't match the code-structure. (Would have been nice there as well.)

eclipsegc’s picture

No, CTools could not use that. The above code actually fulfills the exact situation you laid out as "missing" and will allow a context of type entity:node of bundle page or article to match on a given plugin. CTools does do the exact field example I gave, we in fact already have code for it right now and just didn't do the constraints properly because of this bug, which could lead to some runtime nastiness, which core only saves us from because it's block placement is so naive. A PageManager or Panelizer based block placement system needs to be a lot less naive, and we have the code to support that, if only the context handler is imbued with this logic.

I don't think updating a handful of constraint validators to respond appropriately to metadata is onerous. The diffs in the patch show that this is pretty straight forward and it wouldn't actually affect the runtime evaluations against real objects since we're doing an instanceof check against the value to evaluate. I'm sorry to hear that Rules doesn't make use of this class at all, but CTools (and core) make use of it pretty regularly, and it really needs to do proper constraint checking for that. I really think this is a big step in the right direction, and apparently won't adversely affect Rules. I'd love to get this class to a point that you all feel you can depend on it. Perhaps we could have a dedicated separate conversation about that? I'd like that very much.

Eclipse

eclipsegc’s picture

Oh, also, on the topic of the entity:node:page level validation. I don't really see how this is possible. I can't attach multiple requirements to a plugin. The plugin requires 'entity:node' of bundles page or article, not entity:node:page || entity:node:article with some special handling. And with the approach in this patch, it's future proof against contrib constraints, instead of special casing the constraint we use most often with custom logic.

Eclipse

eclipsegc’s picture

Issue tags: +Plugin Context

Ok, made up a new tag. :-)

Eclipse

dawehner’s picture

At that point there hasn't been too much discussion, basically fago made a comment and disagreed with the idea.
In general it seems to be that we are extending the behaviour of the context handler, so I'm wondering whether a class ContextHandlerWithValidationConstraints would be a usable alternative.

  1. +++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityChangedConstraintValidator.php
    @@ -19,7 +20,8 @@ class EntityChangedConstraintValidator extends ConstraintValidator {
           /** @var \Drupal\Core\Entity\EntityInterface $entity */
    

    This comment can be dropped now.

  2. +++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php
    @@ -23,6 +24,15 @@ public function validate($entity, Constraint $constraint) {
    +      if ($entity->getDataDefinition()->getConstraint('EntityType') != $constraint->type) {
    

    This doesn't make sense for me. \Drupal\Core\TypedData\DataDefinitionInterface::getConstraint documents to return a array (the constraint configuration) and $constraint->type is a string: \Drupal\Core\Entity\Plugin\Validation\Constraint\EntityTypeConstraint::$type

  3. +++ b/core/lib/Drupal/Core/Plugin/Plugin/DataType/DataTypeWrapper.php
    @@ -0,0 +1,31 @@
    + * Contains \Drupal\ctools\Plugin\DataType\DataTypeWrapper.
    

    points to ctools still

  4. +++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
    @@ -56,40 +71,33 @@ public function providerTestCheckRequirements() {
    +    $context_any = new Context(new ContextDefinition('any'));
    

    +1 to not mock in a provider

  5. +++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
    @@ -56,40 +71,33 @@ public function providerTestCheckRequirements() {
    +    $data = [];
    +    $data[] = [[], [], TRUE];
    +    $data[] = [[], [$requirement_any], FALSE];
    +    $data[] = [[], [$requirement_optional], TRUE];
    +    $data[] = [[], [$requirement_any, $requirement_optional], FALSE];
    +    $data[] = [[$context_any], [$requirement_any], TRUE];
    +    $data[] = [[$context_constraint_mismatch], [$requirement_specific], FALSE];
    +    $data[] = [[$context_datatype_mismatch], [$requirement_specific], FALSE];
    +    $data[] = [[$context_specific], [$requirement_specific], TRUE];
    +    $data[] = [[$context_specific], [$requirement_complex], TRUE];
    +    $data[] = [[$context_constraint_mismatch], [$requirement_complex], TRUE];
    

    I agree with tim, let's not convert everything. Why? It moves focus of reviews from fundamental reviews to codestyle only.

lokapujya’s picture

+++ b/core/lib/Drupal/Core/Plugin/Plugin/DataType/DataTypeWrapper.php
@@ -0,0 +1,31 @@
+ * other when a raw data object is not available to validated against.

validated=>validate (be validated)

eclipsegc’s picture

StatusFileSize
new23.93 KB
new7.62 KB

28.1.) OK
28.2.) Yes the return value says that, but the EntityType constraint returns a string. I've stared at it in a debugger a lot, and just to confirm that I wasn't crazy went ahead and did so again just now. So either EntityType constraint plugin is wrong, or the return docs should say mixed.
28.3.) fixed
28.4.) Still learning these provider things
28.5.) Well I converted it because I rewrote it all. With the last two sets of changes to undo that though, sure. That being said, I completely rewrote providerTestFilterPluginDefinitionsByContexts() to be a more realistic test with a full list of plugins and a more nuanced set of contexts that will exercise exactly the problem that this issue is trying to address, so I did not return it to any former state since there is nothing the same about it.

29.1.) fixed

Eclipse

eclipsegc’s picture

StatusFileSize
new23.72 KB
new898 bytes

Fixed another unnecessary change.

Eclipse

fago’s picture

Status: Needs review » Needs work

I really think this is a big step in the right direction, and apparently won't adversely affect Rules

Right, still we should try to do not fork the way context is handled needlessly and use the same way of matching context as far as possible. Such that the context system works the same way for users/devs in both cases + we have the chance to unify code in future.

Oh, also, on the topic of the entity:node:page level validation. I don't really see how this is possible. I can't attach multiple requirements to a plugin. The plugin requires 'entity:node' of bundles page or article, not entity:node:page || entity:node:article with some special handling.

Right. That's what I'm saying that we do not support right now and I'm fine with, in particular if we can get to "the plugin requires 'entity:node' of bundles page or article".

I don't think updating a handful of constraint validators to respond appropriately to metadata is onerous.

If the approach taken does not work with a general symfony validation constraint, then it looks like mis-use of the validation API to me. It does not make much sense to use symfony validation constraints for contexts IF you have to modify them in some special way to make it work imo.

Afaics, my suggestions would achieve the same without the need for extending symfony validation constraints. Let's discuss the idea to see whether it can meet your requirements also.

+    // Validate against wrapper bundle constraint when necessary.
+    if ($entity instanceof DataTypeWrapper) {
+      $bundles = $entity->getDataDefinition()->getConstraint('Bundle');

Seeing that in the validator is quite a no-go imo, as we did quite some work to make coding validators so easy that you know that there is NO wrapped object going to be passed to you. We should not go back to a situation where sometimes wrapped objects are passed and sometimes not.

eclipsegc’s picture

Status: Needs work » Needs review
StatusFileSize
new24.03 KB
new1.4 KB

Core's Context class actually does the typed data work for us when there's a value, so I'm handing off to that functionality instead. It seems worth looking at that class further to see if the data_type_wrapper instantiation could be added there instead. I'll look at this as I continue working to make this simpler.

Eclipse

eclipsegc’s picture

StatusFileSize
new23.46 KB

Oops, unrelated crap in the diff.

Eclipse

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.

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.

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.

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

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

jhedstrom’s picture

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

Patch no longer applies.

jofitz’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new22.67 KB

Re-rolled.

Status: Needs review » Needs work

The last submitted patch, 41: 2671964-41.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

jofitz’s picture

Status: Needs work » Needs review
StatusFileSize
new1.66 KB
new22.57 KB

Addressed test failures.
Corrected coding standards errors.

Status: Needs review » Needs work

The last submitted patch, 43: 2671964-43.patch, failed testing. View results

jofitz’s picture

Unable to replicate the test failures on my local build.

eclipsegc’s picture

Issue summary: View changes
Status: Needs work » Needs review
StatusFileSize
new25.89 KB
new25.13 KB

Ok, so this has been a long time coming on my part, but after a lot of soul searching and even getting another patch into core, I think I have a solution that should satisfy all involved.

The entity system recently got the ability to generate sample entities for given bundles. This new patch undoes all the changes to all constraint validators and removes the new Data Type object I previously added. Instead we generate sample entity or an EntityAdapter in the degenerate case of not knowing or needing a bundle. This way we can pass an object that will properly validate with the constraint validators. These are essentially run time entities which are never saved or used except to determine if the provided context satisfies available plugins. In addition to this, the ContextHandler identifies and generated TypedData objects as necessary for non-entity contexts as well, so things like strings or other data type primitives should all work as expected.

Eclipse

phenaproxima’s picture

I ain't qualified to RTBC this, but honestly, it makes a lot of sense to me. I don't really see anything wrong this patch.

  1. +++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
    @@ -118,4 +165,64 @@ public function applyContextMapping(ContextAwarePluginInterface $plugin, $contex
    +    if (strpos($definition->getDataType(), 'entity:') === 0 && !in_array('EntityType', array_keys($definition->getConstraints()))) {
    

    Micro-optimization: rather than call array_keys() here, we could do:
    !array_key_exists('EntityType, $definition->getConstraints())

  2. +++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
    @@ -118,4 +165,64 @@ public function applyContextMapping(ContextAwarePluginInterface $plugin, $contex
    +    foreach ($definition->getConstraints() as $constraint_name => $constraint_settings) {
    

    Another micro-optimization: can we call $definition->getConstraints() once and reuse the return value?

  3. +++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
    @@ -118,4 +165,64 @@ public function applyContextMapping(ContextAwarePluginInterface $plugin, $contex
    +    return $this->typedDataManager->createInstance($context->getContextDefinition()->getDataType());
    

    Once again, let's call $context->getContextDefinition() once and use that value.

jofitz’s picture

StatusFileSize
new25.95 KB
new2.64 KB

Applied @phenaproxima's micro-optimisations.

tim.plunkett’s picture

Points 1-3 all need test coverage, we had 100% before and I think its worth the extra effort here.

  1. +++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
    @@ -118,4 +165,66 @@ public function applyContextMapping(ContextAwarePluginInterface $plugin, $contex
    +    if ($context->hasContextValue()) {
    +      return $context->getContextData();
    +    }
    

    This

  2. +++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
    @@ -118,4 +165,66 @@ public function applyContextMapping(ContextAwarePluginInterface $plugin, $contex
    +    if (!empty($constraints['EntityType']) && $constraints['EntityType'] instanceof EntityTypeConstraint) {
    ...
    +    // No entity related constraints, so generate a basic typed data object.
    +    return $this->typedDataManager->createInstance($context_definition->getDataType());
    

    The `else` case of this, aka the final return statement

  3. +++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
    @@ -118,4 +165,66 @@ public function applyContextMapping(ContextAwarePluginInterface $plugin, $contex
    +      if ($storage instanceof ContentEntityStorageInterface) {
    

    The `else` case of this

  4. +++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
    @@ -1,26 +1,23 @@
    +class ContextHandlerTest extends KernelTestBase {
    +  use ContentTypeCreationTrait;
    

    Nit: empty line between these, please

  5. +++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
    @@ -30,202 +27,178 @@ class ContextHandlerTest extends UnitTestCase {
    +  public static $modules = ['user', 'system', 'node', 'field', 'text', 'filter'];
    ...
    +    $this->installEntitySchema('user');
    +    $this->installEntitySchema('node_type');
    +    $this->installEntitySchema('node');
    +    $this->installConfig('node');
    

    This could all be done for you if the class extended \Drupal\KernelTests\Core\Entity\EntityKernelTestBase

    I think you'd just need to keep `node` in the $modules line

  6. +++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
    @@ -30,202 +27,178 @@ class ContextHandlerTest extends UnitTestCase {
    +    $this->assertSame(FALSE, $this->contextHandler->checkRequirements([], [$requirement_any]));
    

    Nit: assertFalse

  7. +++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
    @@ -30,202 +27,178 @@ class ContextHandlerTest extends UnitTestCase {
    +    $this->assertSame(TRUE, $this->contextHandler->checkRequirements([], [$requirement_optional]));
    

    Okay not gonna highlight all these

  8. +++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
    @@ -30,202 +27,178 @@ class ContextHandlerTest extends UnitTestCase {
    +    $this->assertSame(FALSE, $this->contextHandler->checkRequirements([], [$requirement_any, $requirement_optional]));
    

    Wait why can't this go back to being a dataProvider? Those work for KTB too

  9. +++ b/core/tests/Drupal/Tests/Core/Plugin/ContextHandlerTest.php
    @@ -30,202 +27,178 @@ class ContextHandlerTest extends UnitTestCase {
        * @covers ::getMatchingContexts
    ...
    +  public function testGetMatchingContexts() {
    

    The @covers need to be updated to include the new protected methods

  10. +++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
    @@ -51,11 +96,13 @@ public function getMatchingContexts(array $contexts, ContextDefinitionInterface
    +        $violations = $validator->validate($typed_data_definition, array_values($this->getConstraintsFromContextDefinition($definition)));
    
    @@ -118,4 +165,66 @@ public function applyContextMapping(ContextAwarePluginInterface $plugin, $contex
    +    $constraints = $this->getConstraintsFromContextDefinition($context_definition);
    

    I notice we call getConstraintsFromContextDefinition() twice in the same code path. Why not pass the result of the first call into getTypedDataFromContext() to avoid the need for the second one?

The entity-specfic-ness of this code worries me.
Why is ContextHandler left holding the bag on so much of this?
Why are getConstraintsFromContextDefinition() and getTypedDataFromContext() part of this service?
Is there a need for other handling similar to this, for non-entity stuff?

tim.plunkett’s picture

Issue summary: View changes

Tried to rewrite the IS

tim.plunkett’s picture

I think ideally, all of this would be a new service, and the changes to ContextHandler would consist entirely of this:

    */
   public function getMatchingContexts(array $contexts, ContextDefinitionInterface $definition) {
     return array_filter($contexts, function (ContextInterface $context) use ($definition) {
-      $context_definition = $context->getContextDefinition();
-
-      // If the data types do not match, this context is invalid unless the
-      // expected data type is any, which means all data types are supported.
-      if ($definition->getDataType() != 'any' && $definition->getDataType() != $context_definition->getDataType()) {
-        return FALSE;
-      }
-
-      // If any constraint does not match, this context is invalid.
-      foreach ($definition->getConstraints() as $constraint_name => $constraint) {
-        if ($context_definition->getConstraint($constraint_name) != $constraint) {
-          return FALSE;
-        }
-      }
-
-      // All contexts with matching data type and contexts are valid.
-      return TRUE;
+      return $this->ourNewService->areConstraintsSatisfied($definition->getConstraints(), $context->getContextDefinition()->getConstraints());
     });
   }
 
eclipsegc’s picture

StatusFileSize
new16.49 KB

Tim and I talked a bunch and I extracted the entity specific bits into another service for the time being. I've not yet added test coverage for that service. What do we think of the basic structure of this now?

Patch got a bit simpler since I'm not rewriting the tests any more, so the patch is actually smaller than the interdiff would be.

Eclipse

Status: Needs review » Needs work

The last submitted patch, 52: 2671964-52.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new26.42 KB

I really like moving this responsibility out of ContextHandler.
I've gone a step further, and moved it to ContextDefinition itself.
Opened #2671964: ContextHandler cannot validate constraints for moving the entity logic out of this class.
Restored a lot of the original tests, but added a new one for the new parts.

Once again, the interdiff is bigger than the patch, so it is not included.

tim.plunkett’s picture

StatusFileSize
new6.79 KB
new26.82 KB

Discussed the failing test with @EclipseGc and came up with this solution.

The last submitted patch, 54: 2671964-context-54.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 55: 2671964-context-55.patch, failed testing. View results

eclipsegc’s picture

+++ b/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php
@@ -252,4 +257,105 @@ public function getDataDefinition() {
+          foreach ($constraints['Bundle']->bundle as $bundle) {
+            // We have a bundle, we are bundleable and we can generate a sample.
+            $values[] = EntityAdapter::createFromEntity($storage->createWithSampleValues($bundle));
+          }

I was thinking about this bit and... we could yield here so that we don't have to generate all bundles we just have to generate until we find a bundle that works. Thoughts?

Eclipse

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new4.49 KB
new26.79 KB

Haven't figured out the block fail yet, but here's the yield.

Status: Needs review » Needs work

The last submitted patch, 59: 2671964-context-59.patch, failed testing. View results

eclipsegc’s picture

Status: Needs work » Needs review
StatusFileSize
new1.49 KB
new26.87 KB

Ok, so we need to encapsulate the yields so that we don't invoke every member of the array return needlessly. I wrapped it in an anonymous function in order to achieve this but given some of the black magic happening here I wanted to see if this reduces us back to the 5 failures of earlier before working further.

Eclipse

Status: Needs review » Needs work

The last submitted patch, 61: 2671964-61.patch, failed testing. View results

eclipsegc’s picture

I've not looked at all the failures, but the BlockTest failure is related to the UserRole condition, which given the code we're touching here likely means it's context related. I'll see what I can do to investigate further.

Eclipse

eclipsegc’s picture

Status: Needs work » Needs review
StatusFileSize
new26.99 KB
new751 bytes

Oooook... So I think this is a user password validation related error. So I cheated, and just told the "satisfied by" code to return true for all user requirements vs user contexts. If this passes all tests, then I'll look at the password validation process on user entities and see if there's something really wrong there.

Eclipse

eclipsegc’s picture

StatusFileSize
new27.62 KB
new1.49 KB

Ok, looking at the password validation stuff, I see that there's a switch for just not validating password. I've added that to the current user context provider. Let's see if this has a negative or positive effect on tests.

Eclipse

tim.plunkett’s picture

StatusFileSize
new6.45 KB
new28.32 KB

Somewhere I missed adding the test coverage to prove that the yield is actually a benefit.
While doing that I was able to confirm that the change in #61 is not needed. Undoing that.

Additionally, I found the root issue causing the password fails.
\Drupal\Core\TypedData\Plugin\DataType\Map::getValue() loops over each property, but only currently works for those properties that happened to be instantiated already.
Switching to iterate over the full list of properties causes the comparison in \Drupal\user\Plugin\Validation\Constraint\ProtectedUserFieldConstraintValidator::validate() to correctly pass.

Status: Needs review » Needs work

The last submitted patch, 66: 2671964-context-66.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new1.65 KB
new28.56 KB

That exposed a lot of other bugs, so opened #2934192: FieldItemBase::getValue() returns partial or full values depending on state for that. Restoring the workaround with an @todo pointing there.

eclipsegc’s picture

I think we need an rtbc at this point.

Eclipse

samuel.mortenson’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me 👍

larowlan’s picture

Status: Reviewed & tested by the community » Needs review
+++ b/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php
@@ -252,4 +257,105 @@ public function getDataDefinition() {
+  public function isSatisfiedBy(ContextInterface $context) {

+++ b/core/lib/Drupal/Core/Plugin/Context/ContextDefinitionInterface.php
@@ -20,4 +20,16 @@
+  public function isSatisfiedBy(ContextInterface $context);

This is a BC break.

I discussed this with @catch and we were of the opinion that in order to maintain BC we might need to add a new interface for this method, and have ContextDefinition implement it.

What would the implications of that be?

What are the implications on contrib if we don't?

We have the 'contributed blocker' tag but there isn't any detail on what it blocks.

Thanks

tim.plunkett’s picture

However, we reserve the ability to add methods to these interfaces in minor releases to support new features. When implementing the interface, module authors are encouraged to either:
Where there is a 1-1 relationship between a class and an interface, inherit from the class. Where a base class or trait is provided, use those. This way your class should inherit new methods from the class or trait when they're added.

There is a 1:1 relationship here.

tim.plunkett’s picture

Status: Needs review » Reviewed & tested by the community

I spent a good amount of time on the IS, it explains why this issue is essential.

Without this fix, context definitions are pretty useless. Which is why no one has implemented the interface besides this single class.

It blocks any contrib module from being able to use the entire subsystem.

larowlan’s picture

Status: Reviewed & tested by the community » Needs review
  1. +++ b/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php
    @@ -252,4 +257,105 @@ public function getDataDefinition() {
    +      $violations = $validator->validate($value, array_values($this->getConstraintObjects()));
    +      // If a value has no violations then the requirement is satisfied.
    ...
    +        return TRUE;
    

    what if there is more than one value?

    if the first one is valid, we return true without checking the later ones.

  2. +++ b/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php
    @@ -252,4 +257,105 @@ public function getDataDefinition() {
    +    // @todo Move the entity specific logic out of this class in
    +    //   https://www.drupal.org/node/2671964.
    ...
    +    // @todo Move the entity specific logic out of this class in
    +    //   https://www.drupal.org/node/2671964.
    

    that is this issue?

  3. +++ b/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php
    @@ -252,4 +257,105 @@ public function getDataDefinition() {
    +    if (!empty($constraints['EntityType']) && $constraints['EntityType'] instanceof EntityTypeConstraint) {
    +      $entity_type_manager = \Drupal::entityTypeManager();
    +      $entity_type_id = $constraints['EntityType']->type;
    +      $storage = $entity_type_manager->getStorage($entity_type_id);
    +      // If the storage can generate a sample entity we might delegate to that.
    +      if ($storage instanceof ContentEntityStorageInterface) {
    +        if (!empty($constraints['Bundle']) && $constraints['Bundle'] instanceof BundleConstraint) {
    +          foreach ($constraints['Bundle']->bundle as $bundle) {
    +            // We have a bundle, we are bundleable and we can generate a sample.
    +            yield EntityAdapter::createFromEntity($storage->createWithSampleValues($bundle));
    +          }
    +          return;
    +        }
    

    this feels back to front.

    we're embedding specific knowledge of an 'EntityType' constraint in ContextDefinition.

    Feels like there is a missing concept between the constraint and the definition.

  4. +++ b/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php
    @@ -252,4 +257,105 @@ public function getDataDefinition() {
    +    if (strpos($this->getDataType(), 'entity:') === 0) {
    +      $entity_type_id = substr($this->getDataType(), 7);
    +      $constraint_definitions['EntityType'] = ['type' => $entity_type_id];
    

    Shouldn't the context definition constructor add this for us, then we don't need to embed this logic

    Or does this indicate there is a missing EntityContextDefinition that sub-classes ContextDefinition and auto-adds this constraint definition.

    I note that \Drupal\layout_builder\Plugin\Derivative\FieldBlockDeriver::getDerivativeDefinitions adds the Bundle constraint - but not the EntityType? Should it?

    If someone already added an alternate EntityType constraint - would this clobber it?

    tl;dr if we expect a context with naming entity:{type} will have an EntityType constraint, shouldn't it happen before here?

larowlan’s picture

There was no discussion here of why #51 was abandoned

tim.plunkett’s picture

#74

1) One is enough

2) That should have linked to #2932462: Add EntityContextDefinition for the 80% use case

3) That's what that ^ was about

4) Also covered by the above.

#75

If you'll note, the diff of ContextHandler is almost identical to #51. But instead of delegating to a service, it's delegating directly to the ContextDefinition object in question.

Not playing status wars anymore.

larowlan’s picture

If you'll note, the diff of ContextHandler is almost identical to #51. But instead of delegating to a service, it's delegating directly to the ContextDefinition object in question.

Right, but keeping it somewhere else outside the context definition would remove the API change and that whole debate becomes moot.

However, in light of the right @todo link, the current approach here (the API addition) gives a clear path forward for #2932462: Add EntityContextDefinition for the 80% use case with a clean API design so that's reason not to make it a separate service.

I think that as per #72, there is a 1:1 relationship.

Given the full scope of the interface:

I just don't see anyone implementing it without sub-classing the ContextDefinition class.

larowlan’s picture

Status: Needs review » Reviewed & tested by the community

Putting back to RTBC to get opinions from other committers.

We still need the @todo links fixed, but that can remain RTBC

larowlan’s picture

StatusFileSize
new35.66 KB
larowlan’s picture

Issue summary: View changes
larowlan’s picture

tim.plunkett’s picture

StatusFileSize
new1.22 KB
new28.56 KB

Fixing NID

catch’s picture

I'd forgotten about the explicit 1-1 rule when discussing this last night. Given that, I think this should be OK to go into 8.5.x too.

tim.plunkett’s picture

Issue summary: View changes
Issue tags: +API addition

Reflected that in the IS

larowlan’s picture

Issue tags: +Needs change record

I know this is an underused API, but can we get a change record for the new functionality, as its a vast improvement over the current logic.

Thanks

eclipsegc’s picture

Issue tags: -Needs change record

Added a change notice here: https://www.drupal.org/node/2934902

Eclipse

berdir’s picture

  1. +++ b/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php
    @@ -252,4 +257,105 @@ public function getDataDefinition() {
    +    // If the data types do not match, this context is invalid unless the
    +    // expected data type is any, which means all data types are supported.
    +    if ($this->getDataType() != 'any' && $definition->getDataType() != $this->getDataType()) {
    +      return FALSE;
    +    }
    

    Related question, I was wondering about hierarchy in regards to context data types before.. lets say I have something requires a type "entity" and I have a node, aka "entity:node". Clearly that's not supported yet, is there any issue/discussion on supporting that?

    This isn't making it any worse than it is now, so obviously not a blocker.

  2. +++ b/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php
    @@ -252,4 +257,105 @@ public function getDataDefinition() {
    +    // If the data type is an entity, manually add one to the constraints array.
    +    if (strpos($this->getDataType(), 'entity:') === 0) {
    +      $entity_type_id = substr($this->getDataType(), 7);
    

    similar here, if we look explicitly for entity:, what if I have a context that accepts any entity...

  3. +++ b/core/tests/Drupal/Tests/Core/Plugin/Context/ContextDefinitionIsSatisfiedTest.php
    @@ -0,0 +1,340 @@
    +    $this->entityTypeBundleInfo->getBundleInfo('test_config')->willReturn([]);
    

    not sure if/how this is relevant for the test, but it's worth pointing out that getBundleInfo() always returns a bundle, if there is none, then there is a default/fallback bundle where bundle === entity_type_id, see \Drupal\Core\Entity\EntityTypeBundleInfo::getAllBundleInfo(). Might result in a more realistic test to simulate that behavior?

tim.plunkett’s picture

larowlan’s picture

Status: Reviewed & tested by the community » Needs work

For #87.3

Let's fix that

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new1.14 KB
new28.68 KB

Doesn't affect the test, but might as well do it.

eclipsegc’s picture

Status: Needs review » Reviewed & tested by the community

No behavioral change, and the test looks more complete. Re-RTBCing.

Eclipse

larowlan’s picture

Crediting @fago, @dawehner, @phenaproxima and @Berdir as their reviews shaped the patch

  • larowlan committed 0c1de8e on 8.5.x
    Issue #2671964 by EclipseGc, tim.plunkett, Jo Fitzgerald, larowlan, fago...
larowlan’s picture

Status: Reviewed & tested by the community » Fixed

Discussed the earlier concerns of @fago and @dawehner with @EclipseGc.

For posterity, the patch changed tack in #46 to address those concerns and the final solution doesn't special case and validation constraints or validators.

Instead, it relies on the ::generateSampleValues functionality since added to core, so that validators don't have to do anything different.

This is a better solution as we don't embed any knowledge of the context api in the validation api.

Committed as 0c1de8e and pushed to 8.5.x.

Published change record, unpostponed issues blocked on this.

dsnopek’s picture

Wow! This has huge implications (the good kind :-)) for Panelizer in Drupal 8 :-)

jonathan1055’s picture

I think the commit in #93 above has caused new test failuers in Rules #2936553: Rules kernel tests fail with "Creating default object from empty value", judging by the timing of the commit and the backtrace. Not saying that the fault does not originate within Rules code, but this core change definitely is involved in it.

eclipsegc’s picture

The fault is almost certainly with Rules. This system was not capable of filtering plugins by context appropriate until now. I'm sure Rules was attempting to compensating for it.

tim.plunkett’s picture

In fact, from the description in the issue it doesn't even sound like a bug in Rules, but a failed assumption in the Rules test setup

jonathan1055’s picture

Thanks EclipseGc and tim.plunkett.

I was asking on this issue because the new line $current_user->_skipProtectedUserFieldConstraint = TRUE; definitely causes the "Creating default object from empty value" error. Is there any way that:

  public function getRuntimeContexts(array $unqualified_context_ids) {
    $current_user = $this->userStorage->load($this->account->id());

should ever validly return NULL i.e. not a user object? Adding the temporary line

    if (empty($current_user)) { $current_user = new \stdClass; }

fixes the problem and the Rules tests run OK. But obviouly that's not the solution. I don't know enough about the contexts to work out where the real source of the problem is, so just thought I would ask here, as this is where that new line came from.

Jonathan

tim.plunkett’s picture

I guess this could be broken for anonymous users.

diff --git a/core/modules/user/src/ContextProvider/CurrentUserContext.php b/core/modules/user/src/ContextProvider/CurrentUserContext.php
index 73be2caa09..aacc5f774a 100644
--- a/core/modules/user/src/ContextProvider/CurrentUserContext.php
+++ b/core/modules/user/src/ContextProvider/CurrentUserContext.php
@@ -50,9 +50,11 @@ public function __construct(AccountInterface $account, EntityManagerInterface $e
   public function getRuntimeContexts(array $unqualified_context_ids) {
     $current_user = $this->userStorage->load($this->account->id());
 
-    // @todo Do not validate protected fields to avoid bug in TypedData, remove
-    //   this in https://www.drupal.org/project/drupal/issues/2934192.
-    $current_user->_skipProtectedUserFieldConstraint = TRUE;
+    if ($current_user) {
+      // @todo Do not validate protected fields to avoid bug in TypedData,
+      //   remove this in https://www.drupal.org/project/drupal/issues/2934192.
+      $current_user->_skipProtectedUserFieldConstraint = TRUE;
+    }
 
     $context = new Context(new ContextDefinition('entity:user', $this->t('Current user')), $current_user);
     $cacheability = new CacheableMetadata();
larowlan’s picture

Can we get a major follow up filed to add add the diff at #100?

tim.plunkett’s picture

larowlan’s picture

thanks

Status: Fixed » Closed (fixed)

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

agentrickard’s picture

Apparently, this change breaks certain uses of COnfig Entities, since they do not require the getIterator() method.

See:

#2941319: Error: Call to undefined method Drupal\domain\Entity\Domain::getIterator()
#2759267: Undefined method YamlForm::getIterator()

I have a temporary fix here: #2941457: EntityAdapter::getIterator requires undocumented methods

Without the fix, Config Entities will throw fatal errors in unpredictable areas, such as Context negotiation. The only other fix is to break BC and force Config Entities to add the method.

tim.plunkett’s picture

The bug in EntityAdapter was introduced in 2014 via #2002138: Use an adapter for supporting typed data on ContentEntities, but only exposed to wider runtime code by this issue.
I think that the "temporary fix" issue is actually a long-term fix, even after #1818574: Support config entities in typed data EntityAdapter happens.
It is now RTBC.

cilefen’s picture

Did this issue cause the linked regression?

timodwhit’s picture

There was a major performance regression with this change that is visible in layout builder. Issue created:#2981889: Performance Degradation in Layout Builder and other places likely