Problem/Motivation

#2905922: Implementation issue for Layout Builder introduced \Drupal\layout_builder\Section as a building block (hah) of the new Layout system
It was a rough first attempt to define the domain of what it means to be a "layout section".
It was also very incomplete.

Proposed resolution

Track the layout ID and layout settings within the section.
Clarify naming and method signatures
Provide a full interface for classes responsible for storing a section

Remaining tasks

N/A

User interface changes

N/A

API changes

Yes, to @internal code in an alpha-stability experimental module

Data model changes

Yes, to internal data structures in an alpha-stability experimental module

CommentFileSizeAuthor
#52 2926914-section-51.patch89.82 KBtim.plunkett
#52 2926914-section-51-interdiff.txt1.68 KBtim.plunkett
#46 2928450-cleanup-46.patch89.75 KBtim.plunkett
#46 2928450-cleanup-46-interdiff.txt23.19 KBtim.plunkett
#45 2926914-section-45-interdiff.txt14.96 KBtim.plunkett
#45 2926914-section-45.patch82.05 KBtim.plunkett
#42 2926914-section-42.patch81.96 KBtim.plunkett
#42 2926914-section-42-interdiff.txt2.3 KBtim.plunkett
#40 interdiff-section-docs.txt4.19 KBxjm
#40 2926914-38.patch81.96 KBxjm
#37 2926914-section-37.patch80.86 KBtim.plunkett
#37 2926914-section-37-interdiff.txt5.55 KBtim.plunkett
#36 2926914-section-36.patch78.3 KBtim.plunkett
#36 2926914-section-36-interdiff.txt2.83 KBtim.plunkett
#30 2926914-section-30-interdiff.txt16.97 KBtim.plunkett
#30 2926914-section-30.patch78.27 KBtim.plunkett
#29 2926914-section-29.patch70.64 KBtim.plunkett
#29 2926914-section-29-interdiff.txt19.41 KBtim.plunkett
#28 2926914-section-28.patch69.14 KBtim.plunkett
#28 2926914-section-28-interdiff.txt694 bytestim.plunkett
#26 2926914-section-26.patch69.11 KBtim.plunkett
#26 2926914-section-26-interdiff.txt5.25 KBtim.plunkett
#23 2926914-section-23.patch68.49 KBtim.plunkett
#21 2926914-section-21.patch104.47 KBtim.plunkett
#21 2926914-section-21-interdiff.txt14.87 KBtim.plunkett
#20 2926914-section-20-interdiff.txt84.94 KBtim.plunkett
#20 2926914-section-20.patch101.19 KBtim.plunkett
#13 2926914-block_info-13.patch98.43 KBtim.plunkett
#13 2926914-block_info-13-interdiff.txt44.21 KBtim.plunkett
#11 2926914-section-11-interdiff.txt20.02 KBtim.plunkett
#11 2926914-section-11.patch93.83 KBtim.plunkett
#6 2926914-section-6.patch88.22 KBtim.plunkett
#6 2926914-section-6-interdiff.txt10.5 KBtim.plunkett
#2 2926914-section-2.patch83.85 KBtim.plunkett

Comments

tim.plunkett created an issue. See original summary.

tim.plunkett’s picture

Status: Active » Needs review
StatusFileSize
new83.85 KB
tedbow’s picture

Just looking at Section.php so far

  1. +++ b/core/modules/layout_builder/src/Section.php
    @@ -5,89 +5,232 @@
    +  protected function validateBlockInfo($uuid, array $block_info) {
    +    if (!isset($block_info['region'])) {
    +      throw new \InvalidArgumentException(sprintf('The block with UUID of "%s" is missing a region', $uuid));
    +    }
    +    if (!isset($block_info['configuration'])) {
    +      throw new \InvalidArgumentException(sprintf('The block with UUID of "%s" is missing configuration', $uuid));
    

    It seems that to get to this function the block should always have a weight. Should validate that here?

    For instance in setBlockInfo() we call validateBlockInfo() and then do a sort assuming all blocks have a weight.

  2. +++ b/core/modules/layout_builder/src/Section.php
    @@ -5,89 +5,232 @@
    -  public function updateBlock($region, $uuid, array $configuration) {
    -    if (!isset($this->section[$region])) {
    -      throw new \InvalidArgumentException('Invalid region');
    -    }
    

    Ok this is what you use to update a block not setBlockInfo() except you could update an existing block with setBlockInfo() and I don't think you would get error. Should error be thrown in setBlockInfo() if the uuid is already set. Right now I think it would just override the settings.

  3. +++ b/core/modules/layout_builder/src/Section.php
    @@ -5,89 +5,232 @@
    +      if (isset($info['weight'])) {
    +        $this->setBlockInfo($uuid, $info);
    +      }
    +      else {
    +        $this->addBlock($uuid, $info);
    +      }
    

    Looking at this it seems that you can add a block to the region with either addBlock() or setBlockInfo() but if I want to specify the weight I should use setBlockInfo().

    The doc for setBlockInfo() doesn't specify this.

    Also addBlock() just calls setBlockInfo() after getting the weight. Could we just get rid of addBlock() and have setBlockInfo() handle find the weight if none is provided.

    Or at least update the doc to so it is obvious when to call which?

tim.plunkett’s picture

setBlockInfo() is the protected method that the public methods (addBlock, updateBlock, insertBlock) that actually sets the block onto the array.
Every caller of setBlockInfo (those 3 methods, and the constructor) are responsible for ensuring that 'weight' is set correctly.
This shouldn't be nearly as confusing from outside the class, as it is protected.

Perhaps addBlock should be renamed to appendBlock().

Additionally, insertBlock() may be confusing.
It's more like insertBlockAfterBlock() than insertBlockAtDelta(), which might be the first assumption.
That is, you specify the UUID of a block, and it inserts it after.
We do this in MoveBlockController, because we track the "preceding UUID" in a data attribute of each "Add Block" link in the UI.
But maybe this is not API worthy, and should be kept within MoveBlockController.

tedbow’s picture

Status: Needs review » Needs work
+++ b/core/modules/layout_builder/src/Field/LayoutSectionItemList.php
@@ -16,17 +17,74 @@ class LayoutSectionItemList extends FieldItemList implements LayoutSectionItemLi
+  public function addSection($index, Section $section) {

\Drupal\layout_builder\SectionStorageInterface::addSection() uses $delta not $index in it's signature. Would be clearer if the they matched.

Otherwise looks good!

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new10.5 KB
new88.22 KB

Discussed the insertBlock split with @larowlan, ended up with insertBlockAtDelta() and insertBlockAfter().
insertBlock() is now a protected method used by both of those.

Renamed addBlock() to appendBlock().

Also fixed #5.

Status: Needs review » Needs work

The last submitted patch, 6: 2926914-section-6.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review

> Build timed out (after 110 minutes)

tedbow’s picture

Duh! Missed that setBlockInfo() is protected.

I like the method name changes in #6. Confirming #5 is fixed.

larowlan’s picture

Some nice test coverage here, ++

  1. +++ b/core/modules/layout_builder/src/Field/LayoutSectionItemListInterface.php
    @@ -24,23 +25,4 @@
    -  public function addItem($index, $value);
    

    Without this the interface only has ::get - which is defined in the parent. Do we need it?

  2. +++ b/core/modules/layout_builder/src/Section.php
    @@ -5,89 +5,232 @@
    + * - A multi-dimensional array keyed by block UUID, containing information like
    + *   region, weight, and block configuration.
    

    Devil's advocate: could/should we model this as a value object instead of a magic array?

  3. +++ b/core/modules/layout_builder/src/Section.php
    @@ -5,89 +5,232 @@
    +      // @todo Devise a mechanism for mapping old regions to new ones in
    +      //   https://www.drupal.org/node/2796877.
    +      $new_region = $layout_definition->getDefaultRegion();
    

    thinking about the implementation, we could support a third argument here 'LayoutChangeStrategyInterface', defaulting to null.

    If null, we could use this logic - which could be moved to new DefaultLayoutChangeStrategy(), then later we'd just pass in something nicer if we had a UI to support something better, but follow up/todo etc

  4. +++ b/core/modules/layout_builder/src/Section.php
    @@ -5,89 +5,232 @@
    +  protected function validateBlockInfo($uuid, array $block_info) {
    

    If we did go down the value object to model the block info, this would sit well on that

  5. +++ b/core/modules/layout_builder/src/SectionStorageInterface.php
    @@ -0,0 +1,84 @@
    +interface SectionStorageInterface {
    

    extends \Countable?

  6. +++ b/core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemListTest.php
    @@ -0,0 +1,121 @@
    +    $expected = [
    

    could just $expected[] = ['layout_id' => 'layout_twocol_bricks'...] here to save repeating the existing bits (the third test case)

tim.plunkett’s picture

StatusFileSize
new93.83 KB
new20.02 KB

1) No we do not! Good looking out

2) I'm hesitant to do this, because I already know that we'll need to expand this for storing Visibility Conditions. That said, we could provide a generic get/set for anyone else to store third party info.
But how do we prevent the constructor from ballooning out of control? Passing an associative array to the constructor would address that, but would only remove the magic array part on read, not on creation...
I went pretty far down the rabbit hole on this, but I'm really unsure about the change.

3) I like it!
Done

4) See 2

5) Oh, duh :) Thanks

6) Fair enough

larowlan’s picture

+++ b/core/modules/layout_builder/src/Section.php
@@ -121,26 +121,19 @@ public function getBlocksInfo() {
+    $blocks_info = $strategy->changeLayout($layout_id, $layout_settings, $this);

nice

Yeah, I see your point on constructor and generic get/set

Let's see where we end up before we introduce the wrong abstraction?

this looks close to me

tim.plunkett’s picture

StatusFileSize
new44.21 KB
new98.43 KB

Here it is with a BlockInfo class, but I'm not 100% sure about it.

The last submitted patch, 11: 2926914-section-11.patch, failed testing. View results

larowlan’s picture

I think it looks a lot cleaner, I was surprised (pleasantly). And it would use less memory because of the php 5.4 object optimisations. But a third opinion would be good though - what say you @tedbow?

Status: Needs review » Needs work

The last submitted patch, 13: 2926914-block_info-13.patch, failed testing. View results

eclipsegc’s picture

Review of 11, I'll do 13 interdiff next. I didn't read other's comments, so if I repeated stuff, I apologize. Also, I may contradict myself in the comments a little, so read all the way through before responding.

  1. +++ b/core/modules/layout_builder/src/Controller/MoveBlockController.php
    @@ -69,31 +69,31 @@ public static function create(ContainerInterface $container) {
    -    /** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
    -    $field = $entity->layout_builder__layout->get($delta_from);
    -    $section = $field->getSection();
    +    /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
    +    $field_list = $entity->layout_builder__layout;
    +    $section = $field_list->getSection($delta_from);
     
    -    $block = $section->getBlock($region_from, $block_uuid);
    -    $section->removeBlock($region_from, $block_uuid);
    +    $block = $section->getBlockInfo($block_uuid);
    +    $section->removeBlock($block_uuid);
    

    I really like this whole change block. It simplifies the parameters and makes it easier to read.

  2. +++ b/core/modules/layout_builder/src/Field/LayoutSectionItemList.php
    @@ -11,22 +13,79 @@
    +  public function appendSection(Section $section) {
    +    /** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $item */
    +    $item = $this->appendItem();
    +    $item->updateFromSection($section);
    +    return $this;
    +  }
    

    I remember seeing an addSection(0, $section) earlier and am wondering why that wasn't just a call to this method.

    Also, this and addSection... I'm not terribly stoked with the naming convention. I'm saying that out loud because it's true, but I see that you're following the methods already on the class from other interfaces, so I won't complain too badly, I just find the create vs append vs add naming abstruse.

  3. +++ b/core/modules/layout_builder/src/Field/LayoutSectionItemList.php
    @@ -11,22 +13,79 @@
    +    foreach ($this->list as $delta => $item) {
    +      $values[$delta] = $this->getSection($delta);
    +    }
    +    return $values;
    

    So... crazy suggestion... but you could do a yield $this->getSection($delta); here I think. Maybe the returned generator would mess stuff up, but you might play with it. We should look into yielding where possible as we continue developing.

  4. +++ b/core/modules/layout_builder/src/Field/LayoutSectionItemList.php
    @@ -11,22 +13,79 @@
    +    if (!$field = $this->get($delta)) {
    +      throw new \OutOfBoundsException('Invalid delta');
    +    }
    +
    +    return $field->getSection();
    

    cute... debating if I like assigning the variable in the condition for use AFTER the condition. I think I don't, but this is certainly the most concise way to write the method.

  5. +++ b/core/modules/layout_builder/src/Form/ConfigureSectionForm.php
    @@ -166,19 +156,15 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    +      $section->changeLayout($plugin_id, $configuration);
    

    So I'm going to propose we NOT allow users to change layouts. We're using more than one layout on a page, and allowing them to just add a new layout, drag blocks around and then kill layouts (and blocks) they no longer want or need seem totally optimal to me. If we support layout changing, we have to do stuff like block mapping, and I really want to never do that again because we cannot do it well and it will never work the way we want (at least not until computers can read our minds).

  6. +++ b/core/modules/layout_builder/src/Form/UpdateBlockForm.php
    @@ -42,12 +42,12 @@ public function getFormId() {
    -    $block = $field->getSection()->getBlock($region, $uuid);
    ...
    +    $block_info = $field->getSection()->getBlockInfo($uuid);
    

    I know I was initially against moving to putting region on the block info, but this clearly demonstrates the dx benefits and I'm glad you convinced me otherwise. :-)

  7. +++ b/core/modules/layout_builder/src/LayoutSectionBuilder.php
    @@ -82,33 +67,24 @@ public function __construct(AccountInterface $account, LayoutPluginManagerInterf
    +        $block_output['#weight'] = $weight++;
    

    ok, so this is actually a thing which gives me some pause here. Despite my praise above about removing regions from the array keys and adding them to block info, doesn't that complicate this process? Don't we need to also be saving relative block weights during the administrative layout process and then using them here? maybe $block_info['weight']??

  8. +++ b/core/modules/layout_builder/src/Section.php
    @@ -5,89 +5,225 @@
    +    if (!is_string($layout_id)) {
    +      throw new \InvalidArgumentException(sprintf('Layout plugin ID of type "%s" must be a string', gettype($layout_id)));
    +    }
    

    Wouldn't it be so nice if we could just use a scalar typehint in the function signature :-D *cough*PHP7*cough*

  9. +++ b/core/modules/layout_builder/src/Section.php
    @@ -5,89 +5,225 @@
    +  public function changeLayout($layout_id, array $layout_settings = [], LayoutChangeStrategyInterface $strategy = NULL) {
    +    if (!$strategy) {
    +      $strategy = new DefaultLayoutChangeStrategy();
    +    }
    +
    +    $blocks_info = $strategy->changeLayout($layout_id, $layout_settings, $this);
    +
    +    $this->layoutId = $layout_id;
    +    $this->layoutSettings = $layout_settings;
    +    $this->blocksInfo = $blocks_info;
    +    return $this;
    +  }
    

    Let's remove this bit.

  10. +++ b/core/modules/layout_builder/src/Section.php
    @@ -5,89 +5,225 @@
    +    uasort($this->blocksInfo, function ($a, $b) {
    +      return $a['weight'] > $b['weight'] ? 1 : -1;
    +    });
     
    

    Ok, I see you're doing some sorting during setBlockInfo()... I'd not considered this. Does this mean your blocks are always sorted in some specific order? I guess I was envisioning some process by which you track order individually across each region.

Eclipse

eclipsegc’s picture

Reviewing the interdiff of 13

  1. +++ b/core/modules/layout_builder/src/Controller/MoveBlockController.php
    @@ -85,12 +85,12 @@ public function build(EntityInterface $entity, $delta_from, $delta_to, $region_f
    +      $section->appendBlock($block_uuid, $block_info);
    

    We don't need block_uuid here do we? because $block_info already knows it? Or maybe this is just wrong? I see other examples later that are not passing the uuid individually.

  2. +++ b/core/modules/layout_builder/src/LayoutSectionBuilder.php
    @@ -131,24 +127,14 @@ protected function buildBlock($uuid, array $configuration, CacheableMetadata $ca
    +    $block = $this->blockManager->createInstance($block_info->getBlockId(), $block_info->getConfiguration());
    

    Might as well put a protected method on BlockInfo() that gets the block manager too (like Section does with layout) then you could $block = $block_info->getBlock(); I dunno, maybe too much magic and service location at that point, but it's along the same lines as what you did in Section, and that made a lot of stuff easier to read.

  3. +++ b/core/modules/layout_builder/src/Section.php
    @@ -237,23 +195,41 @@ protected function setBlockInfo($uuid, array $block_info) {
    +    $block_info = $this->getBlockInfo($uuid);
    +
    +    $blocks = $this->getBlocksInfoByRegion($block_info->getRegion());
    +    $slice_id = array_search($uuid, array_keys($blocks), TRUE);
    +    // Only process the blocks that need new weights.
    +    $blocks_to_set = array_slice($blocks, $slice_id + 1, NULL, TRUE);
    +    $new_weight = $block_info->getWeight();
    +
         unset($this->blocksInfo[$uuid]);
    +    $this->adjustBlockWeights($blocks_to_set, $new_weight);
    

    whoa, that got way more complicated... :-(

  4. +++ b/core/modules/layout_builder/src/Section.php
    @@ -302,21 +278,20 @@ protected function getBlocksInfoByRegion($region) {
    +  public function insertBlockAfter(BlockInfo $block_info, $preceding_uuid) {
    +    $region = $block_info instanceof BlockInfo ? $block_info->getRegion() : $block_info['region'];
    

    this looks like BC, but you're typehinting BlockInfo in the signature, so I think it's unnecessary.

So, all said and done, I'm fine with 11 or 13, so really either way. 13 is certainly easier to read. As long as it's an implementation detail of layout_builder and not a requirement of blocks in general, I'm on board... but it reminds me a lot of the Block entity, which is where much of my "meh" comes from. That might actually be a good thing though because it might indicate that we're adopting an architecture that will be a fairly easy drop in replacement for block layout when the time comes.

All in all, this patch is coming along pretty nicely and cleans up a bunch of stuff, so generally speaking ++.

Eclipse

tim.plunkett’s picture

I think my patch in #13 was less complete than what I had locally. I definitely got rid of the $region = $block_info instanceof BlockInfo ? $block_info->getRegion() : $block_info['region']; bit.

I will dive into the review of #11 in depth tomorrow, since that's all relevant to both approaches.

@EclipseGc, other than the obvious things wrong with the patch in #13, what do you think of the overall approach? +1 if cleaned up?
EDIT: Reread the last paragraph of #18 again. Thanks :)

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new101.19 KB
new84.94 KB

#17

1) Yay! I agree.

2) Two things here. One, definitely need to rename this to insertSection() to match insertBlock. Second, appendSection($section) is the equivalent to addSection($count, $section). Meaning, at the end. If we were to have a dedicated method for addSection(0, $section), it would be called prependSection()

3) Good idea. I keep forgetting yield is in 5.5, not 5.6

5) I borrowed this code from Field Layout, where we definitely need to be able to change the layout. But that's an obsolete approach, as you point out. We only need to be able to manipulate the layout settings, so I added setlayoutSettings() and removed changeLayout, and the really cool LayoutChangeStrategy stuff

6) Yay :)

7) This an oversight, we do store block weights internally now. Fixed this.

8) Hah, yes it would be nice

9) Done, per #5

10) I removed this. We should not be sorting the full list, only the subset when we need it

#18

1) Fixed, that was a typo anyway

2) I agree, done

3) I reverted all this

4) As I said in #19, thought I had removed that. Gone now :)

@EclipseGc and I discussed how much we both disliked the name BlockInfo, but liked the value object.
Since the fact that we're using blocks is an implementation detail, we agreed to name this SectionComponent.
The rename made an interdiff from #13 basically unreadable, so this is from #11

tim.plunkett’s picture

StatusFileSize
new14.87 KB
new104.47 KB

Worked on rebasing all of the work from #2922033: Use the Layout Builder for EntityViewDisplays on top of this, and made a couple changes based on that.
Most notably, the use of yield above breaks a lot of expectations of other code. I'm punting on that.

Additionally, I was able to kill off LayoutSectionItemInterface, which is good. Shouldn't be using interfaces for things that are never supposed to be a public API.

xjm’s picture

Issue tags: +Needs change record

Just scanning so far... The diff is a bit goofy which makes it a bit hard to read. Thinking also of how we could parcel this into more manageable chunks:

  • An initial patch that adds the new APIs.
  • A followup that does some of the renaming of methods and the like.
  • Another followup to remove the now-unneeded interfaces (we can just stick an @todo on them at first).
  • Etc.

I think we'll also eventually want a CR for this, but no point in writing it just yet as we're still finalizing the API.

+++ b/core/modules/layout_builder/src/Section.php
@@ -5,158 +5,280 @@
+ * @internal
  */

Is it internal because it's experimental, or will it remain internal? Let's add text explaining the internal.

tim.plunkett’s picture

StatusFileSize
new68.49 KB

Okay, here is the patch split down as far as I can get it.
No interdiff, because it doesn't really mean anything.

xjm’s picture

  1. +++ b/core/modules/layout_builder/src/Field/LayoutSectionItemInterface.php
    @@ -16,6 +16,8 @@
    + * @deprecated
    
    +++ b/core/modules/layout_builder/src/Field/LayoutSectionItemListInterface.php
    @@ -13,8 +14,10 @@
    + * @deprecated
    

    Presumably we'll fill these out later on the issue.

  2. +++ b/core/modules/layout_builder/src/Field/LayoutSectionItemList.php
    @@ -17,16 +19,123 @@ class LayoutSectionItemList extends FieldItemList implements LayoutSectionItemLi
    +   * Converts an array-based section value to an object.
    +   *
    +   * @param \Drupal\layout_builder\Section|array $value
    +   *   A section value.
    

    What kinds of arrays are these array-based section values? Is the structure defined somewhere that we can link?

  3. +++ b/core/modules/layout_builder/src/Field/LayoutSectionItemList.php
    @@ -17,16 +19,123 @@ class LayoutSectionItemList extends FieldItemList implements LayoutSectionItemLi
    +      throw new \OutOfBoundsException('Invalid delta');
    ...
    +      throw new \OutOfBoundsException('Invalid delta');
    

    This is one of those exeptions that could be hard to debug; probably better to do something like "No section exists for delta $delta in $do_I_know_my_layout_name_or_something.

  4. +++ b/core/modules/layout_builder/src/Form/UpdateBlockForm.php
    @@ -61,7 +58,7 @@ protected function submitLabel() {
    -    $section->updateBlock($region, $uuid, $configuration);
    +    $section->getComponent($uuid)->setConfiguration($configuration);
    
    +++ b/core/modules/layout_builder/src/LayoutSectionBuilder.php
    @@ -84,31 +83,21 @@ public function __construct(AccountInterface $account, LayoutPluginManagerInterf
    -   * @param array $section
    -   *   An array of configuration, keyed first by region and then by block UUID.
    +   * @param \Drupal\layout_builder\SectionComponent[] $components
    +   *   An array of components.
    
    @@ -124,33 +113,30 @@ public function buildSectionFromLayout(LayoutInterface $layout, array $section)
    +   * @param \Drupal\layout_builder\SectionComponent[] $components
    +   *   An array of components.
    

    I appple-Fed through the patch and didn't find anything anywhere that defines what a "component" is. Based on how it's used, it's a "block or blocklike thing"? Presumably the SectionComponent docblocks could explain what it is.

    Also, naming things is hard. Is there a more meaningful and less overloaded word that we could use than "component"?

Status: Needs review » Needs work

The last submitted patch, 23: 2926914-section-23.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new5.25 KB
new69.11 KB

1)
These aren't even really "deprecated", their removal was just punted.
Opened #2928450: Remove dead code in the Layout Builder following Section refactoring and remade this into @todos instad.

2)
This is part of the "BC layer" because of the new split. I reeallly don't feel like documenting this because it will only exist during this dev cycle. It must not be part of the released code. Added an @todo for it as well.

3)
I went with this, not sure what would actually be helpful.

throw new \OutOfBoundsException(sprintf('Invalid delta "%s" for the "%s" entity', $delta, $this->getEntity()->label()));

4)
SectionComponent definitely needs some docs. A SectionComponent is an object to replace the magic array currently used by \Drupal\Core\Entity\Display\EntityDisplayInterface::getComponent(). And yes, it is block-like, similar to how I tried to get blocks into components in #2878685: Allow blocks to be added to entity displays in Field UI

Not actually sure what to write, since EntityDisplay never explain what a component is either :D

Also fixed the fail, was a problem in the BC I added back to the last patch

Status: Needs review » Needs work

The last submitted patch, 26: 2926914-section-26.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new694 bytes
new69.14 KB

Protip: run ALL the tests after a change, not just the previously broken one.

tim.plunkett’s picture

StatusFileSize
new19.41 KB
new70.64 KB

Went back through the whole patch and finished changing things that I believe are in-scope.

tim.plunkett’s picture

StatusFileSize
new78.27 KB
new16.97 KB

Had a break through on a TypedData thing while working on future code for this, and it made a lot of the storage way easier.
I'm really done tweaking now.

larowlan’s picture

+++ b/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php
@@ -36,15 +33,7 @@ class LayoutSectionItem extends FieldItemBase implements LayoutSectionItemInterf
+    $properties['section'] = DataDefinition::create('layout_section')

nice

tedbow’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/layout_builder/src/LayoutSectionBuilder.php
    @@ -84,32 +82,23 @@ public function __construct(AccountInterface $account, LayoutPluginManagerInterf
    +      if ($block_output = $component->build()) {
    

    If we are going to through all the trouble of abstracting components away from blocks this should be renamed $component_output

  2. +++ b/core/modules/layout_builder/src/LayoutSectionBuilder.php
    @@ -124,78 +113,15 @@ public function buildSectionFromLayout(LayoutInterface $layout, array $section)
    -    $block = $this->blockManager->createInstance($configuration['id'], $configuration);
    

    All uses of $this->blockManager have been removed except in the constructor. It can be removed.

    Also $account, $contextRepository, and $contextHandler

  3. +++ b/core/modules/layout_builder/src/SectionComponent.php
    @@ -0,0 +1,308 @@
    + * A component represents the smallest part of a layout. Components wrap a
    + * renderable plugin, currently using \Drupal\Core\Block\BlockPluginInterface.
    

    Why not explicitly only support blocks in the code with exceptions that say "only Blocks are currently supported.

    Just seems weird to say blocks are only supported but only place that is checking if the plugin is Block in build() and still there is no error, just not output. A section component without output is useless.

    Maybe throw exception in __construct then you can remove the if statement from build().

  4. +++ b/core/modules/layout_builder/src/SectionComponent.php
    @@ -0,0 +1,308 @@
    +class SectionComponent implements RefinableCacheableDependencyInterface {
    

    Not a fan of the SectionComponent name either. What about SectionRenderable?
    UPDATE: @tim.plunkett pointed out the sections themselves are are renderable so this would be confusing.
    I think SectionComponent is probably as good as anything unless we are willing to go for ThingThatIsRenderedInsideASectionButNotNecessarlyABlock 😜

xjm’s picture

ThingThatIsRenderedInsideASectionButNotNecessarlyABlock

+1.

xjm’s picture

Issue tags: -Needs change record

@tim.plunkett pointed out that since this is experimental a CR isn't required, which is totally right. Overall API docs can wait for the actual API once it's finalized/stable, and live in the codebase, and a CR can just reference those docs.

xjm’s picture

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new2.83 KB
new78.3 KB

#32
1)
2) I had made these changes already, but removed everything I could to minimize the patch. Punted more of that to #2928450: Remove dead code in the Layout Builder following Section refactoring. I could bring them back though, doesn't matter to me.

3) I agree this is not great. But it will change as part of #2922033: Use the Layout Builder for EntityViewDisplays. Added an @todo for now.

4) @xjm opened up an issue for this.

Also including three changes:
logic fix for SectionData
rename SectionComponent::build() to ::toRenderArray()
fix the cacheability approach in ::toRenderArray() per discussion with Wim Leers

tim.plunkett’s picture

StatusFileSize
new5.55 KB
new80.86 KB

Missed these changes during my cherry-pick from my other other branch :)

The last submitted patch, 36: 2926914-section-36.patch, failed testing. View results

xjm’s picture

xjm’s picture

StatusFileSize
new81.96 KB
new4.19 KB

Just a few morsels of docs; please confirm accuracy.

xjm’s picture

tim.plunkett’s picture

StatusFileSize
new2.3 KB
new81.96 KB

Opened #2930334: Decide if domain objects like Section and SectionComponent should have interfaces, linked it here

This addresses the blank spots from #40

xjm’s picture

+++ b/core/modules/layout_builder/src/Section.php
@@ -5,158 +5,285 @@
+  public function insertComponent($delta, SectionComponent $new_component) {
+    $components = $this->getComponentsByRegion($new_component->getRegion());
+    $count = count($components);
+    if ($delta > $count) {
+      throw new \OutOfBoundsException('Invalid delta');
+    }

Missed one.

xjm’s picture

+++ b/core/modules/layout_builder/src/Plugin/DataType/SectionData.php
@@ -0,0 +1,36 @@
+    if ($value && !$value instanceof Section) {
+      throw new \InvalidArgumentException('Value is not a valid section');
+    }

What value?

tim.plunkett’s picture

StatusFileSize
new82.05 KB
new14.96 KB

@xjm and I discussed the oddity of $weight being in the constructor of SectionComponent, and so it is going away. It was only used by tests anyway, which can call ::setWeight()

Also addressed #43/44

tim.plunkett’s picture

StatusFileSize
new23.19 KB
new89.75 KB

This merges in the API related changes from #2928450: Remove dead code in the Layout Builder following Section refactoring, leaving that as pure cruft cleanup

23k in changes, but only 7k bigger patch :D

Status: Needs review » Needs work

The last submitted patch, 46: 2928450-cleanup-46.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review

Ignore the misnumbered/misnamed patch in the last comment, the contents of it are correct :)
Something up with Drupal CI today...

Status: Needs review » Needs work

The last submitted patch, 46: 2928450-cleanup-46.patch, failed testing. View results

tim.plunkett’s picture

Status: Needs work » Needs review

Exception: Warning: apcu_store(): Unable to allocate memory for pool.

Grrrrr

larowlan’s picture

Looking good, only a couple of minor things and some observations

  1. +++ b/core/modules/layout_builder/src/Controller/LayoutBuilderController.php
    @@ -189,12 +189,12 @@ protected function buildAddSectionLink($entity_type_id, $entity_id, $delta) {
    -    $build = $this->builder->buildSectionFromLayout($layout, $item->section);
    ...
    +    $build = $section->toRenderArray();
    

    this is nice

  2. +++ b/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php
    @@ -73,17 +65,6 @@ public static function mainPropertyName() {
    -          // @todo Address in https://www.drupal.org/node/2914503.
    

    this needs to be added back for the section blob?

  3. +++ b/core/modules/layout_builder/src/Section.php
    @@ -5,158 +5,302 @@
    +    // If the delta is the end of the list, append the component instead.
    +    elseif ($delta === $count) {
    

    can just be an if, the previous if throws an exception

  4. +++ b/core/modules/layout_builder/src/SectionComponent.php
    @@ -0,0 +1,313 @@
    +    if ($plugin instanceof BlockPluginInterface) {
    ...
    +    $plugin = $this->pluginManager()->createInstance($this->getPluginId(), $this->getConfiguration());
    ...
    +    return \Drupal::service('plugin.manager.block');
    

    In what circumstances would this not be an instance of BlockPluginInterface?

    In that follow up we should switch this {duck}->toRenderArray()

    instead of putting this knowledge here

  5. +++ b/core/modules/layout_builder/src/SectionComponent.php
    @@ -0,0 +1,313 @@
    +  public function get($property) {
    ...
    +  public function set($property, $value) {
    

    we should have a trait for this, the concept is used in a few places (unrelated)

    i've reviewed a couple of patches (#2909472: Add value objects to represent the return of hook_requirements, #2908735: [meta] Add value objects to represent hook_schema) that could use it

tim.plunkett’s picture

StatusFileSize
new1.68 KB
new89.82 KB

#51

1) Thanks :)

2) Can't see from the diff, but that @todo was in the method twice, once per occurrence of serialize. Still there for the other blob

3) Sure

4) Correcting that @todo to link to #1875974: Abstract 'component type' specific code out of EntityDisplay for further discussion. Point is, not ready to commit 100% to always being blocks
Also opened #2931040: Consider adding a BlockPluginInterface::toRenderArray() and linked.

5) Opened #2931052: Provide a trait for getting/setting additional properties on an object

larowlan’s picture

Status: Needs review » Reviewed & tested by the community

#52.4 right that makes sense now its the right link :) I commented on the ::toRenderArray() about making it more generic

Let's get this in and move to the next steps.

Adding review credits whilst here.

catch’s picture

Just to say I read through this and it all looks like a good improvement and pretty clear, makes sense to me to get it in and continue with follow-ups.

xjm’s picture

@catch did you commit this? I tried to just now but it does not apply.

Edit: nm it applied now; no idea what I was doing wrong. :)

catch’s picture

I didn't commit it :)

xjm’s picture

Erg, just lost a big dreditor review, Automatic Updates--. Trying to resummarize:

The part of this patch that I get hung up on every time is the storage. Configured layout/section data being stored as field content data seems weird and wrong to me. I guess the reason it was done this way is that HEAD currently only supports layout overrides for individual content entities, and since those individual content entities are saved as content, the sections are too. In #2922033: Use the Layout Builder for EntityViewDisplays that will change; layouts/sections will be attached to entity displays which are config entities. This is where #2927349: Decouple the Layout Builder UI from entities came from, which is the issue that spawned the entity decorator and whatnot that concerned me.

But really it's out of scope here. This patch is a good API improvement; the existing storage is already in HEAD and this patch only adjusts the API without affecting the storage itself (plus the part here that decouples/abstracts the section storage is actually helpful for my concern).

My other major concern is also related to the storage piece, for layouts for the "page chrome" a.k.a. block placement within theme regions (currently via the Block UI). TLDR is I think that layouts need to be stored the same way for all three usecases, not in three separate ways that have to have the API calls forwarded on to them). But this isn't the issue for that either, so there's no reason to hold it up. I would like the opportunity to sign off on any of the followups related to the storage before they go in, though.

Overall the API improvements in this issue are shiny, so (belatedly) committed and pushed to 8.5.x. Thanks!

  • xjm committed 2e16f2a on 8.5.x
    Issue #2926914 by tim.plunkett, xjm, larowlan, tedbow, EclipseGc:...

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 52: 2926914-section-51.patch, failed testing. View results

xjm’s picture

Status: Needs work » Fixed

No, testbot. Stay.

tim.plunkett’s picture

Assigned: tim.plunkett » Unassigned

Thanks!

Status: Fixed » Closed (fixed)

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

tim.plunkett’s picture

Component: layout.module » layout_builder.module