Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
Register patterns under patterns folder and add filters pattern (#6861
Browse files Browse the repository at this point in the history
)

* POC about registering patterns

* POC load patterns from `/patterns` folder

* Change class name and remove unnecessary comment

* Add all four blocks to the filters pattern

* Add all 4 blocks to the block type to allow for transformations

* Add internal

* Improve filters name

* Extract regex to constants

* Add class description

* Rename and move class

* Fix wrong imports

* Address feedback

* Early return from condition

* Fix wrong block type
  • Loading branch information
albarin authored Aug 12, 2022
1 parent ec7cc45 commit 0ae997d
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 4 deletions.
24 changes: 24 additions & 0 deletions patterns/filters.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php
/**
* Title: WooCommerce Product Filters
* Slug: woocommerce-blocks/product-filters
* Categories: WooCommerce
* Block Types: woocommerce/active-filters, woocommerce/price-filter, woocommerce/attribute-filter, woocommerce/stock-filter
*/
?>

<!-- wp:woocommerce/active-filters -->
<div class="wp-block-woocommerce-active-filters is-loading" data-display-style="list" data-heading="Active filters" data-heading-level="3"><span aria-hidden="true" class="wc-block-active-product-filters__placeholder"></span></div>
<!-- /wp:woocommerce/active-filters -->

<!-- wp:woocommerce/price-filter -->
<div class="wp-block-woocommerce-price-filter is-loading" data-showinputfields="true" data-showfilterbutton="false" data-heading="Filter by price" data-heading-level="3"><span aria-hidden="true" class="wc-block-product-categories__placeholder"></span></div>
<!-- /wp:woocommerce/price-filter -->

<!-- wp:woocommerce/attribute-filter -->
<div class="wp-block-woocommerce-attribute-filter is-loading" data-attribute-id="0" data-show-counts="true" data-query-type="or" data-heading="Filter by attribute" data-heading-level="3"><span aria-hidden="true" class="wc-block-product-attribute-filter__placeholder"></span></div>
<!-- /wp:woocommerce/attribute-filter -->

<!-- wp:woocommerce/stock-filter -->
<div class="wp-block-woocommerce-stock-filter is-loading" data-show-counts="true" data-heading="Filter by stock status" data-heading-level="3"><span aria-hidden="true" class="wc-block-product-stock-filter__placeholder"></span></div>
<!-- /wp:woocommerce/stock-filter -->
5 changes: 5 additions & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,17 @@
<rule ref="Squiz.Commenting.FileComment.MissingPackageTag">
<exclude-pattern>src/</exclude-pattern>
<exclude-pattern>tests/php</exclude-pattern>
<exclude-pattern>patterns</exclude-pattern>
</rule>
<rule ref="Squiz.Commenting.FileComment.Missing">
<exclude-pattern>src/</exclude-pattern>
<exclude-pattern>tests/php</exclude-pattern>
</rule>

<rule ref="Squiz.Commenting.FileComment.SpacingAfterComment">
<exclude-pattern>patterns</exclude-pattern>
</rule>

<rule ref="Generic.Commenting">
<exclude-pattern>tests/</exclude-pattern>
</rule>
Expand Down
201 changes: 201 additions & 0 deletions src/BlockPatterns.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
<?php
namespace Automattic\WooCommerce\Blocks;

use Automattic\WooCommerce\Blocks\Domain\Package;

/**
* Registers patterns under the `./patterns/` directory.
* Each pattern is defined as a PHP file and defines its metadata using plugin-style headers.
* The minimum required definition is:
*
* /**
* * Title: My Pattern
* * Slug: my-theme/my-pattern
* *
*
* The output of the PHP source corresponds to the content of the pattern, e.g.:
*
* <main><p><?php echo "Hello"; ?></p></main>
*
* Other settable fields include:
*
* - Description
* - Viewport Width
* - Categories (comma-separated values)
* - Keywords (comma-separated values)
* - Block Types (comma-separated values)
* - Inserter (yes/no)
*
* @internal
*/
class BlockPatterns {
const SLUG_REGEX = '/^[A-z0-9\/_-]+$/';
const COMMA_SEPARATED_REGEX = '/[\s,]+/';

/**
* Path to the patterns directory.
*
* @var string $patterns_path
*/
private $patterns_path;

/**
* Constructor for class
*
* @param Package $package An instance of Package.
*/
public function __construct( Package $package ) {
$this->patterns_path = $package->get_path( 'patterns' );

add_action( 'init', array( $this, 'register_block_patterns' ) );
}

/**
* Registers the block patterns and categories under `./patterns/`.
*/
public function register_block_patterns() {
if ( ! class_exists( 'WP_Block_Patterns_Registry' ) ) {
return;
}

$default_headers = array(
'title' => 'Title',
'slug' => 'Slug',
'description' => 'Description',
'viewportWidth' => 'Viewport Width',
'categories' => 'Categories',
'keywords' => 'Keywords',
'blockTypes' => 'Block Types',
'inserter' => 'Inserter',
);

if ( ! file_exists( $this->patterns_path ) ) {
return;
}

$files = glob( $this->patterns_path . '/*.php' );
if ( ! $files ) {
return;
}

foreach ( $files as $file ) {
$pattern_data = get_file_data( $file, $default_headers );

if ( empty( $pattern_data['slug'] ) ) {
_doing_it_wrong(
'register_block_patterns',
esc_html(
sprintf(
/* translators: %s: file name. */
__( 'Could not register file "%s" as a block pattern ("Slug" field missing)', 'woo-gutenberg-products-block' ),
$file
)
),
'6.0.0'
);
continue;
}

if ( ! preg_match( self::SLUG_REGEX, $pattern_data['slug'] ) ) {
_doing_it_wrong(
'register_block_patterns',
esc_html(
sprintf(
/* translators: %1s: file name; %2s: slug value found. */
__( 'Could not register file "%1$s" as a block pattern (invalid slug "%2$s")', 'woo-gutenberg-products-block' ),
$file,
$pattern_data['slug']
)
),
'6.0.0'
);
continue;
}

if ( \WP_Block_Patterns_Registry::get_instance()->is_registered( $pattern_data['slug'] ) ) {
continue;
}

// Title is a required property.
if ( ! $pattern_data['title'] ) {
_doing_it_wrong(
'register_block_patterns',
esc_html(
sprintf(
/* translators: %1s: file name; %2s: slug value found. */
__( 'Could not register file "%s" as a block pattern ("Title" field missing)', 'woo-gutenberg-products-block' ),
$file
)
),
'6.0.0'
);
continue;
}

// For properties of type array, parse data as comma-separated.
foreach ( array( 'categories', 'keywords', 'blockTypes' ) as $property ) {
if ( ! empty( $pattern_data[ $property ] ) ) {
$pattern_data[ $property ] = array_filter(
preg_split(
self::COMMA_SEPARATED_REGEX,
(string) $pattern_data[ $property ]
)
);
} else {
unset( $pattern_data[ $property ] );
}
}

// Parse properties of type int.
foreach ( array( 'viewportWidth' ) as $property ) {
if ( ! empty( $pattern_data[ $property ] ) ) {
$pattern_data[ $property ] = (int) $pattern_data[ $property ];
} else {
unset( $pattern_data[ $property ] );
}
}

// Parse properties of type bool.
foreach ( array( 'inserter' ) as $property ) {
if ( ! empty( $pattern_data[ $property ] ) ) {
$pattern_data[ $property ] = in_array(
strtolower( $pattern_data[ $property ] ),
array( 'yes', 'true' ),
true
);
} else {
unset( $pattern_data[ $property ] );
}
}

// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText, WordPress.WP.I18n.LowLevelTranslationFunction
$pattern_data['title'] = translate_with_gettext_context( $pattern_data['title'], 'Pattern title', 'woo-gutenberg-products-block' );
if ( ! empty( $pattern_data['description'] ) ) {
// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText, WordPress.WP.I18n.LowLevelTranslationFunction
$pattern_data['description'] = translate_with_gettext_context( $pattern_data['description'], 'Pattern description', 'woo-gutenberg-products-block' );
}

// The actual pattern content is the output of the file.
ob_start();
include $file;
$pattern_data['content'] = ob_get_clean();
if ( ! $pattern_data['content'] ) {
continue;
}

foreach ( $pattern_data['categories'] as $key => $category ) {
$category_slug = _wp_to_kebab_case( $category );

$pattern_data['categories'][ $key ] = $category_slug;

register_block_pattern_category(
$category_slug,
// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText
array( 'label' => __( $category, 'woo-gutenberg-products-block' ) )
);
}

register_block_pattern( $pattern_data['slug'], $pattern_data );
}
}
}
16 changes: 12 additions & 4 deletions src/Domain/Bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

use Automattic\WooCommerce\Blocks\Assets\Api as AssetApi;
use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry;
use Automattic\WooCommerce\Blocks\Migration;
use Automattic\WooCommerce\Blocks\AssetsController;
use Automattic\WooCommerce\Blocks\BlockPatterns;
use Automattic\WooCommerce\Blocks\BlockTemplatesController;
use Automattic\WooCommerce\Blocks\BlockTypesController;
use Automattic\WooCommerce\Blocks\Domain\Services\CreateAccount;
Expand All @@ -13,19 +13,20 @@
use Automattic\WooCommerce\Blocks\Domain\Services\GoogleAnalytics;
use Automattic\WooCommerce\Blocks\InboxNotifications;
use Automattic\WooCommerce\Blocks\Installer;
use Automattic\WooCommerce\Blocks\Templates\ProductSearchResultsTemplate;
use Automattic\WooCommerce\Blocks\Templates\ClassicTemplatesCompatibility;
use Automattic\WooCommerce\Blocks\Migration;
use Automattic\WooCommerce\Blocks\Payments\Api as PaymentsApi;
use Automattic\WooCommerce\Blocks\Payments\Integrations\BankTransfer;
use Automattic\WooCommerce\Blocks\Payments\Integrations\CashOnDelivery;
use Automattic\WooCommerce\Blocks\Payments\Integrations\Cheque;
use Automattic\WooCommerce\Blocks\Payments\Integrations\PayPal;
use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
use Automattic\WooCommerce\Blocks\Registry\Container;
use Automattic\WooCommerce\Blocks\Templates\ClassicTemplatesCompatibility;
use Automattic\WooCommerce\Blocks\Templates\ProductAttributeTemplate;
use Automattic\WooCommerce\StoreApi\StoreApi;
use Automattic\WooCommerce\Blocks\Templates\ProductSearchResultsTemplate;
use Automattic\WooCommerce\StoreApi\RoutesController;
use Automattic\WooCommerce\StoreApi\SchemaController;
use Automattic\WooCommerce\StoreApi\StoreApi;

/**
* Takes care of bootstrapping the plugin.
Expand Down Expand Up @@ -119,6 +120,7 @@ function() {
$this->container->get( ProductSearchResultsTemplate::class );
$this->container->get( ProductAttributeTemplate::class );
$this->container->get( ClassicTemplatesCompatibility::class );
$this->container->get( BlockPatterns::class );
if ( $this->package->feature()->is_feature_plugin_build() ) {
$this->container->get( PaymentsApi::class );
}
Expand Down Expand Up @@ -332,6 +334,12 @@ function( Container $container ) {
return $container->get( StoreApi::class )::container()->get( RoutesController::class );
}
);
$this->container->register(
BlockPatterns::class,
function () {
return new BlockPatterns( $this->package );
}
);
}

/**
Expand Down

0 comments on commit 0ae997d

Please sign in to comment.