Problem/Motivation
Currently layout_builder is very limited when it comes to more complex layouts. The issue is lack of nesting or wrapping ability. See:
I believe that all use cases bringing people towards these issues could be solved if we would simply allow people to create layouts with configurable regions. These could be a "second class citizens", yet would allow devs or contrib modules to develop custom complex rules of layout building, and store them in layouts configurations.
Proposed resolution
Introducing a new DynamicRegionsProviderInterface that could be implemented by Layout classes. That would allow layout to provide additional regions if needed. Thanks to that, we could work around any limits currently set by the layout_builder, by delegating dynamic construction of more complex layout structures to the Layout class itself.
Remaining tasks
- Patch review
- Discuss and agree on the solution
API changes
Addition of a new optional interface for layout classes. This change is completely backward compatible.
Issue fork drupal-3075939
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
luke_nuke commentedComment #4
luke_nuke commentedAttempt at fixing a patch.
Comment #5
luke_nuke commentedComment #6
luke_nuke commentedComment #7
luke_nuke commentedWe could also have an additional, optional interface for a layout e.g.:
DynamicRegionsProviderInterfacewith agetDynamicRegionsdefinition. That way we would be completely backward compatible, but i'm not sure if it's not an overkill, what do you think?Comment #8
luke_nuke commentedI created a new patch with this different interface approach, and a test.
Comment #9
effulgentsia commentedInteresting idea.
A proof of concept of this would be helpful. For example, how would you build on #8 to solve #3053145: Allow layout builder sections to be nested.?
Comment #10
luke_nuke commentedThanks for taking an interest effulgentsia.
1. Create your custom layout implementing
DynamicRegionsProviderInterface.2. Implement a configuration form, that allow to choose and add a "sublayout", sublayouts can be configurable and their list can be draggable, so you can reorder them.
3. List of available sublayout types can be hardcoded in your custom layout's class or pulled from somewhere else. Every sublayout type should have a set of regions.
4. On form submit of your main Layout implementing
DynamicRegionsProviderInterfacecreate and save proper configuration structure, with unique names (e.g. starting from uuid) for each region, for each sublayout.5. Now your
getDynamicRegionsmethod will simply extract list of regions from each sublayout from your configuration and return a flat list of unique regions.7. In your
build()method or layout's template do all the magic that is left to do. Which is making the layout renderable out of sublayout's custom themes with injected regions.It works, I'm already doing it.
Comment #11
luke_nuke commentedAnd if you want to have sublayouts nested even deeper? It entirely depends on your form-fu.
Comment #12
luke_nuke commentedSo in other words.
1. Store unique (e.g. prefixed with a random uuid) region names in configuration of any structure that you like.
2. Return flat array of all regions using
getDynamicRegionsmethod.3. Extract region from the configuration and inject them in the proper templates associated with your "sublayouts" or whatever substructures you decided to create.
Contrib module could make this process easier. To be honest, it's kind of possible even without this patch - you could modify plugin definition in your layout's constructor, to add new regions to it. But it's hacky as hell. What I try to do is to make dynamic regions officially recognized as a thing, to make them always work and have tests that would assure it.
Comment #13
effulgentsia commentedAre you able to share that code? If not, that's a shame but understandable. But if you are, it would be helpful in reviewing #8 to see #10 in action.
Comment #14
luke_nuke commentedI just created a sandbox project that is a proof of concept of the #10 . It just provides user with a configurable section, made of subsections/sublayouts. It's pretty basic.
https://www.drupal.org/sandbox/luken/3077035
It came to my mind that another perspective that can be used to explain this approach is that it's about delegating responsibility for more dynamic region structures to contrib/custom modules, which is the simplest and least invasive solution to the immediate problems presented by the Layout Builder currently.
Comment #15
vinodhini.e commented#8 patch does not apply
Comment #16
luke_nuke commentedRe-roll.
Comment #18
luke_nuke commentedOops, re-roll.
Comment #19
joncjordan commentedSo far this seems like a very promising approach. I've tested the patch in #18 and it says it didn't apply (when I try to apply patch via composer), however I see the changes are being applied. Here's the output from my the patch failure:
I do have it working in a test environment, and implemented the functionality from your sandbox module. Everything seems to be working, not sure why the patch is failing.
Thanks for your work Luke_Neke.
Comment #20
luke_nuke commentedHi, joncjordan.
1. Are you sure you have checked out the newest 8.8.x branch?
2. Do any other core patches apply without issues?
3. Try to remove the core directory and re-run
composer install.I just re-tested it and it should apply without issues, so it look like a local issue.
Comment #21
joncjordan commentedNo I was running against latest core stable release 8.7.6 - Aug 07 2019. You're probably right about it being a local issue. Either way, the patch DOES seem to apply as the files have the changes and the new files are where they need to be.
I've tested the functionality and it works great. It seems to be a promising approach to nested subsections and something the will become a realized need as more and more people start using layouts. Sure you can build endless layouts ahead of time, but sometimes you don't know what layout you need until you're actually building the page. This offers site builders that flexibility.
We'd likely need some method in our custom layout class for subsection removals. I wonder how you might handle that. Overall great work so far.
Comment #22
luke_nuke commentedOh, this patch was made for 8.8.x branch, that's why you got such big offsets. I'm glad it applied though. If this feature would be agreed on, I think it could be backported to 8.7.x branch, as it's completely BC in its current form.
Removal from the forms perspective isn't difficult, although you also have blocks assigned to these dynamic regions so in theory you would need to access node context from your Layout class and modify it on submit, to remove these blocks, or at the very least you could require their removal during the form validation, also by accessing node context. Again, it can be handled.
I see there are also few coding standards issues in the last patch, I will fix them soon.
Added:
Actually I think there would be two contexts that you would have to deal with: the one for display - when we are talking about non per-node layouts, and the node one if we are talking about per-node layouts.
Comment #23
luke_nuke commentedFixed coding standards issues.
Comment #24
luke_nuke commentedComment #25
luke_nuke commentedComment #26
luke_nuke commentedComment #28
tim.plunkettStill needs a review. Just tagging.
Comment #30
tim.plunkettThis is the crux of the patch, but still doesn't feel right to me.
Consider that this is changing only one of the 8 places that
getRegionNames()is called in core.One other idea would be to add a getRegionNames() method to LayoutInterface, and have LayoutDefault delegate to the plugin, allowing classes to provide their own implementation. And then document on the plugin's method that it is only intended for internal use...
Nit: should be
$region_namesSimilar problem to the above
And again
Nit: include trailing comma
Ideally this would actually be dynamic, and not need to be hardcoded. Compare to layout.html.twig which uses
getRegionNamesdynamically right now.Comment #33
tim.plunkettNothing in the issue fork...
Comment #34
mradcliffeI performed Novice Triage on this issue. I added the Easy out of the Box, NorthAmerica2021, Novice and Needs issue summary update tags.
I think the issue summary could use an update based on some pending questions brought up in Tim's review. Additionally the patch probably needs to be re-rolled at this point and we can fix the issues in the code review.
After that, start a thread in the #easy-out-of-the-box Slack channel to ask for help / discuss Tim's 1st point in the review in #30.
Comment #35
suresh prabhu parkala commentedJust a re-roll against 9.2.x.
Comment #36
mradcliffeLooks like we need to add "dynamicregions" to the cspell dictionary following instructions in this change record: https://www.drupal.org/node/3122084
Comment #37
mradcliffeBack to Needs work, removed needs reroll.
Comment #38
bitmap0100 commentedI'm going to apply the patch and make the changes, test, then upload a new patch.
Comment #39
bitmap0100 commentedI added dynamicregions to the cspell dictionary and recombined that with the latest patch I hope I did this correctly.
Comment #41
larowlanI was asked to look at this by James LeMosy.
I'm not sure this is needed, there's already an api in core for dynamic regions, LayoutDefinition::getRegions can be subclassed.
Here's a video of it in use.
https://drupal.slack.com/archives/C0T77ULN9/p1598562327038800
And here's a contrib module that does something similar: https://www.drupal.org/project/lb_tabs
So I think this can be closed works as designed.
Comment #42
jlemosy commentedThanks, larowlan. So, if it is already part of core, what needs to be done to make it functional? It seems like progress on this has stalled, but it is still a frequently-requested feature. I've tried my hand at picking up where this issue left off, but I have had a hard time understanding how to use the API.
Comment #43
larowlanI guess the next step is to convey what part of it isn't functional. The links above show it is in use already, so it would be good to understand where the gaps are
Comment #44
jlemosy commentedSorry, "make it functional" was not exactly what I meant. I am just unclear how to use the API to achieve the original goal of this issue, which is to be able to add multiple layouts within sections.
Comment #45
larowlanOk, so I think perhaps we need more docs and examples
I'll try and find some time for that next week
Comment #46
jlemosy commentedThanks, that would be much appreciated!
Comment #47
larowlanI wrote a blog-post about achieving dynamic tabs
That's the code that corresponds with this video
Comment #51
dalemoore commentedHi @larowlan, the video you've linked to twice on here isn't viewable. It looks like you have to be part of the Drupal Association Slack? Can you repost the video so we can see how your dynamic tabs works?
Comment #52
larowlanSure
Comment #53
larowlanAlso, I recommend joining the slack, the community is a huge part of the power of Drupal
Comment #54
dalemoore commentedThanks, that's helpful to see how it worked.
I couldn't get signing up for Drupal Slack to work before (though I'm a new Slack user). It required either a Google or Apple account or an @association.drupal.org email. The "Continue with Google" method wouldn't work. But I found the link under "Joining" here and was able to create an account on there. Thanks.
Comment #56
wim leersAFAICT @larowlan showed in the blog post he created 3 years ago at https://www.previousnext.com.au/blog/creating-layout-plugin-dynamic-regions that this is already supported?
@Luke_Nuke: can you confirm that your use case can indeed be achieved without modifying Drupal core?
Can we close this issue? 🤞
Comment #57
dqdThat would be great news and +1 for what @WimLeers sad: If the scope of this issue is already covered in core let's move on and close this. Would be great if @Nuke_Luke can confirm this. Apart from that I will go and test now to confirm. Other users who can confirm that is works already would be helpful too. Regarding our policies I set it to PMNMI for now(?)
Comment #58
danielvezaSolutions for how to achieve this have been posted in this video and it has been over three months since the last update where this was marked as Postponed (maintainer needs more info).
Therefore I'm marking this one as closed. Please feel free to reopen or open a follow up ticket if you think there is still work to be done here.
Thanks!