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

[Facet Meta] Compatibility with block themes #3498

Merged
merged 6 commits into from
Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions elasticpress.php
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ function register_indexable_posts() {
*/
$query_logger = apply_filters( 'ep_query_logger', new \ElasticPress\QueryLogger() );
get_container()->set( '\ElasticPress\QueryLogger', $query_logger, true );

get_container()->set( '\ElasticPress\BlockTemplateUtils', new \ElasticPress\BlockTemplateUtils(), true );
}
add_action( 'plugins_loaded', __NAMESPACE__ . '\register_indexable_posts' );

Expand Down
133 changes: 133 additions & 0 deletions includes/classes/BlockTemplateUtils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php
/**
* Block Template Utils class
*
* @since 4.7.0
* @package elasticpress
*/

namespace ElasticPress;

if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}

/**
* Block Template Utils class
*/
class BlockTemplateUtils {
const CACHE_KEY = 'ep_blocks';

/**
* Hook cache cleanup calls
*/
public function setup() {
add_action( 'save_post_wp_template', [ $this, 'regenerate_cache' ] );
add_action( 'save_post_wp_template_part', [ $this, 'regenerate_cache' ] );
add_action( 'switch_theme', [ $this, 'regenerate_cache' ] );
}

/**
* Delete and regenerate the cache
*/
public function regenerate_cache() {
delete_transient( self::CACHE_KEY );

// Simply calling it will reset the transient (if the `ep_blocks_pre_all_blocks` filter isn't in use.)
$this->get_all_blocks_in_all_templates();
}

/**
* Given a block name, return all its instances across all block templates
*
* @param string $block_name The block name, e.g., `elasticpress/facet-meta`
* @return array
*/
public function get_specific_block_in_all_templates( string $block_name ) : array {
$blocks = array_filter(
$this->get_all_blocks_in_all_templates(),
function ( $block ) use ( $block_name ) {
return ( $block['blockName'] === $block_name );
}
);

return $blocks;
}

/**
* Get all blocks in all block templates
*
* It returns a flat list of all blocks, including innerBlocks.
*
* @return array
*/
public function get_all_blocks_in_all_templates() : array {
/**
* Short-circuits the process of getting all blocks of a template.
*
* Returning a non-null value will effectively short-circuit the function.
*
* @since 4.7.0
* @hook ep_blocks_pre_all_blocks
* @param {null} $meta_keys Blocks array
* @return {null|array} Blocks array or `null` to keep default behavior
*/
$pre_all_blocks = apply_filters( 'ep_blocks_pre_all_blocks', null );
if ( null !== $pre_all_blocks ) {
return (array) $pre_all_blocks;
}

$cache = get_transient( self::CACHE_KEY );
if ( is_array( $cache ) ) {
return $cache;
}

$all_blocks = [];

$template_types = [ 'wp_template', 'wp_template_part' ];

foreach ( $template_types as $template_type ) {
$block_templates = get_block_templates( [], $template_type );
foreach ( $block_templates as $block_template ) {
$template_blocks = parse_blocks( $block_template->content );

foreach ( $template_blocks as $block ) {
$all_blocks[] = [
'blockName' => $block['blockName'],
'attrs' => $block['attrs'],
];

$all_blocks = $this->recursively_get_inner_blocks( $all_blocks, $block );
}
}
}

set_transient( self::CACHE_KEY, $all_blocks, MONTH_IN_SECONDS );

return $all_blocks;
}

/**
* Get all inner blocks recursively
*
* @param array $all_blocks All blocks analyzed so far
* @param array $block Block to be analyzed now
* @return array
*/
protected function recursively_get_inner_blocks( array $all_blocks, array $block ) : array {
if ( empty( $block['innerBlocks'] ) ) {
return $all_blocks;
}

foreach ( $block['innerBlocks'] as $inner_block ) {
$all_blocks[] = [
'blockName' => $inner_block['blockName'],
'attrs' => $inner_block['attrs'],
];

$all_blocks = $this->recursively_get_inner_blocks( $all_blocks, $inner_block );
}

return $all_blocks;
}
}
29 changes: 29 additions & 0 deletions includes/classes/Feature/Facets/FacetType.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,33 @@ public function add_query_params( array $query_params, array $filters ) : array

return $query_params;
}

/**
* Get all facet fields selected across all blocks of the current facet type.
*
* Given a block name, e.g., `elasticpress/facet-meta` this method returns all meta fields
* selected in all blocks.
*
* @param string $block_name The block name
* @return array
*/
protected function block_template_meta_fields( string $block_name ) : array {
$block_template_utils = \ElasticPress\get_container()->get( '\ElasticPress\BlockTemplateUtils' );
$ep_blocks = $block_template_utils->get_specific_block_in_all_templates( $block_name );

return array_filter(
array_reduce(
$ep_blocks,
function( $acc, $block ) use ( $block_name ) {
if ( 0 !== strpos( $block['blockName'], $block_name ) ) {
return $acc;
}

$acc[] = $block['attrs']['facet'];
return $acc;
},
[]
)
);
}
}
7 changes: 7 additions & 0 deletions includes/classes/Feature/Facets/Types/Meta/FacetType.php
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,13 @@ public function get_facets_meta_fields() {
$facets_meta_fields = array_merge( $facets_meta_fields, $matches[1] );
}

if ( current_theme_supports( 'block-templates' ) ) {
$facets_meta_fields = array_merge(
$facets_meta_fields,
$this->block_template_meta_fields( 'elasticpress/facet-meta' )
);
}

/**
* Filter meta fields to be used in aggregations.
*
Expand Down
7 changes: 7 additions & 0 deletions includes/classes/Feature/Facets/Types/MetaRange/FacetType.php
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,13 @@ public function get_facets_meta_fields() {
$facets_meta_fields = array_merge( $facets_meta_fields, $matches[1] );
}

if ( current_theme_supports( 'block-templates' ) ) {
$facets_meta_fields = array_merge(
$facets_meta_fields,
$this->block_template_meta_fields( 'elasticpress/facet-meta-range' )
);
}

/**
* Filter meta fields to be used in aggregations related to meta range blocks.
*
Expand Down
124 changes: 124 additions & 0 deletions tests/php/TestBlockTemplateUtils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php
/**
* Test the BlockTemplateUtils class methods
*
* @since 4.7.0
* @package elasticpress
*/

namespace ElasticPressTest;

use ElasticPress\BlockTemplateUtils;

/**
* TestBlockTemplateUtils test class
*/
class TestBlockTemplateUtils extends BaseTestCase {
/**
* Test the `regenerate_cache` method
*
* @group block_template_utils
*/
public function test_regenerate_cache() {
$block_template_utils = new BlockTemplateUtils();

set_transient( $block_template_utils::CACHE_KEY, 'test' );
$block_template_utils->regenerate_cache();

$this->assertIsArray( get_transient( $block_template_utils::CACHE_KEY ) );
}

/**
* Test the `get_specific_block_in_all_templates` method
*
* @group block_template_utils
*/
public function test_get_specific_block_in_all_templates() {
$block_template_utils = new BlockTemplateUtils();

$meta_block = [
'blockName' => 'elasticpress/facet-meta',
'attrs' => [ 'facet' => '_price' ],
];

$meta_range_block = [
'blockName' => 'elasticpress/facet-meta-range',
'attrs' => [ 'facet' => '_sale_price' ],
];

$blocks = [
[
'blockName' => 'core/search',
'attrs' => [ 'label' => 'Search' ],
],
$meta_block,
$meta_range_block,
];

$set_blocks = function () use ( $blocks ) {
return $blocks;
};
add_filter( 'ep_blocks_pre_all_blocks', $set_blocks );

$this->assertEqualsCanonicalizing(
[ $meta_block ],
$block_template_utils->get_specific_block_in_all_templates( 'elasticpress/facet-meta' )
);
$this->assertEqualsCanonicalizing(
[ $meta_range_block ],
$block_template_utils->get_specific_block_in_all_templates( 'elasticpress/facet-meta-range' )
);
}

/**
* Test the `get_all_blocks_in_all_templates` method
*
* @group block_template_utils
*/
public function test_get_all_blocks_in_all_templates() {
$this->markTestIncomplete( 'This test should also test the real returns from get_block_templates(), etc.' );
}

/**
* Test the `ep_blocks_pre_all_blocks` filter
*
* @group block_template_utils
*/
public function test_get_all_blocks_in_all_templates_ep_blocks_pre_all_blocks() {
$block_template_utils = new BlockTemplateUtils();

$times_called = did_filter( 'pre_transient_' . $block_template_utils::CACHE_KEY );

$set_blocks = function ( $pre_all_blocks ) {
$this->assertNull( $pre_all_blocks );
return [ 'test' ];
};
add_filter( 'ep_blocks_pre_all_blocks', $set_blocks );

$this->assertSame( [ 'test' ], $block_template_utils->get_all_blocks_in_all_templates() );

// This filter should not have been called once more
$this->assertSame( $times_called, did_filter( 'pre_transient_' . $block_template_utils::CACHE_KEY ) );
}

/**
* Test the `get_all_blocks_in_all_templates` transient
*
* @group block_template_utils
*/
public function test_get_all_blocks_in_all_templates_transient() {
$block_template_utils = new BlockTemplateUtils();
delete_transient( $block_template_utils::CACHE_KEY );

$times_called = did_filter( 'pre_set_transient_' . $block_template_utils::CACHE_KEY );

// This filter should be called once to set the transient
$block_template_utils->get_all_blocks_in_all_templates();
$expected_times_called = $times_called + 1;
$this->assertSame( $expected_times_called, did_filter( 'pre_set_transient_' . $block_template_utils::CACHE_KEY ) );

// This filter should not be called again, because the transient was already set
$block_template_utils->get_all_blocks_in_all_templates();
$this->assertSame( $expected_times_called, did_filter( 'pre_set_transient_' . $block_template_utils::CACHE_KEY ) );
}
}