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
| Comment | File | Size | Author |
|---|---|---|---|
| #52 | 2926914-section-51.patch | 89.82 KB | tim.plunkett |
| #52 | 2926914-section-51-interdiff.txt | 1.68 KB | tim.plunkett |
| #46 | 2928450-cleanup-46.patch | 89.75 KB | tim.plunkett |
| #46 | 2928450-cleanup-46-interdiff.txt | 23.19 KB | tim.plunkett |
| #45 | 2926914-section-45-interdiff.txt | 14.96 KB | tim.plunkett |
Comments
Comment #2
tim.plunkettComment #3
tedbowJust looking at Section.php so far
It seems that to get to this function the block should always have a
weight. Should validate that here?For instance in
setBlockInfo()we callvalidateBlockInfo()and then do a sort assuming all blocks have a weight.Ok this is what you use to update a block not
setBlockInfo()except you could update an existing block withsetBlockInfo()and I don't think you would get error. Should error be thrown insetBlockInfo()if theuuidis already set. Right now I think it would just override the settings.Looking at this it seems that you can add a block to the region with either
addBlock()orsetBlockInfo()but if I want to specify the weight I should usesetBlockInfo().The doc for
setBlockInfo()doesn't specify this.Also
addBlock()just callssetBlockInfo()after getting the weight. Could we just get rid ofaddBlock()and havesetBlockInfo()handle find the weight if none is provided.Or at least update the doc to so it is obvious when to call which?
Comment #4
tim.plunkettsetBlockInfo() 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()thaninsertBlockAtDelta(), 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.
Comment #5
tedbow\Drupal\layout_builder\SectionStorageInterface::addSection()uses$deltanot$indexin it's signature. Would be clearer if the they matched.Otherwise looks good!
Comment #6
tim.plunkettDiscussed the insertBlock split with @larowlan, ended up with
insertBlockAtDelta()andinsertBlockAfter().insertBlock()is now a protected method used by both of those.Renamed
addBlock()toappendBlock().Also fixed #5.
Comment #8
tim.plunkett> Build timed out (after 110 minutes)
Comment #9
tedbowDuh! Missed that
setBlockInfo()is protected.I like the method name changes in #6. Confirming #5 is fixed.
Comment #10
larowlanSome nice test coverage here, ++
Without this the interface only has ::get - which is defined in the parent. Do we need it?
Devil's advocate: could/should we model this as a value object instead of a magic array?
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 etcIf we did go down the value object to model the block info, this would sit well on that
extends \Countable?
could just
$expected[] = ['layout_id' => 'layout_twocol_bricks'...]here to save repeating the existing bits (the third test case)Comment #11
tim.plunkett1) 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
Comment #12
larowlannice
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
Comment #13
tim.plunkettHere it is with a BlockInfo class, but I'm not 100% sure about it.
Comment #15
larowlanI 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?
Comment #17
eclipsegc commentedReview 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.
I really like this whole change block. It simplifies the parameters and makes it easier to read.
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.
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.
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.
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).
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. :-)
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']??
Wouldn't it be so nice if we could just use a scalar typehint in the function signature :-D *cough*PHP7*cough*
Let's remove this bit.
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
Comment #18
eclipsegc commentedReviewing the interdiff of 13
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.
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.
whoa, that got way more complicated... :-(
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
Comment #19
tim.plunkettI 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 :)
Comment #20
tim.plunkett#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 toaddSection($count, $section). Meaning, at the end. If we were to have a dedicated method foraddSection(0, $section), it would be calledprependSection()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 stuff6) 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
Comment #21
tim.plunkettWorked 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
yieldabove 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.
Comment #22
xjmJust 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:
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.
Is it internal because it's experimental, or will it remain internal? Let's add text explaining the internal.
Comment #23
tim.plunkettOkay, here is the patch split down as far as I can get it.
No interdiff, because it doesn't really mean anything.
Comment #24
xjmPresumably we'll fill these out later on the issue.
What kinds of arrays are these array-based section values? Is the structure defined somewhere that we can link?
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.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
SectionComponentdocblocks 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"?
Comment #26
tim.plunkett1)
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.
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
Comment #28
tim.plunkettProtip: run ALL the tests after a change, not just the previously broken one.
Comment #29
tim.plunkettWent back through the whole patch and finished changing things that I believe are in-scope.
Comment #30
tim.plunkettHad 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.
Comment #31
larowlannice
Comment #32
tedbowIf we are going to through all the trouble of abstracting components away from blocks this should be renamed
$component_outputAll uses of
$this->blockManagerhave been removed except in the constructor. It can be removed.Also $account, $contextRepository, and $contextHandler
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
__constructthen you can remove the if statement frombuild().Not a fan of theSectionComponentname either. What aboutSectionRenderable?UPDATE: @tim.plunkett pointed out the sections themselves are are renderable so this would be confusing.
I think
SectionComponentis probably as good as anything unless we are willing to go forThingThatIsRenderedInsideASectionButNotNecessarlyABlock😜Comment #33
xjmThingThatIsRenderedInsideASectionButNotNecessarlyABlock+1.
Comment #34
xjm@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.
Comment #35
xjmComment #36
tim.plunkett#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
Comment #37
tim.plunkettMissed these changes during my cherry-pick from my other other branch :)
Comment #39
xjmComment #40
xjmJust a few morsels of docs; please confirm accuracy.
Comment #41
xjmComment #42
tim.plunkettOpened #2930334: Decide if domain objects like Section and SectionComponent should have interfaces, linked it here
This addresses the blank spots from #40
Comment #43
xjmMissed one.
Comment #44
xjmWhat value?
Comment #45
tim.plunkett@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
Comment #46
tim.plunkettThis 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
Comment #48
tim.plunkettIgnore the misnumbered/misnamed patch in the last comment, the contents of it are correct :)
Something up with Drupal CI today...
Comment #50
tim.plunkettException: Warning: apcu_store(): Unable to allocate memory for pool.Grrrrr
Comment #51
larowlanLooking good, only a couple of minor things and some observations
this is nice
this needs to be added back for the section blob?
can just be an if, the previous if throws an exception
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
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
Comment #52
tim.plunkett#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
Comment #53
larowlan#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.
Comment #54
catchJust 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.
Comment #55
xjm@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. :)
Comment #56
catchI didn't commit it :)
Comment #57
xjmErg, 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!
Comment #60
xjmNo, testbot. Stay.
Comment #61
tim.plunkettThanks!
Comment #63
tim.plunkett