Overview
This is in support of #3455629: [PP-1] [META] 7. Content Templates — aka "default layouts" — affects the tree+props data model, based on preliminary design discussions that happened 2 days ago, between @lauriii, @jessebaker, @callumharrod and I.
I promised to get an initial outline going of what a new config entity could look like.
Legend:
- ⚜️
- Product question → @lauriii
- 🪄
- Design question → @callumharrod
⚜️- Product question answered
- 🪄
- Design question answered
There's AT LEAST two these major challenges, both design-wise and technically:
- ✅
⚜️View modes: we allow the Site Builder to craft a different component tree per view mode.XB aims to support multiple view modes from the start, and one XB Content Type Template per view mode. See #7.2 for details provided by @lauriii.
- 🪄
⚜️Internal/exposed or Locked/unlocked akaExposed Content Type Slots
aka unlocked subtrees: allow the Site the Site Builder-defined component tree for the content entity type+bundle+view mode to be customized by the Content Creator:- 0 exposed Content Type Slots means no freedom (Content Creator can only enter values for the content entity, not modify the component tree)
- 1 exposed Content Type Slot at the root means complete freedom
- anything in between (1–N exposed content type slots in the component tree) means selective freedom: freedom in some parts, but other parts are locked
(For example: "product" content entities may have rigid presentation of key product information, but unlocked/exposed
Hero
andstory behind the product
subtrees.)The design/UX fir this is not yet defined.
Related:
- #3498819-5: [Needs design] Adjust representation of Regions in the Layers UI
\Drupal\layout_builder\LayoutBuilderOverridableInterface
- 🪄
⚜️What about the existingEntityViewDisplay(s)? As soon as a content entity type+bundle is opted in to XB (see: #3498525: [later phase] [META] Allow XB to be used on any content entity type (bundle), as long as it has certain entity type characteristics), its existingEntityViewDisplay(s) if any are ignored (not deleted, to allow opting out of XB again and be back at the prior state).Design-wise: are they hidden (from the UI) as soon as you create an XB content type template?
- 🪄⚜️Must every field for the content entity type+bundle be used somewhere in the component tree, because otherwise some data is invisible? That seems wrong, because some fields might be meta fields. For that, we have #3463991: [RESEARCH] How to identify all meta fields for an arbitrary content entity?.
So, rephrasing: assuming that we know which of the structured data are "meta" vs not, must every non-meta field be used somewhere in the component tree?
↪ Insight-providing commit - ✅
⚜️Are we okay with either ignoring or breaking existinghook_entity_display_build_alter()implementations? This hook very much assumes the linear/sequential design ofEntityViewDisplays (a flat list of fields, each presented by some formatter), but that won't be true for XB's Content Type Template's component tree. @lauriii has confirmed this is okay. - ✅
🪄⚜️Gotcha forExposed Content Type Slots
aka unlocked subtrees. Do we allow creating none, and hence having zero creative freedom for the Content Creator other than populating the content entity form in thePage Data
tab? Or do we require >=1? Can that one be the entire component tree, granting full creative freedom?0, 1 or N must be supported. Design still needed. Folded into question 2.
- 🪄⚜️Gotcha for
Exposed Content Type Slots
aka unlocked subtrees. However many we decide to allow per the above: do we allow those exposed Content Type Slots to contain be populated using structured data of the content entity (i.e. usingDynamicPropSources) or not? If we do allow this, it's possible that some structured data will NOT be exposed.I can think of use cases, but then this needs affordances (or worst case: confirmation modals) to convey this consequence to the Site Builder.
- 🪄Gotcha for
Exposed Content Type Slots
aka unlocked subtrees. They need (translatable) human labels, arguably also machine names (this would be one way of adding view mode support in the future — then if a Content Type Slot with the same machine name appears in multiple view modes' Content Type Template, the same per-entity override is used). - 🪄
⚜️Gotcha forExposed Content Type Slots
aka unlocked subtrees. Once content exists that populates a Content Type Slot, it can NEVER be removed (its label can still change though), otherwise values in individual content entities' XB fields would disappear!
Per @lauriii, this is considered acceptable:When building a Content Type Template, the UI must warn user when they are taking actions that may be considered to result in perceived data loss. Stale data can remain in each content entity’s XB field until content has been resaved.
Still needed: design/UX.
- 🪄⚜️Translatability. A component tree will typically contain some static values. For example: a button that points to the current content entity's webshop URL, with that URL being mapped into it dynamically (
DynamicPropSource), but the button label being defined statically because it makes no sense as structured data (so:StaticPropSource).Any component input populated using a
DynamicPropSourceis automatically translatable, because it fetches the value from the host content entity.But any component input populated using a
StaticPropSourceis NOT translatable. At least not at this time. Because component trees in config are currently enormous JSON blobs. That's not compatible with Drupal's config translation functionality.
(A third technical challenge: translatability of this config. Specifically, the exposed/unlocked subtrees' labels should be translatable. This should avoid the mistakes e.g. CKEditor 5 made — see #3382464: [Style] CKEditor 5 styles config storage is not compatible with config ovverides.)
Proposed resolution
Details TBD.
Out of scope:
- Explicit test coverage that proves deleting a configurable field from a content entity type (+bundle) is prevented if it is being used in a Content Type Template. For that: #3452848: [PP-1] Test coverage to prove configurable fields cannot be deleted from content entity types if they are used in XB Content Type Template.
P.S.: I think the name ContentTypeTemplate is not great. We might land on a better name later, when development on this truly begins. Until then, we've always referred to it using this term, so might as well use it for now. It's purely internal anyway.
User interface changes
None; this is purely supporting back-end infrastructure.
Issue fork experience_builder-3511366
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:
- 3511366-poc-content-type-template-config-entity-type
changes, plain diff MR !754
Comments
Comment #3
wim leersUpdated issue summary with new design/product questions that I identified while building the PoC to predict future technical challenges: now there's not 2 but 9 challenges! 😅
FYI: I landed on the term for what @jessebaker alluded to in #3498819-5: [Needs design] Adjust representation of Regions in the Layers UI.
Note that I built this config entity type PoC while taking into account all discussion-surfaced requirements outlined in the meta.
Omitted from the work I did:
Comment #4
wim leersIn not-yet-public docs, I encountered a functional requirement I had not read before, but it makes sense (it ties into XB product requirement
14. Configuration management), it reads:… which is the non-UI, but config-management-level-enforced equivalent of point 9 I surfaced in the issue summary above: .
Comment #5
wim leersAdditional notes while digging into this problem space and trying to think through everything:
DynamicPropSource), but the button label being defined statically because it makes no sense as structured data (so:StaticPropSource).Any component input populated using a
DynamicPropSourceis automatically translatable, because it fetches the value from the host content entity.But any component input populated using a
StaticPropSourceis NOT translatable. At least not at this time. Because component trees in config are currently enormous JSON blobs. That's not compatible with Drupal's config translation functionality.(More to follow.)
Comment #6
catchIt's common to have fields that are only used on specific view modes, including ones that don't show on the default. An example would be a 'short summary' field that only shows on a card view mode and not on the main content.
Comment #7
wim leersBased on discussing #3455629: [PP-1] [META] 7. Content Templates — aka "default layouts" — affects the tree+props data model with @lauriii this morning, a few things surfaced:
Per @lauriii, this is necessary to allow iterating on XB Content Type Templates. There MUST be a "heavy" warning, an explicit confirmation by the Site Builder, but this must be allowed.
defaultview mode will be customizable per entity. So, ateaserview mode for example would look the same for all entities of that content entity type+bundle: the same exact XB component tree would be used for all (i.e. the one in the "teaser" Content Type Template config entity), while of course resolving the structured data mapped into the component tree (i.e. usingDynamicPropSources).This avoids the entire "keep Content Type Slots in sync across all Content Type Templates (one per view mode) for this content entity type+bundle" problem. 👍
EntityViewDisplays are ignored. Those that don't have an XB Content Type Template defined yet render nothing.Note: this will change in the far future per @lauriii: he intends to allow specifying an XB element (see #3455036: Clarify "components" vs "elements" vs "patterns") to be associated with each field type, which essentially becomes the XB equivalent of "the default formatter".
It's not clear to me yet how that avoids the
problem described in #3462219: [META] Support alternative renderings of prop data added for the 'full' view mode such as for search indexing or newsletters, though.
Comment #9
wim leersReflected #7 in the PoC MR: https://git.drupalcode.org/project/experience_builder/-/merge_requests/7....
Comment #10
callumharrod commentedResponses
Hey @wim-leers - the answers below is just my take on the problems listed 😄
I think a good way to think of the creation of templates is similar to how it would be done with a headless CMS, in that the content model and content creation are separate from the templating layer.
If a user wanted to they could make the entire page a single slot, but they would still need to add a component and expose the slot in the exact same way as exposing any other slot.
Questions from me:
Comment #11
wim leersI see that I failed to update the issue summary with @lauriii's input from #7. Now updated. Some questions are fully answered, some are merged.
Comment #12
wim leers#10:
RE: your question: per discussion this morning with @lauriii: as the Site Builder modifying/creating an XB Content Type Template, you have complete freedom to completely change your mind about adding/removing exposed Content Type Slots. So the answer is: "yes, you can revert".
my-sectionSDC would then be able to have inputs (image, text next to the image) that are defined in the Content Type Template, but if the Content Creator chooses, they could modify the inputs without the ability to add more component instances(And no, that's not what I meant: I was referring to disallowing deleting Content Type Slots, but @lauriii has since said this is fine, and it's fine to lose Content Creator-crafted data.)
Comment #13
catchafaict this is incompatible with #3483301: Create a configurable search api processor for XB data - which is suggesting not rendering XB at all when configuring search indexing, except via the special search API processor. I'm not convinced by that issue yet (and left a question on there which hasn't been answered yet) but has that been abandoned or is this a mistake? Using #3483301: Create a configurable search api processor for XB data would mean either not using a view mode at all in search indexing, or a view mode minus XB data that only shows entity fields, with the XB data then provided by the separate processor.
If you had to use the default view mode and the processor it would mean double-rendering when search indexing.
This seems unnecessarily restrictive and also unenforceable. e.g. you can configure search API views to return results from a search index, then use any view mode on the entity that you like right now - like displaying search results as a grid of cards or whatever.
Comment #14
wim leersThis blocks pretty much everything in #3455629: [PP-1] [META] 7. Content Templates — aka "default layouts" — affects the tree+props data model.
In more concrete terms, it blocks #3452848: [PP-1] Test coverage to prove configurable fields cannot be deleted from content entity types if they are used in XB Content Type Template.
Comment #15
borisson_Rendering Search results in the same way as the content's detail page doe not seem like the most commonly chosen way of configuring it. I did a quick poll at the contribution room at Drupal Mountain Camp, and everyone who answered me always configures some kind of teaser, card or search-result view mode when rendering a search result page.
For the indexation part, I also don't see a way that would remove those pollutions without having a seperate view mode, but I personally think that's not as bad. When needing complete control over what all is indexed, it should be indexed using fields, because then it's possible to boost per-field. It gives a lot more control.
This however would mean something like https://www.drupal.org/project/layoutbuilder_search_api for all components/props, which is another approach that could be taken. This removes the double rendering that catch talked about in #13.
Comment #16
catchI think that #3477428: [PP-1] Refactor (or decide not to) the XB field type to be multi-valued, to de-jsonify the tree, and to reference the field_union type of the prop values would also handle this - because the props would be available as field data to search API then without an additional translation layer.
Comment #17
lauriiiWe should definitely support configuring customizing how content is displayed in the search results page. I'm not sure we need to do that in this issue though because it might make sense to design and implement solution specifically for that, because it's such a common use case.
Comment #18
catch@lauriii that's the opposite of what this says in #7:
is there a miscommunication somewhere?
If search results use a view mode for rendering (which a lot of sites do), then it shouldn't require any special handling in XB, it just needs to not break using view modes for rendering.
The place where it might get tricky is 'results snippet' type situations where the search result is trying to show the searched keywords in the context of the results. I'm not sure how that's generally implemented in search API or not or whether it would need special handling. For me that's the least important aspect to worry about - it's the initial indexing that feels unresolved still.
Comment #19
wim leers#17: that's not matching what you said earlier.
Unless you meant that that would be the initial state, and you intend to only use the default content type template for search indexing and a different content type template for presenting search results?
But if we do that, then it follows that the information that was matched is absent from the search result's presentation, meaning highlighting is impossible.
Comment #20
lauriiiI feel like we are talking past each other with different use cases and scenarios and concerns that are not being impacted by Experience Builder. My understanding is that there are two primary ways of configuring Search API: text based search and faceted search (as well as combination of the two).
When using text based search and the intent is to index the whole page, the "Rendered HTML output" with the default content type template (same as what user would see when navigating to the page) should be used. Search API would allow configuring other view modes to be used for indexing but those wouldn't include the content added to the slots, but only content that is being rendered through those view modes. So long as we keep view modes working, this should all work without specifically having to accommodate them. This is the same experience that you would get when using Layout Builder today.
When using the text based search, the results would often display an excerpt which is generated based on what is in the index. This allows highlighting the text that is being matched. This shouldn't require any special accommodations from XB so long as what is stored in the index is sensible.
When implementing a faceted search, it's common to configure the results to display to using a view mode like cards or search result. In this scenario, text is not being highlighted. Even in this scenario, the index may include the full rendered page, highlighting that the index is a separate concern from the presentation of the results.
Comment #21
larowlanLayout builder uses third party settings on the entity view display for this rather than a separate config entity.
I think that is a valid approach worth considering.
I also think #3462238: Decouple tree storage, introduce tree storage plugins is important here because our present code-path
\Drupal\experience_builder\InternalXbFieldNameResolver::getXbFieldNamefalls over hereComment #22
wim leersI considered that, but I think there are strong reasons against it:
EntityViewDisplayconfig entities are not yet fully validatable, with an unclear ETA for that in core. This would significantly weaken XB's14. Configuration managementproduct requirement.FieldBlock, which contains field formatter settings (which IIRC are treated as the default formatter settings when placing a field block). So, most of the information in theEntityViewDisplayis still somewhat relevant when using LB.\Drupal\layout_builder\Hook\LayoutBuilderHooks::entityTypeAlter()that causes allEntityViewDisplayconfig entities to use its ownLayoutBuilderEntityViewDisplayclass instead! XB cannot do the same, or it'd either break LB (race: who wins?) or require LB (definitely not the intent either). That's in the MR:I don't think #3462238: Decouple tree storage, introduce tree storage plugins is a hard blocker for this issue, but I definitely agree it'd be nice to solve, especially in light of providing a smooth transition from Layout Builder into Experience Builder! (Note this will require either duplicating LB's
FieldBlockin XB, or moving it to another module — otherwise Layout Builder will be required forever even on sites that have all their data moved from LB to XB!)Comment #24
mglaman+1 to this approach! I had forgotten this issue and proof of concept existed when I had a chat with @phenaproxima where we chatted over this idea. The outcome happened to be nearly the same as this issue, except I didn't consider the possibility of having the config entity implement EntityViewDisplayInterface so we could swap it for the view display object.
I pushed a test and some wonky code to further explore. I like that it mimics how page regions work. If something exists we execute a new code path versus embedding into the existing code path.
Comment #25
mglaman#3366425: move static collect*() methods from display entity classes to EntityDisplayRepository would make this much easier. Then we could swap the display objects not and need hook_entity_prepare_view and hook_entity_view_alter
Comment #26
wim leers@callumharrod walked us (@lauriii, myself and David Bee) through the designs he crafted. All of us asked various questions. The PoC here is still aligned with the designs, with one exception: we're not going to be exposing a component instance as an exposed slot, but a slot in a component instance.
Walked @phenaproxima through that change, and captured this change with him together, as a way to hand this off to him: https://git.drupalcode.org/project/experience_builder/-/merge_requests/7... 😊 (I'm still around, obviously!)
Basically, content type template config entities will look like this:
and the one for the
defaultview mode will be able to specify content type slots on top of that:Comment #28
wim leers@phenaproxima has split this into multiple child issues 👍
Comment #29
wim leersEpic work on #3517741: Content templates, part 1: introduce the `ContentTemplate` config entity type, @phenaproxima — it is in! 😄 🚀
Also: detailed guidance/pointers at:
Comment #30
wim leersFYI: #3518336: When a field instance used by a `ContenTemplate` is deleted, or a field-type providing module is uninstalled, replace affected `inputs` with default `StaticPropSource` has been fixed 🥳
Comment #31
wim leersUpdate for #29:
Comment #32
lauriiiComment #34
wim leersI think this meta issue has served its purpose.
As this meta issue's summary says: — and the design + product decisions have been made, the config entity type exists, the fundamental infrastructure exists thanks to @phenaproxima:
… with now a subset of that infrastructure getting a UI built before 1.0 — see #3541000: [META] Content templates UI for 1.0: only nodes, no exposed slots, no replacement for the view mode/display UI
We still have #3455629 around for delivering the full scope — see #3455629-51: [PP-1] [META] 7. Content Templates — aka "default layouts" — affects the tree+props data model.