Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FSE: Fall back to next best template in hierarchy when querying through REST API #37258

Closed
wants to merge 19 commits into from

Conversation

ockham
Copy link
Contributor

@ockham ockham commented Dec 9, 2021

Description

Purports to fix #36648, as an alternative approach to #37054. For more background, see #37054 (comment)

I think that this PR is in good enough shape for a first round of reviews. I'm especially curious if people think that there are any risks in changing the gutenberg_get_block_template function (which was supposed to return a template specified by its ID) to fall back to the "next best" template if the one queried isn't available. My personal impression is that that's fine, since that better represents how the template hierarchy works, but I might be missing some cases.

Note that this doesn't yet support "full" template hierarchy fallbacks: We only currently fall back within a template type, e.g. page-${slug} to page-${id} to page, and eventually to the catch-all index template, but not between template types, e.g. from page to singular, or from single-post to single to singular. I'd implement those as part of a separate PR if we agree on the direction.

We also need to figure out if this should be backported to WP 5.9 or not.

How has this been tested?

Apply the following patch on top of this branch to ensure you're using GB's compat layer template resolution code (rather than Core's).

Patch
diff --git a/lib/compat/wordpress-5.9/template-parts.php b/lib/compat/wordpress-5.9/template-parts.php
index 102c9275be..6b9b8bc00a 100644
--- a/lib/compat/wordpress-5.9/template-parts.php
+++ b/lib/compat/wordpress-5.9/template-parts.php
@@ -13,7 +13,7 @@
 
 // Only run any of the code in this file if the version is less than 5.9.
 // wp_list_users was introduced in 5.9.
-if ( ! function_exists( 'wp_list_users' ) ) {
+if ( true || ! function_exists( 'wp_list_users' ) ) {
        /**
         * Registers block editor 'wp_template_part' post type.
         */
diff --git a/lib/compat/wordpress-5.9/templates.php b/lib/compat/wordpress-5.9/templates.php
index d5bdf1c55c..c0aa2a2e03 100644
--- a/lib/compat/wordpress-5.9/templates.php
+++ b/lib/compat/wordpress-5.9/templates.php
@@ -13,7 +13,7 @@
 
 // Only run any of the code in this file if the version is less than 5.9.
 // wp_list_users was introduced in 5.9.
-if ( ! function_exists( 'wp_list_users' ) ) {
+if ( true || ! function_exists( 'wp_list_users' ) ) {
        /**
         * Registers block editor 'wp_template' post type.
         */
  1. Enable the Twenty Twenty-Two theme.
  2. Go to the Site Editor.
  3. Click the 'W' logo menu in the top left corner.
  4. In the sidebar that opens, select 'Templates'.
  5. Verify that in the list of the theme's templates that opens, the "Search" template isn't present.
  6. Click the 'Add New' button in the top right corner.
  7. In the dropdown that opens, click on the 'Search' template.
  8. Verify that the site editor opens, with the index template loaded. (This is to ensure parity with the frontend, which would also use the index template as a fallback when there's no dedicated search template.)
  9. Make a change to the template. Make sure it's not to the header or footer template parts, nor to the post content. Ideally, insert a Paragraph block with some text right under the header (and above the content). (Verify that it's a direct child of the template by checking in the bottom breadcrumbs bar that it shows up as "Document > Paragraph.)
  10. Save the template.
  11. Once saved, navigate back to the 'Templates' screen (via the top left 'W' button that opens the sidebar).
  12. Verify that there's now a 'search' template at the top of the list of the theme's templates.
  13. Verify that it's distinct from the 'index' template.

Ideally, give it some more smoke testing -- whatever you can think of makes sense: delete the template again; find a way to test it on the frontend; etc.

Screenshots

Before adding the 'search' template:

image

After adding the 'search' template:

image

Types of changes

Somewhere in between a bug fix and new feature I guess?

@github-actions
Copy link

github-actions bot commented Dec 9, 2021

Size Change: -58 B (0%)

Total Size: 1.13 MB

Filename Size Change
build/edit-site/index.min.js 35.7 kB -58 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 960 B
build/admin-manifest/index.min.js 1.1 kB
build/annotations/index.min.js 2.75 kB
build/api-fetch/index.min.js 2.21 kB
build/autop/index.min.js 2.12 kB
build/blob/index.min.js 459 B
build/block-directory/index.min.js 6.28 kB
build/block-directory/style-rtl.css 1.01 kB
build/block-directory/style.css 1.01 kB
build/block-editor/default-editor-styles-rtl.css 378 B
build/block-editor/default-editor-styles.css 378 B
build/block-editor/index.min.js 140 kB
build/block-editor/style-rtl.css 14.6 kB
build/block-editor/style.css 14.6 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 65 B
build/block-library/blocks/archives/style.css 65 B
build/block-library/blocks/audio/editor-rtl.css 58 B
build/block-library/blocks/audio/editor.css 58 B
build/block-library/blocks/audio/style-rtl.css 111 B
build/block-library/blocks/audio/style.css 111 B
build/block-library/blocks/audio/theme-rtl.css 125 B
build/block-library/blocks/audio/theme.css 125 B
build/block-library/blocks/block/editor-rtl.css 161 B
build/block-library/blocks/block/editor.css 161 B
build/block-library/blocks/button/editor-rtl.css 470 B
build/block-library/blocks/button/editor.css 470 B
build/block-library/blocks/button/style-rtl.css 560 B
build/block-library/blocks/button/style.css 560 B
build/block-library/blocks/buttons/editor-rtl.css 291 B
build/block-library/blocks/buttons/editor.css 291 B
build/block-library/blocks/buttons/style-rtl.css 275 B
build/block-library/blocks/buttons/style.css 275 B
build/block-library/blocks/calendar/style-rtl.css 207 B
build/block-library/blocks/calendar/style.css 207 B
build/block-library/blocks/categories/editor-rtl.css 84 B
build/block-library/blocks/categories/editor.css 83 B
build/block-library/blocks/categories/style-rtl.css 79 B
build/block-library/blocks/categories/style.css 79 B
build/block-library/blocks/code/style-rtl.css 90 B
build/block-library/blocks/code/style.css 90 B
build/block-library/blocks/code/theme-rtl.css 134 B
build/block-library/blocks/code/theme.css 134 B
build/block-library/blocks/columns/editor-rtl.css 210 B
build/block-library/blocks/columns/editor.css 208 B
build/block-library/blocks/columns/style-rtl.css 502 B
build/block-library/blocks/columns/style.css 501 B
build/block-library/blocks/comment-template/style-rtl.css 127 B
build/block-library/blocks/comment-template/style.css 127 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/cover/editor-rtl.css 546 B
build/block-library/blocks/cover/editor.css 547 B
build/block-library/blocks/cover/style-rtl.css 1.22 kB
build/block-library/blocks/cover/style.css 1.22 kB
build/block-library/blocks/embed/editor-rtl.css 488 B
build/block-library/blocks/embed/editor.css 488 B
build/block-library/blocks/embed/style-rtl.css 417 B
build/block-library/blocks/embed/style.css 417 B
build/block-library/blocks/embed/theme-rtl.css 124 B
build/block-library/blocks/embed/theme.css 124 B
build/block-library/blocks/file/editor-rtl.css 300 B
build/block-library/blocks/file/editor.css 300 B
build/block-library/blocks/file/style-rtl.css 255 B
build/block-library/blocks/file/style.css 255 B
build/block-library/blocks/file/view.min.js 322 B
build/block-library/blocks/freeform/editor-rtl.css 2.44 kB
build/block-library/blocks/freeform/editor.css 2.44 kB
build/block-library/blocks/gallery/editor-rtl.css 966 B
build/block-library/blocks/gallery/editor.css 970 B
build/block-library/blocks/gallery/style-rtl.css 1.63 kB
build/block-library/blocks/gallery/style.css 1.62 kB
build/block-library/blocks/gallery/theme-rtl.css 122 B
build/block-library/blocks/gallery/theme.css 122 B
build/block-library/blocks/group/editor-rtl.css 159 B
build/block-library/blocks/group/editor.css 159 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 114 B
build/block-library/blocks/heading/style.css 114 B
build/block-library/blocks/html/editor-rtl.css 332 B
build/block-library/blocks/html/editor.css 333 B
build/block-library/blocks/image/editor-rtl.css 810 B
build/block-library/blocks/image/editor.css 809 B
build/block-library/blocks/image/style-rtl.css 507 B
build/block-library/blocks/image/style.css 511 B
build/block-library/blocks/image/theme-rtl.css 124 B
build/block-library/blocks/image/theme.css 124 B
build/block-library/blocks/latest-comments/style-rtl.css 284 B
build/block-library/blocks/latest-comments/style.css 284 B
build/block-library/blocks/latest-posts/editor-rtl.css 137 B
build/block-library/blocks/latest-posts/editor.css 137 B
build/block-library/blocks/latest-posts/style-rtl.css 528 B
build/block-library/blocks/latest-posts/style.css 527 B
build/block-library/blocks/list/style-rtl.css 94 B
build/block-library/blocks/list/style.css 94 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 493 B
build/block-library/blocks/media-text/style.css 490 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 649 B
build/block-library/blocks/navigation-link/editor.css 650 B
build/block-library/blocks/navigation-link/style-rtl.css 94 B
build/block-library/blocks/navigation-link/style.css 94 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 299 B
build/block-library/blocks/navigation-submenu/editor.css 299 B
build/block-library/blocks/navigation-submenu/view.min.js 343 B
build/block-library/blocks/navigation/editor-rtl.css 1.91 kB
build/block-library/blocks/navigation/editor.css 1.92 kB
build/block-library/blocks/navigation/style-rtl.css 1.8 kB
build/block-library/blocks/navigation/style.css 1.8 kB
build/block-library/blocks/navigation/view.min.js 2.82 kB
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 377 B
build/block-library/blocks/page-list/editor.css 377 B
build/block-library/blocks/page-list/style-rtl.css 172 B
build/block-library/blocks/page-list/style.css 172 B
build/block-library/blocks/paragraph/editor-rtl.css 157 B
build/block-library/blocks/paragraph/editor.css 157 B
build/block-library/blocks/paragraph/style-rtl.css 273 B
build/block-library/blocks/paragraph/style.css 273 B
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-comments-form/style-rtl.css 446 B
build/block-library/blocks/post-comments-form/style.css 446 B
build/block-library/blocks/post-comments/style-rtl.css 509 B
build/block-library/blocks/post-comments/style.css 509 B
build/block-library/blocks/post-excerpt/editor-rtl.css 73 B
build/block-library/blocks/post-excerpt/editor.css 73 B
build/block-library/blocks/post-excerpt/style-rtl.css 69 B
build/block-library/blocks/post-excerpt/style.css 69 B
build/block-library/blocks/post-featured-image/editor-rtl.css 721 B
build/block-library/blocks/post-featured-image/editor.css 721 B
build/block-library/blocks/post-featured-image/style-rtl.css 153 B
build/block-library/blocks/post-featured-image/style.css 153 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 391 B
build/block-library/blocks/post-template/style.css 392 B
build/block-library/blocks/post-terms/style-rtl.css 73 B
build/block-library/blocks/post-terms/style.css 73 B
build/block-library/blocks/post-title/style-rtl.css 80 B
build/block-library/blocks/post-title/style.css 80 B
build/block-library/blocks/preformatted/style-rtl.css 103 B
build/block-library/blocks/preformatted/style.css 103 B
build/block-library/blocks/pullquote/editor-rtl.css 198 B
build/block-library/blocks/pullquote/editor.css 198 B
build/block-library/blocks/pullquote/style-rtl.css 389 B
build/block-library/blocks/pullquote/style.css 388 B
build/block-library/blocks/pullquote/theme-rtl.css 167 B
build/block-library/blocks/pullquote/theme.css 167 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/style-rtl.css 234 B
build/block-library/blocks/query-pagination/style.css 231 B
build/block-library/blocks/query/editor-rtl.css 131 B
build/block-library/blocks/query/editor.css 132 B
build/block-library/blocks/quote/style-rtl.css 187 B
build/block-library/blocks/quote/style.css 187 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/rss/editor-rtl.css 202 B
build/block-library/blocks/rss/editor.css 204 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 165 B
build/block-library/blocks/search/editor.css 165 B
build/block-library/blocks/search/style-rtl.css 397 B
build/block-library/blocks/search/style.css 398 B
build/block-library/blocks/search/theme-rtl.css 64 B
build/block-library/blocks/search/theme.css 64 B
build/block-library/blocks/separator/editor-rtl.css 99 B
build/block-library/blocks/separator/editor.css 99 B
build/block-library/blocks/separator/style-rtl.css 245 B
build/block-library/blocks/separator/style.css 245 B
build/block-library/blocks/separator/theme-rtl.css 172 B
build/block-library/blocks/separator/theme.css 172 B
build/block-library/blocks/shortcode/editor-rtl.css 474 B
build/block-library/blocks/shortcode/editor.css 474 B
build/block-library/blocks/site-logo/editor-rtl.css 744 B
build/block-library/blocks/site-logo/editor.css 744 B
build/block-library/blocks/site-logo/style-rtl.css 181 B
build/block-library/blocks/site-logo/style.css 181 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 84 B
build/block-library/blocks/site-title/editor.css 84 B
build/block-library/blocks/social-link/editor-rtl.css 177 B
build/block-library/blocks/social-link/editor.css 177 B
build/block-library/blocks/social-links/editor-rtl.css 670 B
build/block-library/blocks/social-links/editor.css 669 B
build/block-library/blocks/social-links/style-rtl.css 1.32 kB
build/block-library/blocks/social-links/style.css 1.32 kB
build/block-library/blocks/spacer/editor-rtl.css 307 B
build/block-library/blocks/spacer/editor.css 307 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 471 B
build/block-library/blocks/table/editor.css 472 B
build/block-library/blocks/table/style-rtl.css 481 B
build/block-library/blocks/table/style.css 481 B
build/block-library/blocks/table/theme-rtl.css 188 B
build/block-library/blocks/table/theme.css 188 B
build/block-library/blocks/tag-cloud/style-rtl.css 146 B
build/block-library/blocks/tag-cloud/style.css 146 B
build/block-library/blocks/template-part/editor-rtl.css 560 B
build/block-library/blocks/template-part/editor.css 559 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 87 B
build/block-library/blocks/verse/style.css 87 B
build/block-library/blocks/video/editor-rtl.css 569 B
build/block-library/blocks/video/editor.css 570 B
build/block-library/blocks/video/style-rtl.css 173 B
build/block-library/blocks/video/style.css 173 B
build/block-library/blocks/video/theme-rtl.css 124 B
build/block-library/blocks/video/theme.css 124 B
build/block-library/common-rtl.css 910 B
build/block-library/common.css 908 B
build/block-library/editor-rtl.css 10 kB
build/block-library/editor.css 10 kB
build/block-library/index.min.js 165 kB
build/block-library/reset-rtl.css 474 B
build/block-library/reset.css 474 B
build/block-library/style-rtl.css 10.9 kB
build/block-library/style.css 10.9 kB
build/block-library/theme-rtl.css 675 B
build/block-library/theme.css 679 B
build/block-serialization-default-parser/index.min.js 1.09 kB
build/block-serialization-spec-parser/index.min.js 2.79 kB
build/blocks/index.min.js 46.3 kB
build/components/index.min.js 215 kB
build/components/style-rtl.css 15.5 kB
build/components/style.css 15.5 kB
build/compose/index.min.js 11.2 kB
build/core-data/index.min.js 13.2 kB
build/customize-widgets/index.min.js 11.4 kB
build/customize-widgets/style-rtl.css 1.5 kB
build/customize-widgets/style.css 1.49 kB
build/data-controls/index.min.js 631 B
build/data/index.min.js 7.49 kB
build/date/index.min.js 31.9 kB
build/deprecated/index.min.js 485 B
build/dom-ready/index.min.js 304 B
build/dom/index.min.js 4.5 kB
build/edit-navigation/index.min.js 16 kB
build/edit-navigation/style-rtl.css 3.76 kB
build/edit-navigation/style.css 3.76 kB
build/edit-post/classic-rtl.css 492 B
build/edit-post/classic.css 494 B
build/edit-post/index.min.js 29.5 kB
build/edit-post/style-rtl.css 7.16 kB
build/edit-post/style.css 7.15 kB
build/edit-site/style-rtl.css 6.61 kB
build/edit-site/style.css 6.6 kB
build/edit-widgets/index.min.js 16.5 kB
build/edit-widgets/style-rtl.css 4.18 kB
build/edit-widgets/style.css 4.18 kB
build/editor/index.min.js 37.9 kB
build/editor/style-rtl.css 3.75 kB
build/editor/style.css 3.74 kB
build/element/index.min.js 3.29 kB
build/escape-html/index.min.js 517 B
build/format-library/index.min.js 6.58 kB
build/format-library/style-rtl.css 571 B
build/format-library/style.css 571 B
build/hooks/index.min.js 1.63 kB
build/html-entities/index.min.js 424 B
build/i18n/index.min.js 3.71 kB
build/is-shallow-equal/index.min.js 501 B
build/keyboard-shortcuts/index.min.js 1.8 kB
build/keycodes/index.min.js 1.39 kB
build/list-reusable-blocks/index.min.js 1.72 kB
build/list-reusable-blocks/style-rtl.css 838 B
build/list-reusable-blocks/style.css 838 B
build/media-utils/index.min.js 2.92 kB
build/notices/index.min.js 925 B
build/nux/index.min.js 2.08 kB
build/nux/style-rtl.css 747 B
build/nux/style.css 743 B
build/plugins/index.min.js 1.84 kB
build/primitives/index.min.js 924 B
build/priority-queue/index.min.js 582 B
build/react-i18n/index.min.js 671 B
build/react-refresh-entry/index.min.js 8.44 kB
build/react-refresh-runtime/index.min.js 7.31 kB
build/redux-routine/index.min.js 2.65 kB
build/reusable-blocks/index.min.js 2.22 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/rich-text/index.min.js 11 kB
build/server-side-render/index.min.js 1.57 kB
build/shortcode/index.min.js 1.49 kB
build/token-list/index.min.js 639 B
build/url/index.min.js 1.9 kB
build/viewport/index.min.js 1.05 kB
build/warning/index.min.js 248 B
build/widgets/index.min.js 7.15 kB
build/widgets/style-rtl.css 1.16 kB
build/widgets/style.css 1.16 kB
build/wordcount/index.min.js 1.04 kB

compressed-size-action

}

$block_template = get_block_file_template( $id, $template_type );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I feel the logic change here doesn't make sense, at least not from the gutenberg_get_block_template perspective, this is a low level API to retrieve a template given its ID. Potentially it can be moved to the endpoint to create a template but not while quering. WDYT

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's the big question here. I think that there's good arguments for each side:

  • It is indeed a fairly low-level API, and it seems like when given a template ID, it should just return that template (or nothing, if it can't find it).
  • OTOH, I think a point can be made that we've now identified one significant use case where that doesn't cut it, and we need the fallback. My hypothesis was that this could be the case pretty much anywhere where we'd call gutenberg_get_block_template (or "require a template given its ID") -- not just for the endpoint -- since it replicates what happens on the frontend, and it's likely that we always want to replicate that.

I'll do a quick audit of all the other occurrences of gutenberg_get_block_template. If indeed I find that the fallback makes sense in all current cases, I'll probably opt to change it to that effect. (The argument then being YAGNI: If we currently only need it with the fallback, we should only provide it that way.)

Another option would be to add a bool arg to the function signature to indicate whether or not to look for fallback templates or not, but I'd only do that if we find that we really need both versions of the function already.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do a quick audit of all the other occurrences of gutenberg_get_block_template.

#37258 (comment)

@ockham
Copy link
Contributor Author

ockham commented Dec 10, 2021

Audit of existing gutenberg_get_block_template() callsites (all in the template REST API controller):

get_item()

update_item()

create_item

delete_item


Out of all these cases, I think that falling back to the next best template doesn't harm the update_item and create_item cases (since we know that template resolution will find an existing template, or otherwise one that's been just created).

It only seems undesirable in the delete_item() case, where we probably really don't want to delete anything else than the template whose ID we specified. This means that we'd put all the responsibility on the caller to make sure that the template with the specified ID really exists, since we'd otherwise delete a different template.

However, I think this can be easily mitigated by comparing the template ID specified by the user to the one returned by gutenberg_get_block_template.

@youknowriad
Copy link
Contributor

I'm not sure I agree with this reasoning personally, For me, the fallback doesn't really make sense in get_item at all. It only makes sense in create_item where you want to create a new empty template, you use the fallback as starting point. If we start returning fallback templates when we request specific ones, it's going to create a lot of issues IMO. Think template listing where you want the info of a specific template, you want a 404 if it doesn't exist in the listing...

@ockham
Copy link
Contributor Author

ockham commented Dec 10, 2021

Ah, missed one:

prepare_item_for_database

@ockham
Copy link
Contributor Author

ockham commented Dec 10, 2021

I'm not sure I agree with this reasoning personally, For me, the fallback doesn't really make sense in get_item at all.

Just to make sure we're on the same page here 🤔 -- having the fallback in get_item is kind of the premise of this PR:

We open the site editor to edit a template which doesn't exist yet, e.g. "Search". So instead, we load the fallback template -- "Index", in this case. We do so since that's also the template that the frontend falls back to, so it's reasonable for the user to assume that that's what they'd see when attempting to edit the corresponding template.

Doing this at get_item level has some benefits, e.g. if the user doesn't end up modifying/saving the template, the wp_template post won't even be created. (It also aligns with what we're doing for templates where there is an existing block template file, but no post yet.)


LMK if I'm reading you correctly and you'd rather solve this altogether differently 🙂

@youknowriad
Copy link
Contributor

youknowriad commented Dec 10, 2021

yeah, I think it's a difference in terms of user expectation that we need to clarify. For me, if I'm on a listing of templates or I want to open the site editor for a given template and that template doesn't exist, I don't really care about hierarchy, I'd like for the site editor to tell me that it doesn't exist but when I want to create a template I think it's fine to inherit the frontend behavior (hierarchy)

@ockham
Copy link
Contributor Author

ockham commented Dec 13, 2021

yeah, I think it's a difference in terms of user expectation that we need to clarify. For me, if I'm on a listing of templates or I want to open the site editor for a given template and that template doesn't exist, I don't really care about hierarchy, I'd like for the site editor to tell me that it doesn't exist but when I want to create a template I think it's fine to inherit the frontend behavior (hierarchy)

I should point out the templates listing indeed doesn't include non-existent templates (since we're not changing the pluralized functions, i.e. gutenberg_get_block_templates and the controller's get_items) -- and I agree that we shouldn't. This means there's arguably some inconsistency in behavior between the pluralized and singular versions of those functions, but I think that those are acceptable. Maybe it helps to think of the $id arg given to the singular versions less of an ID in the normal sense, but rather a key to look up (and, if necessary, fall back to), since that's how template resolution works 🤷‍♂️

@youknowriad
Copy link
Contributor

but rather a key to look up (and, if necessary, fall back to), since that's how template resolution works

I think that's where we should ask ourselves: do we want for this to be a template resolution instead of a template lookup? Don't we already have template resolution functions resolve_template and the like? Aren't there any use cases for simple template lookup. I fear we're breaking the abstraction personally by transforming to template resolution (a specific use-case)

@ockham
Copy link
Contributor Author

ockham commented Dec 14, 2021

Aren't there any use cases for simple template lookup

Yeah, this, to me, is the biggest question. What I was trying to say throughout our convo is that I couldn't really think of any 🤷‍♂️

@youknowriad
Copy link
Contributor

youknowriad commented Dec 14, 2021

@ockham Here's a quick usecase

function TemplateList() {
  const templateIds = // From a query for instance or anything else. 
  return ( 

    <div>
       {templateIds.map( id => <TemplateItem id={id} />
    </div>
  )
} 

function TemplateItem({ id }) {
  const template = useSelect(select => select('core').getEntityRecord('postType', 'wp_template', id, [] );  
  if ( ! template ) return "Template not found"  
return <div><strong>{template.name}</strong>:{template.description}</div>
}

Basically I'm listing the available templates for a given theme, you could argue that you should pass the whole object to TemplateItem and not just id but it doesn't make sense to have GET /templates return 10 templates but have GET template/{id} return a result for all ids (even if not present in the 10 items returns by the first endpoint). Basically an identifier should identify a specific template. That doesn't mean we can't have GET /resolve-template/single or something like that but for me they are conceptually two different things and a user shouldn't be forced to call GET /templates to know whether a given id exists or not. calling GET /template/{id} should be enough.

@ockham
Copy link
Contributor Author

ockham commented Dec 14, 2021

Not a bad example, but still not totally convinced 😬

On a practical level: We already have a template list implemented in our UI that's using the "pluralized" endpoint, and it's working well (even with this PR applied) -- no non-existent templates are listed:

image


Importantly, your example would be unaffected in most practical cases IMO: If the templateIds are obtained some way or another from the endpoint, then it will only contain actually existing templates (as noted earlier); and for existing templates, the "singular" endpoint then works no different than before (no fallback required, since the template that's being queried exists).

So the only case where this would be different are rather pathological queries whose responses would include non-existing templates; and once again, I'm struggling to come up with an example 😅


I get that we're bending the concept of IDs, and maybe both Gutenberg's entity concept, and WP's CPT endpoint concept quite a bit. My takeaway is that templates might have deserved a distinct concept (and endpoint) after all, but I guess it's a bit late for that now. Still, I'd like to make the relevant tools as ergonomic as possible. This, to me, means that our tools should replicate the frontend as much as possible (whereas we have more freedom for things that don't exist on the frontend -- such as a list of existing templates). As a thought experiment, I think that this PR solves #36648 much more cleanly and consistently (also in terms of mental models) than would be otherwise possible; we'd have to make changes in many more different places.


I don't think that the "resolved" template query at this point deserves a totally distinct endpoint; if anything, then maybe a bool flag (whether or not to resolve/fall back). If we do go that way, I'd still like to make the resolution the default behavior 😬

@mcsf
Copy link
Contributor

mcsf commented Dec 15, 2021

First of all, I think all your arguments on both sides are pertinent, and I've been looking at this issue for a while trying to make up my mind. :) The more I re-read things, though, the more I lean towards this:

The user feature is really compelling: when adding a missing template, it should be possible to bootstrap from the fallback template. That doesn't mean that the feature should be implicit. It might, but I can also imagine that the editor would ask the user whether to start from the fallback or start empty.

I also quite like that, in contrast with its predecessor, this PR avoids unnecessary (and possibly harmful) saving upon starting a template.

Regardless of whether the user-facing bits are implicit, I don't think the mechanics under the hood should. If one of the initial points of the PR was consistency (between front end and editor), then we shouldn't introduce (direct or indirect) inconsistencies elsewhere, which is what seems to be happening:

  • Inconsistencies between /templates and /template/<id>/ and between the underlying getters
  • Adding convenient implicit fallback in the GET route in exchange for putting the onus on the DELETE route of preventing unintended deletions.

As for the internal semantics, I'd beware of changing gutenberg_get_block_template. Have you considered taking an approach similar to what we had for years in @wordpress/blocks and have a separate gutenberg_get_block_template_with_fallback function?

Finally, I should ask this: aside from the new-template.js flow, what other pieces of Gutenberg would want the implicit fallback?

@ockham ockham changed the title FSE: Fall back to next best template in hiearchy when querying through REST API FSE: Fall back to next best template in hierarchy when querying through REST API Dec 15, 2021
@ockham
Copy link
Contributor Author

ockham commented Dec 15, 2021

Thanks a lot for your feedback, Miguel! ❤️

Regardless of whether the user-facing bits are implicit, I don't think the mechanics under the hood should. If one of the initial points of the PR was consistency (between front end and editor), then we shouldn't introduce (direct or indirect) inconsistencies elsewhere, which is what seems to be happening:

  • Inconsistencies between /templates and /template/<id>/ and between the underlying getters
  • Adding convenient implicit fallback in the GET route in exchange for putting the onus on the DELETE route of preventing unintended deletions.

Just to clarify, this wouldn't be on the caller though; what I meant is an extra check in the controller. I've now pushed one in 4b09790 -- that should be pretty much it.

As for the internal semantics, I'd beware of changing gutenberg_get_block_template. Have you considered taking an approach similar to what we had for years in @wordpress/blocks and have a separate gutenberg_get_block_template_with_fallback function?

I wasn't aware of this -- thanks for the pointer, I'll look into this!

Finally, I should ask this: aside from the new-template.js flow, what other pieces of Gutenberg would want the implicit fallback?

I've struggled to come up with other use cases. My overall reading is: Templates are CRUD, and for the Create and Read parts, it seemed to me that we'd want to replicate the frontend for most conceivable use cases.

A short digressionAnother way of looking at it is that we barely have any API (REST or even just PHP functions) at all ATM to "directly" query what would get rendered for a given route -- partly since it's hard to formulate a language for those queries (that takes into account not just simple entities such as a page with a given slug or a category with a given ID, but also post archives for a given month etc). Famously, this is why originally had the site editor simply query the actual route for the entity, with a magic ?_wp_find_template querystring appended (see here and here).

I digress; the current solution obviously also only works for entities with an ID, so I don't claim it's the end-all.

Anyway, one more-or-less trivial example I can think of is replicating the frontend over the REST API. I.e. you wanna render your template, but not straight from PHP, but something else that's connected to ("headless"?) WordPress/GB only through the REST API. (Aren't the Frontity folks looking into something like that? cc/ our liaison @gziolo plus @michalczaplinski who I still owe a reply on a P2 👋 )

@gziolo
Copy link
Member

gziolo commented Dec 16, 2021

Anyway, one more-or-less trivial example I can think of is replicating the frontend over the REST API. I.e. you wanna render your template, but not straight from PHP, but something else that's connected to ("headless"?) WordPress/GB only through the REST API. (Aren't the Frontity folks looking into something like that?

I don't know the full context of this PR and how it's related to the work that the Frontity team is doing. At the moment the focus is to replicate the comments list with the Comments Query Loop block that contains inner blocks. The next project in the pipeline is to replicate the comment form with inner blocks, too. On top of that, the plan is to look at finding ways to replicate the frontend view with a JS code to bring interactivity without a full page refresh (like pagination, replying to the comment). However, the obvious challenge is where it is even possible to use REST API and JavaScript to mirror what PHP renders knowing that there are WP filters available on the server that might completely change how the comments are presented.

@ockham
Copy link
Contributor Author

ockham commented Dec 21, 2021

Apologies for letting this stall for a bit. I'm happy to move the implicit template resolution into a new gutenberg_get_block_template_with_fallback, and keep gutenberg_get_block_template as-is.

For the endpoint, I'll add a bool flag to the /template/<id>/ one to indicate whether or not to fall back, if that's okay.

@ockham
Copy link
Contributor Author

ockham commented Dec 21, 2021

Apologies for letting this stall for a bit. I'm happy to move the implicit template resolution into a new gutenberg_get_block_template_with_fallback, and keep gutenberg_get_block_template as-is.

For the endpoint, I'll add a bool flag to the /template/<id>/ one to indicate whether or not to fall back, if that's okay.

Done. Note that the default behavior is to use the fallback. I briefly tried the other way round, but that got messy quickly, since the calls to the endpoint are all over the place and not exactly easy to track down.

@noisysocks noisysocks added the Backport to WP Minor Release Pull request that needs to be backported to a WordPress minor release label Jan 18, 2022
@noisysocks noisysocks added this to 👀 Needs review in WordPress 5.9.x via automation Jan 18, 2022
@adamziel
Copy link
Contributor

adamziel commented Apr 4, 2022

👋 @ockham I noticed this PR is on the WordPress 5.9.x board. Do you this could still land until Wednesday to be included in WordPress 6.0 release?

@ockham
Copy link
Contributor Author

ockham commented Apr 4, 2022

👋 @ockham I noticed this PR is on the WordPress 5.9.x board. Do you this could still land until Wednesday to be included in WordPress 6.0 release?

Hey @adamziel, thanks for reaching out! I'm afraid it's rather unlikely for this PR to get merged by Wednesday since there was some disagreement about some of the underlying choices 😕

@priethor priethor added this to In progress in WordPress 6.0.1 Editor via automation May 6, 2022
@priethor priethor moved this from In progress to Needs review in WordPress 6.0.1 Editor May 6, 2022
@ndiego ndiego removed this from Needs review in WordPress 6.0.1 Editor May 6, 2022
@ndiego ndiego removed this from 👀 Needs review in WordPress 5.9.x May 6, 2022
@mtias
Copy link
Member

mtias commented May 21, 2022

Can we move forwards with this one?

@ockham
Copy link
Contributor Author

ockham commented May 24, 2022

Can we move forwards with this one?

The basic idea of this PR -- using template resolution pretty much whenever a template was requested, even by ID -- was a bit controversial (it would return a fallback template if the one specified by the given ID didn't exist).

I was asked to change the PR so that there would be two versions of template lookup by ID, one without, and one with fallback. However, in practice, I ran into a problem when trying to use the non-fallback version for a case where it was requested to be used (e.g. REST API update), and I ended up using the version with fallback as the default anyway:

I briefly tried the other way round, but that got messy quickly, since the calls to the endpoint are all over the place and not exactly easy to track down.

This means that the PR currently implements the suggested changes only half-way (where they probably don't make a lot of sense; it's really just cosmetics on top of the original PR).

If people are okay with this state (or with rolling back those cosmetic changes) and give approval, I'm happy to merge. Otherwise, we should probably close this PR and either pursue #37054, or wait for someone to figure out if it's possible to implement the requested changes while retaining the underlying idea of this PR.

@mtias
Copy link
Member

mtias commented May 24, 2022

There's a flow being created for adding slug specific templates that asks a few steps, it stands to reason that you should be able to:

  • Start blank
  • Start from closest existing template
  • Pick a pattern / template starting point from somewhere else

We are not building all of this at once, but the option to do blank or existing template makes sense asap. cc @jameskoster

@jameskoster
Copy link
Contributor

There's a flow being created for adding slug specific templates that asks a few steps

This is now merged (#41189), which imo elevates the priority on this PR. It is especially strange to intentionally create a template for a specific entity only to be met with a totally blank canvas.

@jameskoster
Copy link
Contributor

There's a flow being created for adding slug specific templates that asks a few steps, it stands to reason that you should be able to:

  • Start blank
  • Start from closest existing template
  • Pick a pattern / template starting point from somewhere else

Just connecting a dot here... a concept was shared to do virtually the same thing from the 'add template' flow in the post editor: #41060 (comment). We can probably do the same thing in the site editor, with some minor copy tweaks.

Obviously out-of-scope for this PR, but I find it helps to try and keep these things connected :D

@ockham
Copy link
Contributor Author

ockham commented Jun 21, 2022

Since this PR's branch is pretty badly outdated, I've decided to start fresh: #41848

@ockham ockham closed this Jun 21, 2022
@ockham ockham deleted the try/implicit-template-resolution-for-rest-api branch June 21, 2022 19:53
@adamziel adamziel removed the Backport to WP Minor Release Pull request that needs to be backported to a WordPress minor release label Jun 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Block Template creation is using empty content instead of the appropiate fallback
8 participants