Problem/Motivation

Layout builder has a relatively naive context implementation that uses Core's normal context provider approach. This is problematic because Layout Builder itself thinks in terms of entities generically while core's context providers attempt to provide very specific entities. This is especially problematic with the NodeRouteContext which provides a node context at all times regardless of the presence of an actual node. This is purposeful in order to support the core block module's approach to block placement which is largely context-ignorant.

Proposed resolution

Get core's context provider list, ignore NodeRouteContext's provided context and create a "current_entity" context from the provided entity being "laid out". That context can be provided as part of the layout field's build process and needn't come directly from any context provider in core. This approach will allow us to support both embedded laid out entities with contextual blocks within other entities as well as things like views rendering entity layouts.

Remaining tasks

Write the code.

User interface changes

It would add new context for contextual blocks. The ignoring of the provided NodeRouteContext would typically prevent any UI changes except for during User entity layouts since both a "current user" and a "current entity" context would compete in that case. TBD how we want to handle that but it's pretty edge-casey.

API changes

Existing layout builder methods, especially those on the layout field, probably need to pass the context stack around rather than obtaining it late in the build process as happens now.

Data model changes

No expected changes.

CommentFileSizeAuthor
#45 2921626-layout-45.patch15.23 KBtim.plunkett
#45 2921626-layout-45-interdiff.txt571 bytestim.plunkett
#41 2921626-layout-41.patch14.98 KBtim.plunkett
#41 2921626-layout-41-interdiff.txt756 bytestim.plunkett
#36 2921626-layout-36.patch14.24 KBtim.plunkett
#36 2921626-layout-36-interdiff.txt719 bytestim.plunkett
#31 2921626-layout-31.patch14.17 KBtim.plunkett
#31 2921626-layout-31-interdiff.txt2.14 KBtim.plunkett
#31 Screen Shot 2018-01-11 at 12.45.25 PM.png201.81 KBtim.plunkett
#27 2921626-layout-27.patch14.18 KBtim.plunkett
#27 2921626-layout-27-interdiff.txt3.27 KBtim.plunkett
#22 2921626-layout-22.patch14.07 KBtim.plunkett
#19 2921626-layout_context-19-PASS.patch14.74 KBtim.plunkett
#19 2921626-layout_context-19-FAIL.patch1.37 KBtim.plunkett
#19 2921626-layout_context-19-interdiff.txt2.13 KBtim.plunkett
#16 2921626-layout_context-16.patch12.61 KBtim.plunkett
#11 2921626-layout-11.patch19.69 KBtim.plunkett
#8 2921626-8.patch13.75 KBEclipseGc
#5 2921626-5.patch12.61 KBEclipseGc
#2 2921626-2.patch14.18 KBEclipseGc
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

EclipseGc created an issue. See original summary.

EclipseGc’s picture

Status: Active » Needs review
FileSize
14.18 KB

An initial attempt at what the code should probably look similar to. No additional test, just want to see what blows up and how.

Eclipse

EclipseGc’s picture

+++ b/core/modules/layout_builder/src/LayoutSectionBuilder.php
@@ -126,13 +124,15 @@ public function buildSectionFromLayout(LayoutInterface $layout, array $section)
+   * @return array The render array for a given section.

bah... phpstorm.

Status: Needs review » Needs work

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

EclipseGc’s picture

Let's see how this fairs. I had to rewrite it from scratch after the layout_builder changes, so no interdiff. It's a small patch anyway.

Eclipse

EclipseGc’s picture

Status: Needs work » Needs review

oops

Status: Needs review » Needs work

The last submitted patch, 5: 2921626-5.patch, failed testing. View results

EclipseGc’s picture

Status: Needs work » Needs review
FileSize
13.75 KB

Clearly, I completely screwed up the previous patch. Sorry.

Eclipse

EclipseGc’s picture

So this patch is provided warts and all. With the move to some calls happening as part of hook_entity_view_alter(), that meant I had to repeat some code. Obviously this could be remedied by creating a new service. I opted not to for the moment. I'm pretty sure that's the right solution long term.

Eclipse

Status: Needs review » Needs work

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

tim.plunkett’s picture

tim.plunkett’s picture

Issue tags: +beta blocker
tim.plunkett’s picture

xjm’s picture

Priority: Normal » Major
tim.plunkett’s picture

Component: other » layout.module
Status: Needs work » Needs review

Here's a patch NOT built on all the other changes, since this is actually important now that FieldBlock is in.
Writing a test now.

tim.plunkett’s picture

The last submitted patch, 11: 2921626-layout-11.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 16: 2921626-layout_context-16.patch, failed testing. View results

tim.plunkett’s picture

Fixes the above fail, and adds tests of its own.

The last submitted patch, 19: 2921626-layout_context-19-FAIL.patch, failed testing. View results

EclipseGc’s picture

This looks really good. I've not applied the patch yet (which I will do momentarily) but from a pure code-review perspective, I'm very ++

Eclipse

tim.plunkett’s picture

Status: Needs review » Needs work
FileSize
14.07 KB

Here's a version that is dependent on #2927349: Decouple the Layout Builder UI from entities going in. Marking NW for now so as to not trigger the testbot.

tim.plunkett’s picture

Status: Needs work » Needs review

Blocker is in!

larowlan’s picture

+++ b/core/modules/layout_builder/layout_builder.module
@@ -166,9 +169,11 @@ function layout_builder_add_layout_section_field($entity_type_id, $bundle, $fiel
+    $contexts += _layout_builder_get_contexts_for_entity($entity);

@@ -187,3 +192,22 @@ function layout_builder_entity_view_alter(array &$build, EntityInterface $entity
+function _layout_builder_get_contexts_for_entity(EntityInterface $entity) {

Any reason not to implement ContextProviderInterface here and add a new tagged service.

e.g. see NodeRouteContext etc

https://gist.github.com/larowlan/dc88c7e2cd2d816fe80d99a8c92160b3 was something I'd used before

larowlan’s picture

+++ b/core/modules/layout_builder/layout_builder.module
@@ -187,3 +192,22 @@ function layout_builder_entity_view_alter(array &$build, EntityInterface $entity
+  $definition = new ContextDefinition("entity:{$entity->getEntityTypeId()}", new TranslatableMarkup('Current @entity', [
+    '@entity' => $entity->getEntityType()->getSingularLabel(),
+  ]));
+  return ['layout_builder.entity' => new Context($definition, $entity)];

I think if we plan to remove this after #2932462: Add EntityContextDefinition for the 80% use case, we should avoid adding it in the first place.

It's a couple of lines - we could just inline it.

Then when we have ECD, we can simplify it.

EclipseGc’s picture

So, the ContextProviders really are an adapter mechanism which extract context values from globally available services. Things like current user and language are all driven by the services in core, and the node is driven by the current request object. Unfortunately, that's the only scope at which context providers are really useful, and as we begin to delve into these more contextually-sensitive use cases and plugins, it becomes increasingly important that we have no only the global contexts, but also local ones.

The context object we're dealing with most commonly here is actually the entity which is reference by the field we're rendering during an entity override. So literally $field->getEntity(). This will change from one entity to the next, and in use cases like a view rendering multiple laid-out entities, we can't get the entity as context from the route. And even if we could, we didn't layout all the entities on the view page together, so they're all going to reference "node" incorrectly. (rather than something like node1, node2, etc). So the introduction of the current entity context in this patch allows us to pass around a context that's relevant for each entity being rendered because the field being rendered already knows what entity it's attached to, we just need to provide that entity as a context which can be mapped by the plugins.

TL:DR; ContextProviders weren't meant for this.

Eclipse

tim.plunkett’s picture

Implementations of ContextProviderInterface rely on global state or preconfigured information.
We can rely on neither.

Imagine a view of nodes, each laid out with Sections.
When rendering each node, the context must be derived from that entity at that time. There's nothing in the routeMatch to help us there.

Inlined _layout_builder_get_contexts_for_entity()

EclipseGc’s picture

I'm totally on board with this last patch. Can we get an RTBC?

Eclipse

samuel.mortenson’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me. 👍

EclipseGc’s picture

Status: Reviewed & tested by the community » Needs work

Tim, Sam and I were discussing this and realized that the text we're using is "Current @entity" so for users it'd be "Current User" which is the exact label that the current user context uses, so we should change that. Tim suggested'@label being viewed which I fully support.

Relevant sections are:

+++ b/core/modules/layout_builder/layout_builder.module
@@ -166,9 +169,13 @@ function layout_builder_add_layout_section_field($entity_type_id, $bundle, $fiel
+    $contexts['layout_builder.entity'] = new Context(new ContextDefinition("entity:{$entity->getEntityTypeId()}", new TranslatableMarkup('Current @entity', ['@entity' => $entity->getEntityType()->getSingularLabel()])), $entity);

+++ b/core/modules/layout_builder/src/Field/LayoutSectionItemList.php
@@ -74,6 +77,17 @@ public function removeSection($delta) {
+    $contexts['layout_builder.entity'] = new Context(new ContextDefinition("entity:{$entity->getEntityTypeId()}", new TranslatableMarkup('Current @entity', ['@entity' => $entity->getEntityType()->getSingularLabel()])), $entity);

Eclipse

tim.plunkett’s picture

Actually, the suggestion was from @dead_arm, she deserves the credit there :)
Here's the after screenshot, before it said "Current user" twice

EclipseGc’s picture

Status: Needs review » Reviewed & tested by the community

Yay @dead_arm.

No reason I can't re-rtbc given the scope of the interdiff.

Eclipse

webchick credited dead_arm.

webchick’s picture

Adding @dead_arm to issue credit. :)

larowlan’s picture

Status: Reviewed & tested by the community » Needs work

I manually tested this.

Steps taken

  1. enable module
  2. enable for basic page node type
  3. add a node
  4. edit layout
  5. add body field
  6. attempt to edit body field
  7. fatal error in ajax response
AjaxError: 
An AJAX HTTP error occurred.
HTTP Result Code: 500
Debugging information follows.
Path: /layout_builder/update/block/overrides/node%3A1/0/content/e94baa5f-2a30-463a-9485-d45f1dfd98d2?destination=node/1/layout
StatusText: 500 Service unavailable (with message)
ResponseText: The website encountered an unexpected error. Please try again later.Drupal\Component\Plugin\Exception\ContextException: Required contexts without a value: entity. in Drupal\Core\Plugin\Context\ContextHandler->applyContextMapping() (line 96 of core/lib/Drupal/Core/Plugin/Context/ContextHandler.php). Drupal\layout_builder\SectionComponent->getPlugin() (Line: 46)
Drupal\layout_builder\Form\UpdateBlockForm->buildForm(Array, Object, Object, '0', 'content', 'e94baa5f-2a30-463a-9485-d45f1dfd98d2', Array)
call_user_func_array(Array, Array) (Line: 514)
Drupal\Core\Form\FormBuilder->retrieveForm('layout_builder_update_block', Object) (Line: 271)
Drupal\Core\Form\FormBuilder->buildForm(Object, Object) (Line: 74)
Drupal\Core\Controller\FormController->getContentResult(Object, Object)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 582)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
call_user_func_array(Object, Array) (Line: 153)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 68)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 99)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 78)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 50)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 657)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

Test site -> https://due6v.ply.st/node/1/layout

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
719 bytes
14.24 KB

If we don't have any contexts passed, we shouldn't run them through the ContextHandler.
Simple fix, really nasty side effects without it.

EclipseGc’s picture

Status: Needs review » Reviewed & tested by the community

Tim and I discussed this together, and I am still totally on board.

I double checked add/edit/delete of a field block. Given the scope of the interdiff, I'm going to re-rtbc it pending tests passing.

Eclipse

larowlan’s picture

Ideally we'd have tests here, but we're blocked on #2924201: Resolve random failure in LayoutBuilderTest so that it can be added to HEAD, which is already tagged as a beta blocker

larowlan’s picture

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 36: 2921626-layout-36.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
756 bytes
14.98 KB

😩

EclipseGc’s picture

Status: Needs review » Reviewed & tested by the community

Still on the RTBC bandwagon over here (pending tests).

Eclipse

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Ok, Tim walked me through this patch, as well as demoing the before/after behaviour. We hit a snag at one point with some PHP errors, but tracked it down to applying the patch mid-demo and some old cruft that was exhibiting the bugs this patch is fixing leaking into the new stuff. Upon clearing that out and starting over, the patch worked great.

It's really nice that this patch limits the number of options available to people to stick in their layouts to only those that are relevant. Huge +1 to that from a UX POV.

The code itself is really straight-forward. There's one bit of ugliness which is some manual string concatenation ickiness in a couple of places, but there's a follow-up to make a nice API method for that at #2932462: Add EntityContextDefinition for the 80% use case. There's a method that gets removed, and a couple of others that get their signatures adjusted for new (optional) parameters, but all of these are limited to the layout builder module itself, not anywhere else in core. Additionally, there's test coverage for the new behaviour.

So, in sum: no complaints here! :)

Committed and pushed to 8.5.x. Thanks!

  • webchick committed 749f41f on 8.5.x
    Issue #2921626 by tim.plunkett, EclipseGc, larowlan, dead_arm: Add...
tim.plunkett’s picture

Stupid freaking PHP 5.
Thanks PHP 7 for fixing this asinine rule for traits.

EclipseGc’s picture

Status: Needs review » Reviewed & tested by the community

blah

Eclipse

  • xjm committed 6bc0d92 on 8.6.x
    Revert "Issue #2921626 by tim.plunkett, EclipseGc, larowlan, dead_arm:...

  • xjm committed 4637781 on 8.5.x
    Revert "Issue #2921626 by tim.plunkett, EclipseGc, larowlan, dead_arm:...
webchick’s picture

Status: Reviewed & tested by the community » Fixed

Ok, take two. :)

Committed and pushed to 8.6.x (oooh!) and cherry-picked to 8.5.x.

  • webchick committed 6f97c28 on 8.6.x
    Issue #2921626 by tim.plunkett, EclipseGc, larowlan, dead_arm, xjm: Add...

  • webchick committed 80390b6 on 8.5.x
    Issue #2921626 by tim.plunkett, EclipseGc, larowlan, dead_arm, xjm: Add...

Status: Fixed » Closed (fixed)

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

tim.plunkett’s picture

Component: layout.module » layout_builder.module
vitalie’s picture

Hi, looks like whatever was done here, was undone without much explanation in later commits. How could I pass the current entity to a Views block placed using Layout Builder?

yonailo’s picture

Yes I've got the same problem, I have a contextual filter and when I add the views block to the layout builder, I do not see the option to choose the contextual parameter. Any pointers to help ?

maskedjellybean’s picture

Same problem for me as unknownguy. I suspect this is not really what this issue was about, but could we look to https://www.drupal.org/project/viewsreference as an example of how to provide an Argument form field which a user could use to enter arguments that are passed to the view?

eworwa’s picture

Same here, I've been trying different combinations but I'm still not able to 'expose' contextual filters when including views (block displays) using Layout Builder, any hint on this?

maskedjellybean’s picture

Hey @eworwa I was able to pass arguments to a view block via a field in Layout Builder by applying this patch to ctools:

https://www.drupal.org/project/ctools/issues/2759445

Pay attention to comment #55. You have to do a tiny amount of configuring within the view itself for the field to appear.

Good luck!

super_romeo’s picture

Contexts _without_ data not visible in block context selector.
I created https://www.drupal.org/project/drupal/issues/3194198