Skip to content

Commit

Permalink
Merge pull request #949 from carstingaxion/feature/use-block-hooks-api
Browse files Browse the repository at this point in the history
Use hookable-patterns with existing blocks & DOCS
  • Loading branch information
mauteri authored Oct 15, 2024
2 parents 8a44580 + 861446d commit df8f92b
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 31 deletions.
124 changes: 111 additions & 13 deletions docs/developer/blocks/hookable-patterns/README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,139 @@
# Hookable patterns for events & venues

GatherPress registers multiple invisible block-patterns, that are used as template properties of the main post types.
GatherPress registers multiple invisible block-patterns, that are used as template properties for the main post types.

Patterns allow to be filtered by the (upgraded since WordPress 6.5) Block Hooks API. Making use of this API brings some advantages, which are at least:

- GatherPress' blocks can be easily moved, modified or removed by extenders via standardized core code
- GatherPress provides central entry points for plugin developers to hook in own blocks, to extend GatherPress
- GatherPress provides central entry points for plugin developers to hook-in own blocks, to extend GatherPress
- GatherPress' blocks will provide their hooking code themself, which keeps concerns separate and code clean

For example when you create a new event post, it gets pre-poulated with a set of blocks, curated within a block-pattern named `gatherpress/event-template`.

## Overview of hookable patterns

GatherPress combines four of such block-patterns to curate the creation of:

- [New Events](#new-event)
- [New Venues](#new-venue)
- [New Event Queries within any post](#new-event-queries-within-any-post)
- [Venue Details within any post](#venue-details-within-any-post)

## New Event
### New Event

GatherPress adds the following blocks by default into a new created event:

- A block-pattern named `gatherpress/event-template`.

- A block-pattern named `gatherpress/event-template`, which contains the following blocks by default:

## New Venue
- `gatherpress/event-date`
- `gatherpress/add-to-calendar`
- `gatherpress/venue`
- `gatherpress/rsvp`
- `core/paragraph`
- `gatherpress/rsvp-response`

A new created venue will have the following blocks prepared by default:

- A block-pattern named `gatherpress/venue-template`
- A block-pattern named `gatherpress/venue-details`, which keeps detailed information about a selected venue in the shape of blocks
### New Venue

## New Event Queries within any post

## Venue Details within any post
A new created venue will have the following blocks prepared by default:

### Resources
- A block-pattern named `gatherpress/venue-template`, which contains the following block by default:

- `gatherpress/venue`


### New Event Queries within any post

...

### Venue Details within any post

...

## Modify the blocks in the patterns

- [Change order of the default blocks](#change-order-of-the-default-blocks)
- [Add blocks to the default patterns](#add-blocks-to-the-default-patterns)
- [Remove default blocks](#remove-default-blocks)

### Change order of the default blocks

**Example**: To move the *RSVP-Response* block directly behind the *RSVP* block for every new created Event,
you could call:

```php
/**
* Move the "RSVP-Response" block directly behind the "RSVP" block.
*
* @param string[] $hooked_block_types The list of hooked block types.
* @return string[] The modified list of hooked block types.
*/
add_filter( 'hooked_block_types', function( array $hooked_block_types ) : array {
$index = array_search('gatherpress/rsvp-response', $hooked_block_types);
if ( $index !== false ) {
// Remove the "RSVP-Response" block from its current position.
$block = array_splice( $hooked_block_types, $index, 1 );
// Find the index of the "RSVP" block.
$rsvp_index = array_search( 'gatherpress/rsvp', $hooked_block_types );
// Insert the "RSVP-Response" block directly behind the "RSVP" block.
array_splice( $hooked_block_types, $rsvp_index + 1, 0, $block );
}
return $hooked_block_types;
});
```

### Add blocks to the default patterns

**Example**: To add the *Featured Image* block before the *Event Date* block for every new created Event,
you could call:

```php
/**
* Add the 'Featured Image' block before the 'Event Date' block.
*
* @see https://developer.wordpress.org/reference/hooks/hooked_block_types/
*
* @param string[] $hooked_block_types The list of hooked block types.
* @param string $relative_position The relative position of the hooked blocks. Can be one of 'before', 'after', 'first_child', or 'last_child'.
* @param string|null $anchor_block_type The anchor block type.
* @param \WP_Block_Template|array $context The block template, template part, or pattern that the anchor block belongs to.
* @return string[] The list of hooked block types.
*/
add_filter( 'hooked_block_types', function ( array $hooked_block_types, string $relative_position, ?string $anchor_block_type, $context ): array {
// Check that the place to hook into is a pattern and
// hook block into the "gatherpress/event-template" pattern.
if (
is_array( $context ) &&
isset( $context['name'] ) &&
'gatherpress/event-template' === $context['name'] &&
'gatherpress/event-date' === $anchor_block_type &&
'before' === $relative_position
) {
$hooked_block_types[] = 'core/post-featured-image';
}
return $hooked_block_types;
}, 10, 4 );
```

### Remove default blocks

**Example**: To remove the *RSVP-Response* block, you could call:

```php
/**
* Remove every use of the RSVP-Response block (everywhere).
*
* @see https://developer.wordpress.org/reference/hooks/hooked_block_types/
*
* @param string[] $hooked_block_types The list of hooked block types.
* @return string[] The modified list of hooked block types.
*/
add_filter( 'hooked_block_types', function( array $hooked_block_types ) : array {
return array_diff( $hooked_block_types, array( 'gatherpress/rsvp-response' ) );
});
```

## Resources

- [@wordpress/hooks - Block Editor Handbook | Developer.WordPress.org](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-hooks/)
- [#devnote - Introducing Block Hooks for dynamic blocks - Make WordPress Core](https://make.wordpress.org/core/2023/10/15/introducing-block-hooks-for-dynamic-blocks/)
Expand Down
100 changes: 99 additions & 1 deletion includes/core/classes/class-block.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore

use GatherPress\Core\Traits\Singleton;
use WP_Block_Template;
use WP_Post;

/**
* Class Block.
Expand Down Expand Up @@ -52,6 +54,9 @@ protected function setup_hooks(): void {
add_action( 'init', array( $this, 'register_block_patterns' ) );
// Priority 11 needed for block.json translations of title and description.
add_action( 'init', array( $this, 'register_blocks' ), 11 );
// Run on priority 9 to allow extenders to use the hooks with the default of 10.
add_filter( 'hooked_block_types', array( $this, 'hook_blocks_into_patterns' ), 9, 4 );
add_filter( 'hooked_block_core/paragraph', array( $this, 'modify_hooked_blocks_in_patterns' ), 9, 5 );
}

/**
Expand Down Expand Up @@ -93,7 +98,7 @@ public function register_block_patterns(): void {
// Even this paragraph seems useless, it's not.
// It is the entry point for all our hooked blocks
// and as such absolutely important!
'content' => '<!-- gatherpress:event-date /--><!-- wp:pattern {"slug":"gatherpress/venue-details"} /-->', // Other blocks are hooked-in here.
'content' => '<!-- wp:gatherpress/event-date /-->', // Other blocks are hooked-in here.
'inserter' => false,
'source' => 'plugin',
),
Expand Down Expand Up @@ -133,4 +138,97 @@ public function register_block_patterns(): void {
register_block_pattern( $block_pattern[0], $block_pattern[1] );
}
}

/**
* Filters the list of hooked block types for a given anchor block type and relative position.
*
* @see https://developer.wordpress.org/reference/hooks/hooked_block_types/
*
* @param string[] $hooked_block_types The list of hooked block types.
* @param string $relative_position The relative position of the hooked blocks. Can be one of 'before', 'after', 'first_child', or 'last_child'.
* @param string $anchor_block_type The anchor block type.
* @param WP_Block_Template|array $context The block template, template part, or pattern that the anchor block belongs to.
* @return string[] The list of hooked block types.
*/
public function hook_blocks_into_patterns( array $hooked_block_types, string $relative_position, ?string $anchor_block_type, $context ): array {
// Check that the place to hook into is a pattern.
if ( ! is_array( $context ) || ! isset( $context['name'] ) ) {
return $hooked_block_types;
}

// Hook blocks into the "gatherpress/event-template" pattern.
if (
'gatherpress/event-template' === $context['name'] &&
'gatherpress/event-date' === $anchor_block_type &&
'after' === $relative_position
) {
$hooked_block_types[] = 'gatherpress/add-to-calendar';

// @todo As soon as the new venue block is in place,
// load 'core/patterns' here
// and fill it with {"slug":"gatherpress/venue-details"} in modify_hooked_blocks_in_patterns() later
// instead of loading the (old) venue block.
$hooked_block_types[] = 'gatherpress/venue';

$hooked_block_types[] = 'gatherpress/rsvp';
$hooked_block_types[] = 'core/paragraph';
$hooked_block_types[] = 'gatherpress/rsvp-response';
}

// Hook blocks into the "gatherpress/venue-template" pattern.
if (
'gatherpress/venue-template' === $context['name'] &&
'core/paragraph' === $anchor_block_type &&
'after' === $relative_position
) {
$hooked_block_types[] = 'gatherpress/venue';
}

return $hooked_block_types;
}

/**
* Filters the parsed block array for a hooked 'core/paragraph' block.
*
* @see https://developer.wordpress.org/reference/hooks/hooked_block_hooked_block_type/
*
* @param array|null $parsed_hooked_block The parsed block array for the given hooked block type, or null to suppress the block.
* @param string $hooked_block_type The hooked block type name.
* @param string $relative_position The relative position of the hooked block.
* @param array $parsed_anchor_block The anchor block, in parsed block array format.
* @param WP_Block_Template|WP_Post|array $context The block template, template part, `wp_navigation` post type,
* or pattern that the anchor block belongs to.
* @return array|null The parsed block array for the given hooked block type, or null to suppress the block.
*/
public function modify_hooked_blocks_in_patterns( ?array $parsed_hooked_block, string $hooked_block_type, string $relative_position, array $parsed_anchor_block, $context ): ?array {

// Has the hooked block been suppressed by a previous filter?
if ( is_null( $parsed_hooked_block ) ) {
return $parsed_hooked_block;
}

// Check that the place to hook into is a pattern.
if ( ! is_array( $context ) || ! isset( $context['name'] ) ) {
return $parsed_hooked_block;
}

// Conditionally hook the block into the "gatherpress/venue-facts" pattern.
if (
'gatherpress/event-template' !== $context['name'] ||
'gatherpress/event-date' !== $parsed_anchor_block['blockName'] ||
'after' !== $relative_position
) {
return $parsed_hooked_block;
}

// The opener text for new Events... a paragraph block.
if ( 'core/paragraph' === $hooked_block_type ) {
$parsed_hooked_block['attrs']['placeholder'] = __(
'Add a description of the event and let people know what to expect, including the agenda, what they need to bring, and how to find the group.',
'gatherpress'
);
}

return $parsed_hooked_block;
}
}
16 changes: 1 addition & 15 deletions includes/core/classes/class-event-setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,21 +114,7 @@ public function register_post_type(): void {
'public' => true,
'hierarchical' => false,
'template' => array(
array( 'gatherpress/event-date' ),
array( 'gatherpress/add-to-calendar' ),
array( 'gatherpress/venue' ),
array( 'gatherpress/rsvp' ),
array(
'core/paragraph',
array(
'placeholder' => __(
'Add a description of the event and let people know what to expect, including the agenda, what they need to bring, and how to find the group.',
'gatherpress'
),
),
),
array( 'gatherpress/rsvp-response' ),
// The future! // array( 'core/pattern', array( 'slug' => 'gatherpress/event-template' ) ), // phpcs:ignore Squiz.PHP.CommentedOutCode.Found !
array( 'core/pattern', array( 'slug' => 'gatherpress/event-template' ) ),
),
'menu_position' => 4,
'supports' => array(
Expand Down
3 changes: 1 addition & 2 deletions includes/core/classes/class-venue.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,7 @@ public function register_post_type(): void {
),
'menu_icon' => 'dashicons-location',
'template' => array(
array( 'gatherpress/venue' ),
// The future! // array( 'core/pattern', array( 'slug' => 'gatherpress/venue-template' ) ), // phpcs:ignore Squiz.PHP.CommentedOutCode.Found !
array( 'core/pattern', array( 'slug' => 'gatherpress/venue-template' ) ),
),
'has_archive' => true,
'rewrite' => array(
Expand Down

0 comments on commit df8f92b

Please sign in to comment.