Remove BlockTemplateRegistry (#43589)

This commit is contained in:
Matt Sherman 2024-01-17 07:12:12 -05:00 committed by GitHub
parent 4966697b7f
commit 824dc6be51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 57 additions and 444 deletions

View File

@ -12,27 +12,39 @@ A template is implemented in PHP and sent to the client. The client then renders
The lifecycle of a template is as follows:
- [Registration](#registration)
- [Creation](#creation)
- [Block addition and removal](#block-addition-and-removal)
- [Actions](#actions)
- [Registration](#registration)
- [Sent to client](#sent-to-client)
- [Rendered on client](#rendered-on-client)
## Registration
A template class can be registered with the `Automattic\WooCommerce\LayoutTemplates\LayoutTemplateRegistry`. All template classes must implement the `Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface` interface.
Registration is required in order for the template to be available to be sent to the client (via the `/wc/v3/layout-templates` REST API endpoint).
The default templates are registered in the `rest_api_init` action hook, priority 10.
Blocks can be added or removed from a template before or after it is registered, but the template cannot be modified after it is sent to the client.
**In order for the template to be sent to the client, it should be registered in or before the `rest_request_before_callbacks` filter hook. They can be registered in the `rest_api_init` hook.**
## Creation
A template instance is created by instantiating a class that implements the `Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface` interface.
**A template should be instantiated in or after an `init` action hook, priority 4 or higher.**
Templates are instantiated by the handler for the `/wcv3/layout-templates` REST API endpoint.
## Block addition and removal
After a template instance is created, blocks can be added to or removed from a template using the `add_block()` and `remove_block()` methods, or similar methods that are specific to the type of block being added or removed, such as `add_section()` and `remove_section()`.
Blocks can be added or removed immediately after instantiation.
See the [Automattic\WooCommerce\Admin\BlockTemplates](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/src/Admin/BlockTemplates/README.md) documentation for more information about these methods.
You can modify template instances in a hook for the following action:
- `woocommerce_layout_template_after_instantiation`
### Actions
The following actions are fired when blocks are added to or removed from a template, to support extensibility:
@ -42,26 +54,16 @@ The following actions are fired when blocks are added to or removed from a templ
- `woocommerce_product_editor_block_template_{template_name}_after_remove_block_{block_id}`
- `woocommerce_product_editor_block_template_after_remove_block`
**In order for your action hooks to be called for all block additions and removals for a template, you should call `add_action()` for each of these hooks before the template is instantiated, in or before an `init` action hook, priority 3 or lower.**
**In order for your action hooks to be called for all block additions and removals for a template, you should call `add_action()` for each of these hooks before the template is instantiated, in or before an `rest_api_init` action hook, priority 9 or lower. If your hooks are not being called, verify that you are hooking them up in an action that is called when REST API endpoints are called.**
See the [Automattic\WooCommerce\Admin\BlockTemplates](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/src/Admin/BlockTemplates/README.md) documentation for more information about these hooks.
## Registration
After a template is instantiated, it can be registered with the `Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry\BlockTemplateRegistry`.
Registration is required in order for the template to be sent to the client.
Blocks can be added or removed from a template before or after it is registered, but the template cannot be modified after it is sent to the client.
**In order for the template to be sent to the client, it should be in or before the `admin_enqueue_scripts` action hook, priority 9 or lower.**
## Sent to client
A template is sent to the client in or after the `admin_enqueue_scripts` action hook, priority 10 or higher.
A template is sent to the client in the handler for the `/wcv3/layout-templates` REST API endpoint, after the `rest_request_before_callbacks` filter hook.
Any template modification after this point will not be sent to the client.
## Rendered on client
When the template is rendered on the client, all blocks in the template have their `hideConditions` evaluated to determine whether they should be rendered or not.
When the template is rendered on the client, all blocks in the template have their `hideConditions` and `disableConditions` evaluated to determine whether they should be rendered or not.

View File

@ -1,4 +1,4 @@
Significance: minor
Significance: major
Type: update
Load layout templates via the REST API.
Load layout templates via the REST API. Note that layout template modifications must now be hooked up in an action that is called when REST API endpoints are handled, such as `rest_api_init`.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Remove unused BlockTemplateRegistry (replaced by LayoutTemplateRegistry).

View File

@ -10,7 +10,6 @@ use Automattic\WooCommerce\Admin\Features\ProductBlockEditor\ProductTemplate;
use Automattic\WooCommerce\Admin\PageController;
use Automattic\WooCommerce\LayoutTemplates\LayoutTemplateRegistry;
use Automattic\WooCommerce\Internal\Admin\BlockTemplates\BlockTemplateLogger;
use Automattic\WooCommerce\Internal\Admin\Features\ProductBlockEditor\ProductTemplates\SimpleProductTemplate;
use Automattic\WooCommerce\Internal\Admin\Features\ProductBlockEditor\ProductTemplates\ProductVariationTemplate;
@ -84,10 +83,6 @@ class Init {
$tracks = new Tracks();
$tracks->init();
// Make sure the block template logger is initialized before any templates are created.
BlockTemplateLogger::get_instance();
$this->register_layout_templates();
$this->register_product_templates();
}
}

View File

@ -29,7 +29,6 @@ use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\Proxie
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\RestockRefundedItemsAdjusterServiceProvider;
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\UtilsClassesServiceProvider;
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\BatchProcessingServiceProvider;
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\BlockTemplatesServiceProvider;
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\LayoutTemplatesServiceProvider;
/**
@ -77,7 +76,6 @@ final class Container {
FeaturesServiceProvider::class,
MarketingServiceProvider::class,
MarketplaceServiceProvider::class,
BlockTemplatesServiceProvider::class,
LayoutTemplatesServiceProvider::class,
LoggingServiceProvider::class,
EnginesServiceProvider::class,

View File

@ -1,78 +0,0 @@
<?php
namespace Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface;
/**
* Block template registry.
*/
final class BlockTemplateRegistry {
/**
* Class instance.
*
* @var BlockTemplateRegistry|null
*/
private static $instance = null;
/**
* Templates.
*
* @var array
*/
protected $templates = array();
/**
* Get the instance of the class.
*/
public static function get_instance(): BlockTemplateRegistry {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Register a single template.
*
* @param BlockTemplateInterface $template Template to register.
*
* @throws \ValueError If a template with the same ID already exists.
*/
public function register( BlockTemplateInterface $template ) {
$id = $template->get_id();
if ( isset( $this->templates[ $id ] ) ) {
throw new \ValueError( 'A template with the specified ID already exists in the registry.' );
}
/**
* Fires when a template is registered.
*
* @param BlockTemplateInterface $template Template that was registered.
*
* @since 8.2.0
*/
do_action( 'woocommerce_block_template_register', $template );
$this->templates[ $id ] = $template;
}
/**
* Get the registered templates.
*/
public function get_all_registered(): array {
return $this->templates;
}
/**
* Get a single registered template.
*
* @param string $id ID of the template.
*/
public function get_registered( $id ): ?BlockTemplateInterface {
return isset( $this->templates[ $id ] ) ? $this->templates[ $id ] : null;
}
}

View File

@ -1,53 +0,0 @@
<?php
namespace Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry;
/**
* Block template controller.
*/
class BlockTemplatesController {
/**
* Block template registry
*
* @var BlockTemplateRegistry
*/
private $block_template_registry;
/**
* Block template transformer.
*
* @var TemplateTransformer
*/
private $template_transformer;
/**
* Init.
*/
public function init( $block_template_registry, $template_transformer ) {
$this->block_template_registry = $block_template_registry;
$this->template_transformer = $template_transformer;
add_action( 'rest_api_init', array( $this, 'register_templates' ) );
}
/**
* Register templates in the blocks endpoint.
*/
public function register_templates() {
$templates = $this->block_template_registry->get_all_registered();
foreach ( $templates as $template ) {
add_filter( 'pre_get_block_templates', function( $query_result, $query, $template_type ) use( $template ) {
if ( ! isset( $query['area'] ) || $query['area'] !== $template->get_area() ) {
return $query_result;
}
$wp_block_template = $this->template_transformer->transform( $template );
$query_result[] = $wp_block_template;
return $query_result;
}, 10, 3 );
}
}
}

View File

@ -1,37 +0,0 @@
<?php
namespace Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface;
/**
* Template transformer.
*/
class TemplateTransformer {
/**
* Transform the WooCommerceBlockTemplate to a WP_Block_Template.
*
* @param object $block_template The product template.
*/
public function transform( BlockTemplateInterface $block_template ): \WP_Block_Template {
$template = new \WP_Block_Template();
$template->id = $block_template->get_id();
$template->theme = 'woocommerce/woocommerce';
$template->content = $block_template->get_formatted_template();
$template->source = 'plugin';
$template->slug = $block_template->get_id();
$template->type = 'wp_template';
$template->title = $block_template->get_title();
$template->description = $block_template->get_description();
$template->status = 'publish';
$template->has_theme_file = true;
$template->origin = 'plugin';
$template->is_custom = false; // Templates loaded from the filesystem aren't custom, ones that have been edited and loaded from the DB are.
$template->post_types = array(); // Don't appear in any Edit Post template selector dropdown.
$template->area = $block_template->get_area();
return $template;
}
}

View File

@ -210,25 +210,35 @@ class BlockTemplateLogger {
}
/**
* Get all template events for a given template.
* Get all template events for a given template as a JSON like array.
*
* @param string $template_id Template ID.
*/
public function get_formatted_template_events( string $template_id ): array {
public function template_events_to_json( string $template_id ): array {
if ( ! isset( $this->all_template_events[ $template_id ] ) ) {
return array();
}
$template_events = $this->all_template_events[ $template_id ];
$template = $this->templates[ $template_id ];
$formatted_template_events = array();
return $this->to_json( $template_events );
}
/**
* Get all template events as a JSON like array.
*
* @param array $template_events Template events.
*
* @return array The JSON.
*/
private function to_json( array $template_events ): array {
$json = array();
foreach ( $template_events as $template_event ) {
$container = $template_event['container'];
$block = $template_event['block'];
$formatted_template_events[] = array(
$json[] = array(
'level' => $template_event['level'],
'event_type' => $template_event['event_type'],
'message' => $template_event['message'],
@ -246,7 +256,7 @@ class BlockTemplateLogger {
);
}
return $formatted_template_events;
return $json;
}
/**
@ -311,7 +321,7 @@ class BlockTemplateLogger {
* @param array $template_events Template events.
*/
private function generate_template_events_hash( array $template_events ): string {
return md5( wp_json_encode( $template_events ) );
return md5( wp_json_encode( $this->to_json( $template_events ) ) );
}
/**

View File

@ -8,7 +8,6 @@ namespace Automattic\WooCommerce\Internal\Admin;
use Automattic\WooCommerce\Admin\Features\Features;
use Automattic\WooCommerce\Admin\PageController;
use Automattic\WooCommerce\Admin\PluginsHelper;
use Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry\BlockTemplatesController;
use Automattic\WooCommerce\Internal\Admin\ProductReviews\Reviews;
use Automattic\WooCommerce\Internal\Admin\ProductReviews\ReviewsCommentsOverrides;
use Automattic\WooCommerce\Internal\Admin\Settings;
@ -73,7 +72,6 @@ class Loader {
wc_get_container()->get( Reviews::class );
wc_get_container()->get( ReviewsCommentsOverrides::class );
wc_get_container()->get( BlockTemplatesController::class );
add_filter( 'admin_body_class', array( __CLASS__, 'add_admin_body_classes' ) );
add_filter( 'admin_title', array( __CLASS__, 'update_admin_title' ) );

View File

@ -1,42 +0,0 @@
<?php
/**
* BlockTemplatesServiceProvider class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider;
use Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry\BlockTemplatesController;
use Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry\BlockTemplateRegistry;
use Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry\TemplateTransformer;
/**
* Service provider for the block templates controller classes in the Automattic\WooCommerce\Internal\BlockTemplateRegistry namespace.
*/
class BlockTemplatesServiceProvider extends AbstractServiceProvider {
/**
* The classes/interfaces that are serviced by this service provider.
*
* @var array
*/
protected $provides = array(
BlockTemplateRegistry::class,
BlockTemplatesController::class,
TemplateTransformer::class,
);
/**
* Register the classes.
*/
public function register() {
$this->share( TemplateTransformer::class );
$this->share( BlockTemplateRegistry::class );
$this->share( BlockTemplatesController::class )->addArguments(
array(
BlockTemplateRegistry::class,
TemplateTransformer::class,
)
);
}
}

View File

@ -4,6 +4,8 @@ namespace Automattic\WooCommerce\LayoutTemplates;
use Automattic\WooCommerce\Admin\BlockTemplates\BlockTemplateInterface;
use Automattic\WooCommerce\Internal\Admin\BlockTemplates\BlockTemplateLogger;
/**
* Layout template registry.
*/
@ -100,13 +102,21 @@ final class LayoutTemplateRegistry {
* @param array $query_params Query params.
*/
public function instantiate_layout_templates( array $query_params = array() ): array {
// Make sure the block template logger is initialized before the templates are created,
// so that the logger will collect the template events.
$logger = BlockTemplateLogger::get_instance();
$layout_templates = array();
$layout_templates_info = $this->get_matching_layout_templates_info( $query_params );
foreach ( $layout_templates_info as $layout_template_info ) {
$layout_template = $this->get_layout_template_instance( $layout_template_info );
$layout_templates[ $layout_template->get_id() ] = $layout_template;
$layout_template_id = $layout_template->get_id();
$layout_templates[ $layout_template_id ] = $layout_template;
$logger->log_template_events_to_file( $layout_template_id );
}
return $layout_templates;
@ -148,16 +158,8 @@ final class LayoutTemplateRegistry {
*/
do_action( 'woocommerce_layout_template_after_instantiation', $layout_template_info['id'], $layout_template_info['area'], $layout_template_instance );
// Call the old, soon-to-be-deprecated, register hook.
/**
* Fires when a template is registered.
*
* @param BlockTemplateInterface $template Template that was registered.
*
* @since 8.2.0
*/
do_action( 'woocommerce_block_template_register', $layout_template_instance );
// Call the old, deprecated, register hook.
wc_do_deprecated_action( 'woocommerce_block_template_register', array( $layout_template_instance ), '8.6.0', 'woocommerce_layout_template_after_instantiation' );
return $layout_template_instance;
}

View File

@ -1,56 +0,0 @@
<?php
namespace Automattic\WooCommerce\Tests\Internal\Admin\BlockTemplates;
use Automattic\WooCommerce\Internal\Admin\BlockTemplates\Block;
use Automattic\WooCommerce\Internal\Admin\BlockTemplates\BlockTemplate;
use Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry\BlockTemplateRegistry;
use WC_Unit_Test_Case;
/**
* Tests for the BlockTemplateRegistryTest class.
*/
class BlockTemplateRegistryTest extends WC_Unit_Test_Case {
/**
* Block template registry.
*
* @var BlockTemplateRegistry
*/
protected $block_template_registry;
/**
* Runs before each test.
*/
public function setUp(): void {
$this->block_template_registry = new BlockTemplateRegistry();
$this->block_template_registry->register( new CustomBlockTemplate() );
$this->block_template_registry->register( new BlockTemplate() );
}
/**
* Test getting all registered templates.
*/
public function test_get_all_registered() {
$registered_templates = $this->block_template_registry->get_all_registered();
$this->assertCount( 2, $registered_templates );
$this->assertInstanceOf( CustomBlockTemplate::class, $registered_templates['custom-block-template'] );
$this->assertInstanceOf( BlockTemplate::class, $registered_templates['woocommerce-block-template'] );
}
/**
* Test registering a template with an existing ID.
*/
public function test_register_duplicate_id() {
$this->expectException( \ValueError::class );
$registered_templates = $this->block_template_registry->register( new BlockTemplate() );
}
/**
* Test getting a single template by ID.
*/
public function test_get_registered() {
$custom_block_template = $this->block_template_registry->get_registered( 'custom-block-template' );
$this->assertInstanceOf( CustomBlockTemplate::class, $custom_block_template );
}
}

View File

@ -1,89 +0,0 @@
<?php
namespace Automattic\WooCommerce\Tests\Internal\Admin\BlockTemplates;
use Automattic\WooCommerce\Internal\Admin\BlockTemplates\Block;
use Automattic\WooCommerce\Internal\Admin\BlockTemplates\BlockTemplate;
use Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry\BlockTemplateRegistry;
use Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry\BlockTemplatesController;
use Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry\TemplateTransformer;
use WC_REST_Unit_Test_Case;
/**
* Tests for the BlockTemplatesControllerTest class.
*/
class BlockTemplatesControllerTest extends WC_REST_Unit_Test_Case {
/**
* Block template registry.
*
* @var BlockTemplateRegistry
*/
protected $block_template_registry;
/**
* Block templates controller.
*
* @var BlockTemplatesController
*/
protected $block_templates_controller;
/**
* Runs before suite initialization.
*/
public static function setUpBeforeClass(): void {
parent::setUpBeforeClass();
wc_get_container()->get( BlockTemplatesController::class );
$block_template_registry = wc_get_container()->get( BlockTemplateRegistry::class );
$block_template_registry->register( new CustomBlockTemplate() );
}
/**
* Runs before each test.
*/
public function setUp(): void {
parent::setUp();
$admin_user = wp_insert_user(
array(
'user_login' => uniqid(),
'role' => 'administrator',
'user_pass' => 'x',
)
);
wp_set_current_user( $admin_user );
}
/**
* Test getting a registered template when area is specififed.
*/
public function test_get_template_by_area() {
$request = new \WP_REST_Request( 'GET', '/wp/v2/templates' );
$request->set_param( 'area', 'test-area' );
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$this->assertEquals( 200, $response->get_status() );
$this->assertCount( 1, $data );
$this->assertEquals( 'custom-block-template', $data[0]['id'] );
}
/**
* Test that getting default templates works as expected and does not show the custom templates.
*/
public function test_get_template_without_area() {
$request = new \WP_REST_Request( 'GET', '/wp/v2/templates' );
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$found_registery_template = false;
foreach ( $data as $template ) {
if ( 'custom-block-template' === $template['id'] ) {
$found_registery_template = true;
}
}
$this->assertEquals( 200, $response->get_status() );
$this->assertFalse( $found_registery_template );
}
}

View File

@ -1,41 +0,0 @@
<?php
namespace Automattic\WooCommerce\Tests\Internal\Admin\BlockTemplates;
use Automattic\WooCommerce\Internal\Admin\BlockTemplateRegistry\TemplateTransformer;
use Automattic\WooCommerce\Tests\Internal\Admin\BlockTemplates\CustomBlockTemplate;
use WC_Unit_Test_Case;
/**
* Tests for the BlockTemplatesControllerTest class.
*/
class TemplateTransformerTest extends WC_Unit_Test_Case {
/**
* Template transformer instance.
*
* @var TemplateTransformer
*/
protected $template_transformer;
/**
* Runs before each test.
*/
public function setUp(): void {
parent::setUp();
$this->template_transformer = new TemplateTransformer();
}
/**
* Test transforming a template returns a valid WP_Block_Template.
*/
public function test_transform() {
$custom_block_template = new CustomBlockTemplate();
$wp_block_template = $this->template_transformer->transform( $custom_block_template );
$this->assertInstanceOf( \WP_Block_Template::class, $wp_block_template );
$this->assertEquals( 'custom-block-template', $wp_block_template->id );
$this->assertEquals( 'Custom Block Template', $wp_block_template->title );
$this->assertEquals( 'A custom block template for testing.', $wp_block_template->description );
$this->assertEquals( 'test-area', $wp_block_template->area );
}
}