Make templates fallback methods template-agnostic (#46372)

* Make some methods agnostic of which fallback template is used

* Get rid of the template_is_eligible_for_fallback() util

* Get rid of the template_is_eligible_for_fallback() util (II)

* Add changelog

* Get rid of the template_is_eligible_for_fallback() util (III)

* Add back 'template_is_eligible_for_fallback' method

* Fix issue after rebase

* Remove some more instances of hard-coded templates from the template hierarchy code

* Update comments

* Remove unused template_is_eligible_for_fallback() function

* Add extra explanatory comment
This commit is contained in:
Albert Juhé Lluveras 2024-08-14 15:30:37 +02:00 committed by GitHub
parent 9f69c63a9e
commit 474eae1135
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 69 additions and 88 deletions

View File

@ -0,0 +1,5 @@
Significance: patch
Type: tweak
Comment: Refactor block templates fallback methods so they are template-agnostic

View File

@ -1,7 +1,6 @@
<?php
namespace Automattic\WooCommerce\Blocks;
use Automattic\WooCommerce\Blocks\Templates\ProductCatalogTemplate;
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
use Automattic\WooCommerce\Blocks\Templates\ComingSoonTemplate;
@ -27,7 +26,7 @@ class BlockTemplatesController {
add_filter( 'pre_get_block_file_template', array( $this, 'get_block_file_template' ), 10, 3 );
add_filter( 'get_block_template', array( $this, 'add_block_template_details' ), 10, 3 );
add_filter( 'get_block_templates', array( $this, 'add_block_templates' ), 10, 3 );
add_filter( 'taxonomy_template_hierarchy', array( $this, 'add_archive_product_to_eligible_for_fallback_templates' ), 10, 1 );
add_filter( 'taxonomy_template_hierarchy', array( $this, 'add_fallback_template_to_hierarchy' ), 10, 1 );
add_filter( 'block_type_metadata_settings', array( $this, 'add_plugin_templates_parts_support' ), 10, 2 );
add_filter( 'block_type_metadata_settings', array( $this, 'prevent_shortcodes_html_breakage' ), 10, 2 );
add_action( 'current_screen', array( $this, 'hide_template_selector_in_cart_checkout_pages' ), 10 );
@ -54,6 +53,10 @@ class BlockTemplatesController {
* This function is used on the `pre_get_block_template` hook to return the fallback template from the db in case
* the template is eligible for it.
*
* Currently, the Products by Category, Products by Tag and Products by Attribute templates fall back to the
* Product Catalog template. That means that if there are customizations in the Product Catalog template,
* they are also reflected in the other templates as long as they haven't been customized as well.
*
* @param \WP_Block_Template|null $template Block template object to short-circuit the default query,
* or null to allow WP to run its normal queries.
* @param string $id Template unique identifier (example: theme_slug//template_slug).
@ -76,13 +79,14 @@ class BlockTemplatesController {
$template_name_parts = explode( '//', $id );
$theme = $template_name_parts[0] ?? '';
$slug = $template_name_parts[1] ?? '';
$registered_template = BlockTemplateUtils::get_template( $slug );
if ( empty( $theme ) || empty( $slug ) || ! BlockTemplateUtils::template_is_eligible_for_product_archive_fallback( $slug ) ) {
if ( empty( $theme ) || empty( $slug ) || ! $registered_template || ! isset( $registered_template->fallback_template ) ) {
return null;
}
$wp_query_args = array(
'post_name__in' => array( ProductCatalogTemplate::SLUG, $slug ),
'post_name__in' => array( $registered_template->fallback_template, $slug ),
'post_type' => $template_type,
'post_status' => array( 'auto-draft', 'draft', 'publish', 'trash' ),
'no_found_rows' => true,
@ -98,12 +102,12 @@ class BlockTemplatesController {
$posts = $template_query->posts;
// If we have more than one result from the query, it means that the current template is present in the db (has
// been customized by the user) and we should not return the `archive-product` template.
// been customized by the user) and we should not return the fallback template.
if ( count( $posts ) > 1 ) {
return null;
}
if ( count( $posts ) > 0 && ProductCatalogTemplate::SLUG === $posts[0]->post_name ) {
if ( count( $posts ) > 0 && $registered_template->fallback_template === $posts[0]->post_name ) {
$template = _build_block_template_result_from_post( $posts[0] );
if ( ! is_wp_error( $template ) ) {
@ -121,26 +125,21 @@ class BlockTemplatesController {
}
/**
* Adds the `archive-product` template to the `taxonomy-product_cat`, `taxonomy-product_tag`, `taxonomy-attribute`
* templates to be able to fall back to it.
* Adds the fallback template to the template hierarchy.
*
* @param array $template_hierarchy A list of template candidates, in descending order of priority.
*/
public function add_archive_product_to_eligible_for_fallback_templates( $template_hierarchy ) {
public function add_fallback_template_to_hierarchy( $template_hierarchy ) {
$template_slugs = array_map(
'_strip_template_file_suffix',
$template_hierarchy
);
$templates_eligible_for_fallback = array_filter(
$template_slugs,
function ( $template_slug ) {
return BlockTemplateUtils::template_is_eligible_for_product_archive_fallback( $template_slug );
foreach ( $template_slugs as $template_slug ) {
$template = BlockTemplateUtils::get_template( $template_slug );
if ( $template && isset( $template->fallback_template ) ) {
$template_hierarchy[] = $template->fallback_template;
}
);
if ( count( $templates_eligible_for_fallback ) > 0 ) {
$template_hierarchy[] = ProductCatalogTemplate::SLUG;
}
return $template_hierarchy;
@ -233,10 +232,12 @@ class BlockTemplatesController {
list( $template_id, $template_slug ) = $template_name_parts;
// If the theme has an archive-product.html template, but not a taxonomy-product_cat/tag/attribute.html template let's use the themes archive-product.html template.
if ( BlockTemplateUtils::template_is_eligible_for_product_archive_fallback_from_theme( $template_slug ) ) {
$template_path = BlockTemplateUtils::get_theme_template_path( ProductCatalogTemplate::SLUG );
$template_object = BlockTemplateUtils::create_new_block_template_object( $template_path, $template_type, $template_slug, true );
// If the template is not present in the theme but its fallback template is,
// let's use the theme's fallback template.
if ( BlockTemplateUtils::template_is_eligible_for_fallback_from_theme( $template_slug ) ) {
$registered_template = BlockTemplateUtils::get_template( $template_slug );
$template_path = BlockTemplateUtils::get_theme_template_path( $registered_template->fallback_template );
$template_object = BlockTemplateUtils::create_new_block_template_object( $template_path, $template_type, $template_slug, true );
return BlockTemplateUtils::build_template_result_from_file( $template_object, $template_type );
}
@ -441,7 +442,7 @@ class BlockTemplatesController {
continue;
}
if ( BlockTemplateUtils::template_is_eligible_for_product_archive_fallback_from_db( $template_slug, $already_found_templates ) ) {
if ( BlockTemplateUtils::template_is_eligible_for_fallback_from_db( $template_slug, $already_found_templates ) ) {
$template = clone BlockTemplateUtils::get_fallback_template_from_db( $template_slug, $already_found_templates );
$template_id = explode( '//', $template->id );
$template->id = $template_id[0] . '//' . $template_slug;
@ -452,10 +453,12 @@ class BlockTemplatesController {
continue;
}
// If the theme has an archive-product.html template, but not a taxonomy-product_cat/tag/attribute.html template let's use the themes archive-product.html template.
if ( BlockTemplateUtils::template_is_eligible_for_product_archive_fallback_from_theme( $template_slug ) ) {
$template_file = BlockTemplateUtils::get_theme_template_path( ProductCatalogTemplate::SLUG );
$templates[] = BlockTemplateUtils::create_new_block_template_object( $template_file, $template_type, $template_slug, true );
// If the template is not present in the theme but its fallback template is,
// let's use the theme's fallback template.
if ( BlockTemplateUtils::template_is_eligible_for_fallback_from_theme( $template_slug ) ) {
$registered_template = BlockTemplateUtils::get_template( $template_slug );
$template_file = BlockTemplateUtils::get_theme_template_path( $registered_template->fallback_template );
$templates[] = BlockTemplateUtils::create_new_block_template_object( $template_file, $template_type, $template_slug, true );
continue;
}

View File

@ -509,23 +509,6 @@ class BlockTemplateUtils {
return false;
}
/**
* Checks if we can fall back to the `archive-product` template for a given slug.
*
* `taxonomy-product_cat`, `taxonomy-product_tag`, `taxonomy-product_attribute` templates can
* generally use the `archive-product` as a fallback if there are no specific overrides.
*
* @param string $template_slug Slug to check for fallbacks.
* @return boolean
*/
public static function template_is_eligible_for_product_archive_fallback( $template_slug ) {
$registered_template = self::get_template( $template_slug );
if ( $registered_template && isset( $registered_template->fallback_template ) ) {
return ProductCatalogTemplate::SLUG === $registered_template->fallback_template;
}
return false;
}
/**
* Checks if we can fall back to an `archive-product` template stored on the db for a given slug.
*
@ -533,20 +516,21 @@ class BlockTemplateUtils {
* @param array $db_templates Templates that have already been found on the db.
* @return boolean
*/
public static function template_is_eligible_for_product_archive_fallback_from_db( $template_slug, $db_templates ) {
$eligible_for_fallback = self::template_is_eligible_for_product_archive_fallback( $template_slug );
if ( ! $eligible_for_fallback ) {
return false;
public static function template_is_eligible_for_fallback_from_db( $template_slug, $db_templates ) {
$registered_template = self::get_template( $template_slug );
if ( $registered_template && isset( $registered_template->fallback_template ) ) {
$array_filter = array_filter(
$db_templates,
function ( $template ) use ( $registered_template ) {
return isset( $registered_template->fallback_template ) && $registered_template->fallback_template === $template->slug;
}
);
return count( $array_filter ) > 0;
}
$array_filter = array_filter(
$db_templates,
function ( $template ) use ( $template_slug ) {
return ProductCatalogTemplate::SLUG === $template->slug;
}
);
return count( $array_filter ) > 0;
return false;
}
/**
@ -557,14 +541,13 @@ class BlockTemplateUtils {
* @return boolean|object
*/
public static function get_fallback_template_from_db( $template_slug, $db_templates ) {
$eligible_for_fallback = self::template_is_eligible_for_product_archive_fallback( $template_slug );
if ( ! $eligible_for_fallback ) {
return false;
}
$registered_template = self::get_template( $template_slug );
foreach ( $db_templates as $template ) {
if ( ProductCatalogTemplate::SLUG === $template->slug ) {
return $template;
if ( $registered_template && isset( $registered_template->fallback_template ) ) {
foreach ( $db_templates as $template ) {
if ( $registered_template->fallback_template === $template->slug ) {
return $template;
}
}
}
@ -580,10 +563,12 @@ class BlockTemplateUtils {
* @param string $template_slug Slug to check for fallbacks.
* @return boolean
*/
public static function template_is_eligible_for_product_archive_fallback_from_theme( $template_slug ) {
return self::template_is_eligible_for_product_archive_fallback( $template_slug )
public static function template_is_eligible_for_fallback_from_theme( $template_slug ) {
$registered_template = self::get_template( $template_slug );
return $registered_template && isset( $registered_template->fallback_template )
&& ! self::theme_has_template( $template_slug )
&& self::theme_has_template( ProductCatalogTemplate::SLUG );
&& self::theme_has_template( $registered_template->fallback_template );
}
/**
@ -609,7 +594,7 @@ class BlockTemplateUtils {
$query_result_template->slug === $template->slug
&& $query_result_template->theme === $template->theme
) {
if ( self::template_is_eligible_for_product_archive_fallback_from_theme( $template->slug ) ) {
if ( self::template_is_eligible_for_fallback_from_theme( $template->slug ) ) {
$query_result_template->has_theme_file = true;
}

View File

@ -44,7 +44,7 @@ class BlockTemplateUtilsTest extends WP_UnitTestCase {
}
/**
* Provides data for testing template_is_eligible_for_product_archive_fallback.
* Provides data for testing template_is_eligible_for_fallback functions.
*/
public function provideFallbackData() {
return array(
@ -56,39 +56,27 @@ class BlockTemplateUtilsTest extends WP_UnitTestCase {
}
/**
* Test template_is_eligible_for_product_archive_fallback.
*
* @param string $input The template slug.
* @param bool $expected The expected result.
*
* @dataProvider provideFallbackData
* Test template_is_eligible_for_fallback_from_db when the template is not eligible.
*/
public function test_template_is_eligible_for_product_archive_fallback( $input, $expected ) {
$this->assertEquals( $expected, BlockTemplateUtils::template_is_eligible_for_product_archive_fallback( $input ) );
public function test_template_is_eligible_for_fallback_from_db_no_eligible_template() {
$this->assertEquals( false, BlockTemplateUtils::template_is_eligible_for_fallback_from_db( 'single-product', array() ) );
}
/**
* Test template_is_eligible_for_product_archive_fallback_from_db when the template is not eligible.
* Test template_is_eligible_for_fallback_from_db when the template is eligible but not in the db.
*/
public function test_template_is_eligible_for_product_archive_fallback_from_db_no_eligible_template() {
$this->assertEquals( false, BlockTemplateUtils::template_is_eligible_for_product_archive_fallback_from_db( 'single-product', array() ) );
public function test_template_is_eligible_for_fallback_from_db_eligible_template_empty_db() {
$this->assertEquals( false, BlockTemplateUtils::template_is_eligible_for_fallback_from_db( 'taxonomy-product_cat', array() ) );
}
/**
* Test template_is_eligible_for_product_archive_fallback_from_db when the template is eligible but not in the db.
* Test template_is_eligible_for_fallback_from_db when the template is eligible and in the db.
*/
public function test_template_is_eligible_for_product_archive_fallback_from_db_eligible_template_empty_db() {
$this->assertEquals( false, BlockTemplateUtils::template_is_eligible_for_product_archive_fallback_from_db( 'taxonomy-product_cat', array() ) );
}
/**
* Test template_is_eligible_for_product_archive_fallback_from_db when the template is eligible and in the db.
*/
public function test_template_is_eligible_for_product_archive_fallback_from_db_eligible_template_custom_in_the_db() {
public function test_template_is_eligible_for_fallback_from_db_eligible_template_custom_in_the_db() {
$db_templates = array(
(object) array( 'slug' => 'archive-product' ),
);
$this->assertEquals( true, BlockTemplateUtils::template_is_eligible_for_product_archive_fallback_from_db( 'taxonomy-product_cat', $db_templates ) );
$this->assertEquals( true, BlockTemplateUtils::template_is_eligible_for_fallback_from_db( 'taxonomy-product_cat', $db_templates ) );
}
/**