register_block_type_from_metadata() │ WP 5.5.0

Click here to view original web page at wp-kama.ru

Регистрирует блок на основе метаданных из файла block.json.

Используйте register_block_type(), чтобы зарегистрировать блок обычном способом (без block.json файла).

Смотрите также описание JS функции registerBlockType() многие из параметров этой функции передаются туда.

Работает на основе: WP_Block_Type_Registry(), wp_json_file_decode(), register_block_script_handle(), register_block_style_handle(), register_block_script_module_id()

Основа для: register_block_type()

Хуки из функции

block_type_metadata

block_type_metadata_settings

Возвращает

WP_Block_Type|false. Зарегистрированный тип блока или false при неудачи.

Использование

register_block_type_from_metadata( $file_or_folder, $args );
$file_or_folder(string) (required)

Путь к файлу JSON с метаданными для блока. Или путь к папке, где расположен файл block.json.

Если указывается путь к файлу JSON, имя файла должно заканчиваться на block.json.

Путь к файлу блока:

register_block_type_from_metadata( __DIR__ . '/block.json' );

Или можно указать путь к папке где лежит файл block.json:

register_block_type_from_metadata( __DIR__ . '/my-block' );
$args(array)
Массив аргументов блока. Принимает любое публичное свойство WP_Block_Type{}. См. WP_Block_Type::__construct() для полного списка принимаемых аргументов.
По умолчанию: пустой массив

Примеры

0

#1 Использование WP Dashicon для блока

Для этого нужно в $args в параметре icon указать иконку без префикса dashicons-:

add_action( 'init', 'wpkama_register_block' );

function wpkama_register_block(){
	register_block_type(
		__DIR__ . '/block.json',
		[
			'icon' => 'admin-home', /* omit 'dashicons-' prefix */
		]
	);
}

Все имена Dashicons: https://developer.wordpress.org/resource/dashicons/

0

#2 Как написать плагин/тему с несколькими блоками

Создание папки src

  1. Запустите команду:

    npx @wordpress/create-block@latest my-blocks --variant=dynamic
    cd my-blocks

    Подробнее смотрите мануал https://developer.wordpress.org/block-editor/getting-started/tutorial/

  2. Переместите содержимое каталога src в подкаталог, например block-a: src/block-a.

  3. Продублируйте подкаталог block-a, чтобы создать второй блок, и назовите его, например, block-b.

  4. Обновите файлы block.json в каждом подкаталоге, чтобы они соответствовали требованиям блоков.

    Должна получится такая структура:

    my-blocks
    ├── package.json
    ├── package-lock.json
    └── src
    	├── block-a
    	│   ├── block.json
    	│   ├── edit.js
    	│   ├── editor.scss
    	│   ├── index.js
    	│   ├── render.php
    	│   ├── style.scss
    	│   └── view.js
    	└── block-b
    		├── block.json
    		├── edit.js
    		├── editor.scss
    		├── index.js
    		├── render.php
    		├── style.scss
    		└── view.js

    Пример содержимого block.json:

    {
    	"$schema": "https://schemas.wp.org/trunk/block.json",
    	"apiVersion": 3,
    	"name": "create-block/block-a",
    	"version": "0.1.0",
    	"title": "Block A",
    	"category": "widgets",
    	"icon": "smiley",
    	"description": "Example block scaffolded with Create Block tool.",
    	"example": {},
    	"supports": {
    		"html": false
    	},
    	"textdomain": "wpkama",
    	"render": "file:./render.php",
    	"editorScript": "file:./index.js",
    	"editorStyle": "file:./index.css",
    	"viewStyle": "file:./style-index.css",
    	"viewScript": "file:./view.js"
    }
  5. Выполните команду npm run build в каталоге my-blocks. Будут созданы соответствующие директории в папке my-blocks/build.

Регистрация блоков

Теперь нужно зарегистрировать блоки в PHP, указав на соответствующую директорию в папке build:

add_action( 'init', 'wpdocs_create_blocks_mysite_block_init' );

function wpdocs_create_blocks_mysite_block_init() {

	register_block_type( __DIR__ . '/build/block-a' );
	register_block_type( __DIR__ . '/build/block-b' );
}

Перемещение папки блоков внутрь проекта

Если у вас папка npm пакетов node_modules находится где-то выше, а блоки должны находится внутри, например в папке темы, то можно указать пути где лежат исходники и куда выкладывать билды.

Для этого добавьте опции в скрипты build и start в файле package.json:

"scripts": {
	"build": "wp-scripts build --webpack-src-dir=path/to/my-blocks/src/ --output-path=path/to/my-blocks/build/ --webpack-copy-php",
	"start": "wp-scripts start --webpack-src-dir=path/to/my-blocks/src/ --output-path=path/to/my-blocks/build/ --webpack-copy-php",
	...
}

Теперь npm run build можно запускать из папки где лежит package.json, и блоки будут билдиться в во внутренней папке (там где вы указали).

0

#3 Авто-создание блоков через .json файлы

class My_Blocks {

	public function setup_hooks(): void {
		add_action( 'acf/init', [ $this, 'register_blocks' ] );
		add_filter( 'block_categories_all', [ $this, 'register_block_category' ] );
	}

	public function register_blocks(): void {

		$blocks = glob( __DIR__ . '/Blocks/*/block.json');

		if ( ! $blocks ) {
			return;
		}

		foreach ( $blocks as $block ) {
			register_block_type( $block );
		}
	}

}

( new My_Blocks() )->setup_hooks();

Пример .json файла:

{
  "name": "ice-cream/slider",
  "title": "Ice Cream Slider",
  "description": "Простой настраиваемый слайдер изображений",
  "style": "block.css", 
  "category": "ice-cream",
  "icon": "images-alt",
  "apiVersion": 2,
  "keywords": [],
  "acf": {
	"mode": "preview",
	"renderTemplate": "render.php"
  },
  "styles": [],
  "supports": {
	"align": false,
	"anchor": false,
	"alignContent": false,
	"color": {
	  "text": false,
	  "background": true,
	  "link": false
	},
	"alignText": false,
	"fullHeight": false
  }
}

Read more here: https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/

Список изменений

С версии 5.5.0Введена.
С версии 5.7.0Added support for textdomain field and i18n handling for all translatable fields.
С версии 5.9.0Added support for variations and viewScript fields.
С версии 6.1.0Added support for render field.
С версии 6.3.0Added selectors field.
С версии 6.4.0Added support for blockHooks field.
С версии 6.5.0Added support for allowedBlocks, viewScriptModule, and viewStyle fields.

Код register_block_type_from_metadata() WP 6.5.5

wp-includes/blocks.php

function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
	/*
	 * Get an array of metadata from a PHP file.
	 * This improves performance for core blocks as it's only necessary to read a single PHP file
	 * instead of reading a JSON file per-block, and then decoding from JSON to PHP.
	 * Using a static variable ensures that the metadata is only read once per request.
	 */
	static $core_blocks_meta;
	if ( ! $core_blocks_meta ) {
		$core_blocks_meta = require ABSPATH . WPINC . '/blocks/blocks-json.php';
	}

	$metadata_file = ( ! str_ends_with( $file_or_folder, 'block.json' ) ) ?
		trailingslashit( $file_or_folder ) . 'block.json' :
		$file_or_folder;

	$is_core_block = str_starts_with( $file_or_folder, ABSPATH . WPINC );
	// If the block is not a core block, the metadata file must exist.
	$metadata_file_exists = $is_core_block || file_exists( $metadata_file );
	if ( ! $metadata_file_exists && empty( $args['name'] ) ) {
		return false;
	}

	// Try to get metadata from the static cache for core blocks.
	$metadata = array();
	if ( $is_core_block ) {
		$core_block_name = str_replace( ABSPATH . WPINC . '/blocks/', '', $file_or_folder );
		if ( ! empty( $core_blocks_meta[ $core_block_name ] ) ) {
			$metadata = $core_blocks_meta[ $core_block_name ];
		}
	}

	// If metadata is not found in the static cache, read it from the file.
	if ( $metadata_file_exists && empty( $metadata ) ) {
		$metadata = wp_json_file_decode( $metadata_file, array( 'associative' => true ) );
	}

	if ( ! is_array( $metadata ) || ( empty( $metadata['name'] ) && empty( $args['name'] ) ) ) {
		return false;
	}

	$metadata['file'] = $metadata_file_exists ? wp_normalize_path( realpath( $metadata_file ) ) : null;

	/**
	 * Filters the metadata provided for registering a block type.
	 *
	 * @since 5.7.0
	 *
	 * @param array $metadata Metadata for registering a block type.
	 */
	$metadata = apply_filters( 'block_type_metadata', $metadata );

	// Add `style` and `editor_style` for core blocks if missing.
	if ( ! empty( $metadata['name'] ) && str_starts_with( $metadata['name'], 'core/' ) ) {
		$block_name = str_replace( 'core/', '', $metadata['name'] );

		if ( ! isset( $metadata['style'] ) ) {
			$metadata['style'] = "wp-block-$block_name";
		}
		if ( current_theme_supports( 'wp-block-styles' ) && wp_should_load_separate_core_block_assets() ) {
			$metadata['style']   = (array) $metadata['style'];
			$metadata['style'][] = "wp-block-{$block_name}-theme";
		}
		if ( ! isset( $metadata['editorStyle'] ) ) {
			$metadata['editorStyle'] = "wp-block-{$block_name}-editor";
		}
	}

	$settings          = array();
	$property_mappings = array(
		'apiVersion'      => 'api_version',
		'name'            => 'name',
		'title'           => 'title',
		'category'        => 'category',
		'parent'          => 'parent',
		'ancestor'        => 'ancestor',
		'icon'            => 'icon',
		'description'     => 'description',
		'keywords'        => 'keywords',
		'attributes'      => 'attributes',
		'providesContext' => 'provides_context',
		'usesContext'     => 'uses_context',
		'selectors'       => 'selectors',
		'supports'        => 'supports',
		'styles'          => 'styles',
		'variations'      => 'variations',
		'example'         => 'example',
		'allowedBlocks'   => 'allowed_blocks',
	);
	$textdomain        = ! empty( $metadata['textdomain'] ) ? $metadata['textdomain'] : null;
	$i18n_schema       = get_block_metadata_i18n_schema();

	foreach ( $property_mappings as $key => $mapped_key ) {
		if ( isset( $metadata[ $key ] ) ) {
			$settings[ $mapped_key ] = $metadata[ $key ];
			if ( $metadata_file_exists && $textdomain && isset( $i18n_schema->$key ) ) {
				$settings[ $mapped_key ] = translate_settings_using_i18n_schema( $i18n_schema->$key, $settings[ $key ], $textdomain );
			}
		}
	}

	if ( ! empty( $metadata['render'] ) ) {
		$template_path = wp_normalize_path(
			realpath(
				dirname( $metadata['file'] ) . '/' .
				remove_block_asset_path_prefix( $metadata['render'] )
			)
		);
		if ( $template_path ) {
			/**
			 * Renders the block on the server.
			 *
			 * @since 6.1.0
			 *
			 * @param array    $attributes Block attributes.
			 * @param string   $content    Block default content.
			 * @param WP_Block $block      Block instance.
			 *
			 * @return string Returns the block content.
			 */
			$settings['render_callback'] = static function ( $attributes, $content, $block ) use ( $template_path ) {
				ob_start();
				require $template_path;
				return ob_get_clean();
			};
		}
	}

	$settings = array_merge( $settings, $args );

	$script_fields = array(
		'editorScript' => 'editor_script_handles',
		'script'       => 'script_handles',
		'viewScript'   => 'view_script_handles',
	);
	foreach ( $script_fields as $metadata_field_name => $settings_field_name ) {
		if ( ! empty( $settings[ $metadata_field_name ] ) ) {
			$metadata[ $metadata_field_name ] = $settings[ $metadata_field_name ];
		}
		if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
			$scripts           = $metadata[ $metadata_field_name ];
			$processed_scripts = array();
			if ( is_array( $scripts ) ) {
				for ( $index = 0; $index < count( $scripts ); $index++ ) {
					$result = register_block_script_handle(
						$metadata,
						$metadata_field_name,
						$index
					);
					if ( $result ) {
						$processed_scripts[] = $result;
					}
				}
			} else {
				$result = register_block_script_handle(
					$metadata,
					$metadata_field_name
				);
				if ( $result ) {
					$processed_scripts[] = $result;
				}
			}
			$settings[ $settings_field_name ] = $processed_scripts;
		}
	}

	$module_fields = array(
		'viewScriptModule' => 'view_script_module_ids',
	);
	foreach ( $module_fields as $metadata_field_name => $settings_field_name ) {
		if ( ! empty( $settings[ $metadata_field_name ] ) ) {
			$metadata[ $metadata_field_name ] = $settings[ $metadata_field_name ];
		}
		if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
			$modules           = $metadata[ $metadata_field_name ];
			$processed_modules = array();
			if ( is_array( $modules ) ) {
				for ( $index = 0; $index < count( $modules ); $index++ ) {
					$result = register_block_script_module_id(
						$metadata,
						$metadata_field_name,
						$index
					);
					if ( $result ) {
						$processed_modules[] = $result;
					}
				}
			} else {
				$result = register_block_script_module_id(
					$metadata,
					$metadata_field_name
				);
				if ( $result ) {
					$processed_modules[] = $result;
				}
			}
			$settings[ $settings_field_name ] = $processed_modules;
		}
	}

	$style_fields = array(
		'editorStyle' => 'editor_style_handles',
		'style'       => 'style_handles',
		'viewStyle'   => 'view_style_handles',
	);
	foreach ( $style_fields as $metadata_field_name => $settings_field_name ) {
		if ( ! empty( $settings[ $metadata_field_name ] ) ) {
			$metadata[ $metadata_field_name ] = $settings[ $metadata_field_name ];
		}
		if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
			$styles           = $metadata[ $metadata_field_name ];
			$processed_styles = array();
			if ( is_array( $styles ) ) {
				for ( $index = 0; $index < count( $styles ); $index++ ) {
					$result = register_block_style_handle(
						$metadata,
						$metadata_field_name,
						$index
					);
					if ( $result ) {
						$processed_styles[] = $result;
					}
				}
			} else {
				$result = register_block_style_handle(
					$metadata,
					$metadata_field_name
				);
				if ( $result ) {
					$processed_styles[] = $result;
				}
			}
			$settings[ $settings_field_name ] = $processed_styles;
		}
	}

	if ( ! empty( $metadata['blockHooks'] ) ) {
		/**
		 * Map camelCased position string (from block.json) to snake_cased block type position.
		 *
		 * @var array
		 */
		$position_mappings = array(
			'before'     => 'before',
			'after'      => 'after',
			'firstChild' => 'first_child',
			'lastChild'  => 'last_child',
		);

		$settings['block_hooks'] = array();
		foreach ( $metadata['blockHooks'] as $anchor_block_name => $position ) {
			// Avoid infinite recursion (hooking to itself).
			if ( $metadata['name'] === $anchor_block_name ) {
				_doing_it_wrong(
					__METHOD__,
					__( 'Cannot hook block to itself.' ),
					'6.4.0'
				);
				continue;
			}

			if ( ! isset( $position_mappings[ $position ] ) ) {
				continue;
			}

			$settings['block_hooks'][ $anchor_block_name ] = $position_mappings[ $position ];
		}
	}

	/**
	 * Filters the settings determined from the block type metadata.
	 *
	 * @since 5.7.0
	 *
	 * @param array $settings Array of determined settings for registering a block type.
	 * @param array $metadata Metadata provided for registering a block type.
	 */
	$settings = apply_filters( 'block_type_metadata_settings', $settings, $metadata );

	$metadata['name'] = ! empty( $settings['name'] ) ? $settings['name'] : $metadata['name'];

	return WP_Block_Type_Registry::get_instance()->register(
		$metadata['name'],
		$settings
	);
}

Cвязанные функции

Редактор блоков (Gutenberg Гутенберг blocks)