ブロックパターンと再利用ブロックについて!

ブロックパターンと再利用ブロックについて!

再利用ブロックについて調べました。
記事はインサーターや初期設定など広範囲に及んでます。

まず再利用ブロックと呼ばれていたものは同期ブロックに変更されました。
また独自のブロックパターンを追加することもできます。

  • ここでは独自のブロックパターンを非同期ブロックと呼ぶことにします。

  • ここでは独自のブロックパターンを非同期ブロックと呼ぶことにします。

  • ここでは独自のブロックパターンを非同期ブロックと呼ぶことにします。

どういうこと?

記事を書く際に、毎回同じ形式のブロックを追加したくなることがあります。
これをひな形にして使いまわそうという発想があります。
ブロックをパターン化しておくんですね。

それが非同期ブロックです。
非同期ブロックの場合はブロック挿入した時点でそれ以降はただのブロックの集まりでしかありません。
以後自由にブロックの形式を変えることが出来ます。

それとは逆に、そのひな形をサイト全体で共有しましょう的な発想が同期ブロックです。
同期ブロックに変更があると、別のページから参照されている同期ブロックにも同じように反映されます。
厳密には非同期ブロックの実態は一つで、他からはそれが参照される形になります。

プログラミング言語的な発想でいうと非同期ブロックがで、
同期ブロックが参照値ポインタ的なものです。

どういう仕組み?

同期ブロック/非同期ブロックは投稿タイプ名がwp_blockの投稿にすぎません。
これを知るにはすでにカスタム投稿について知識がある必要があります。

このpost_contentにGutenbergの構文が入ります。

例えば投稿タイプがwp_blockにアクセスしたとします。

https://kurage/wp-admin/edit.php?post_type=wp_block

この例は二つの同期ブロック、二つの非同期ブロックをすでに追加した状態のものです。

投稿を新規追加をする時に、同期か非同期を選択することが出来ます。

追加した同期ブロック/非同期ブロックはデータベース(wp_postsテーブル)から取得出来ます。

SELECT ID, post_title FROM wp_posts
    WHERE post_type = 'wp_block' AND post_status = 'publish';
+-----+--------------------+
| ID  | post_title         |
+-----+--------------------+
| 757 | Test Sync Pattern  |
| 759 | Test Async Pattern |
| 761 | 同期               |
| 763 | 非同期             |
+-----+--------------------+

ブロックの構文はpost_contentカラムに入ってます。

ではどうやって同期ブロックと非同期ブロックを区別しているのでしょう!?
どうやら非同期ブロックにはメタ情報にwp_pattern_sync_status=unsyncedが設定してあるようです。

ちょっとSQLを省きましたが、非同期ブロック一覧を取得してみましょう。

select m.meta_key, m.meta_value, p.post_title
    from wp_postmeta as m
    JOIN wp_posts as p
    ON
        p.ID = m.post_id
        AND
        m.meta_key = "wp_pattern_sync_status";
+------------------------+------------+--------------------+
| meta_key               | meta_value | post_title         |
+------------------------+------------+--------------------+
| wp_pattern_sync_status | unsynced   | Test Async Pattern |
| wp_pattern_sync_status | unsynced   | 非同期             |
+------------------------+------------+--------------------+

これらブロック一覧はREST APIから取得出来ます。

wp/v2/blocks

以下のコードをブラウザから直接実行してみましょう。

(await wp.apiFetch({ path: 'wp/v2/blocks' }))
    .map(_ => ({
        id: _.id,
        title: _.title.raw,
        mode: _.wp_pattern_sync_status
    }))

非同期か同期の情報はwp_pattern_sync_statusで取得出来ます。
メタプロパティとしてではなく、トップレベルプロパティで取得出来ます。

このAPIのコントローラはWP_REST_Blocks_Controllerクラスです。
どうやらこのコントローラがメタをトップレベルプロパティへ移動しているようです。

詳しくはPHPのコードを見てください。

ではこれらブロックを実際に使っていきます。

エディタで同期ブロックや非同期ブロックを追加する時は、インサーターの「パターン」タブから「マイパターン」を選択します。
同期ブロックは紫のひし形アイコンがついているのが確認できます。

試しに非同期ブロック、同期ブロックを順に追加します。
どちらも段落3行分で構成されており、間に区切りを置いてます。

非同期ブロックを追加すると、そのまま段落が追加されているのが確認できます。
逆に同期ブロックは紫の新たなブロックが追加されていて、そのブロックの中に段落が表示されます。

中身はどうなってるでしょう、オプションからコードエディタに変えてみます。

オプションでビジュアルエディタからコードエディタに変更してみます。

追加された同期ブロックの正体は
ブロック名core/blockのブロックエディタであり、
ref属性でその投稿IDを保有しています。

ref757は追加した同期ブロックの投稿IDです。
投稿IDが757の投稿を見てみましょう。

SELECT ID, post_title, post_content FROM wp_posts WHERE ID = 757;

↓ そのままのMySQLの出力ではレイアウトが崩れるので修正してます。

+-----+-------------------+----------------------------------------+
| ID  | post_title        | post_content                           |
+-----+-------------------+----------------------------------------+
| 757 | Test Sync Pattern | <!-- wp:paragraph -->
                            <p>再利用パターンです!</p>
                            <!-- /wp:paragraph -->

                            <!-- wp:paragraph -->
                            <p>クラゲは泳ぐ!</p>
                            <!-- /wp:paragraph -->

                            <!-- wp:paragraph -->
                            <p>どこまでも、泳ぐ!</p>
                            <!-- /wp:paragraph -->                 |
+-----+-------------------+----------------------------------------+

このブロックはダイナミックブロック(サーバーサイドでレンダリングされる)で実装されており、
フロントエンドからリクエストがあるたびに投稿(投稿(wp_block)のID)を取得し、
post_contentを解析してからレンダリングを行ってます。

つまりこのブロックが必要とされる時に投稿(757)から構文を持ってきていい感じに変換してくれるわけです。

詳しくは

block-library/src/block/index.php

を見てください。

ざっくり読むと、
ref属性の投稿IDからget_post()を使って同期ブロックの投稿を取得し、
post_contentの内容ををdo_blocks()に渡してます。

複雑なので解読はしませんが、このような仕組みになっていることが分かりました。

以降はインサーターからカテゴリ、パターンまでUI部分を調べていきます。

InserterMenu

.block-editor-inserter__menu

block-editor/src/components/inserter/menu.js

CSSが指定してあるところ。

InserterTabs

.block-editor-inserter__tabs

block-editor/src/components/inserter/tabs.js

CSSが指定してあるところ

ちなみにtabsContentsがプロパティから取得され、
この要素がタブのコンテンツ部分に配置される。
例えばパターンの場合はtabsContents[patterns]でパネルのコンテンツが決定する。
このtabsCpmtemtsはメニュー側で設定されている。

例えばパターンの場合はInserterMenuで以下のように定義されている。

const patternsTab = useMemo(
    () => (
        <BlockPatternsTab
            rootClientId={ destinationRootClientId }
            onInsert={ onInsertPattern }
            onSelectCategory={ onClickPatternCategory }
            selectedCategory={ selectedPatternCategory }
        />
    ),
    [
        destinationRootClientId,
        onInsertPattern,
        onClickPatternCategory,
        selectedPatternCategory,
    ]
);

BlockPatternsTab

ブロック、パターン、メディアのうちパターンがタブに表示される場合についてです。

.block-editor-inserter__block-patterns-tabs-container

block-editor/src/components/inserter/block-patterns-tab/index.js

CSSが指定してあるところ

PatternCategoryPreviews

  • BlockPatternsTab内にて呼び出し。

例えば投稿タイプwp_blockに自分で追加することが出来る同期パターンや非同期パターンはこのマイパターンをクリックすることパターン一覧のダイアログを表示することが出来る。

.block-editor-inserter__patterns-category-panel

block-patterns-tab/pattern-category-previews.js

CSSが指定してあるところ

BlockPatternsList

  • PatternCategoryPreviews内にて呼び出し

実際にパターンのリストを表示する部分です。

block-editor/src/components/block-patterns-list/index.js

PatternsExplorerModal

  • BlockPatternsTab内にて呼び出し。

をクリックするとダイアログが開きます。

PatternsExplorer

block-editor/src/components/inserter/block-patterns-explorer/index.js

.block-editor-block-patterns-explorer

CSSが指定してあるところ

return (
    <div className="block-editor-block-patterns-explorer">
        <PatternExplorerSidebar
            selectedCategory={ selectedCategory }
            patternCategories={ patternCategories }
            onClickCategory={ setSelectedCategory }
            searchValue={ searchValue }
            setSearchValue={ setSearchValue }
            patternSourceFilter={ patternSourceFilter }
            setPatternSourceFilter={ setPatternSourceFilter }
        />
        <PatternList
            searchValue={ searchValue }
            selectedCategory={ selectedCategory }
            patternCategories={ patternCategories }
            patternSourceFilter={ patternSourceFilter }
        />
    </div>
);

PatternListは内部でBlockPatternsListを呼び出してます。

パターン一覧はどっから持ってくる?

wp.data.select('core/block-editor').__experimentalGetAllowedPatterns()

最初の4行、見てわかる通り同期ブロックや非同期ブロックは名前が

core/block/123

のような形式になってます。
123の部分は投稿IDです。

また、idプロパティにその投稿IDが設定してあります。

  • バージョンによってはpattern.typeを比較しているようですが未確認。

肝心のブロックは構文が解析されブロック化された状態でblocksプロパティに入ってます。

以下はブラウザから実行したコードです。

wp.data.select('core/block-editor')
    .__experimentalGetAllowedPatterns()
    .map(( { name, id, blocks } ) => ({
        name,
        id: id ?? 'NULL',
        blocks: blocks.map(b => b.name).join(', ') 
    }))

blocksはブロックエディタ名を文字列化して表示してます。

ただしblocksのブロックリストをそのまま使用してはいけません。

  • 同期ブロックの場合はcore/blockに変換します。
  • cloneBlock()でクローンを作ってそれをインサートするようにします。

仕掛けはパターンがクリックされたときに起こります。

パターンがクリックされた時の動作

InserterMenuではパターンがクリックされたときにブロックを受け取るためのコールバック、onInsertPatternを準備してBlockPatternsTabonInsertに渡します。

<BlockPatternsTab
    rootClientId={ destinationRootClientId }
    onInsert={ onInsertPattern }
    onSelectCategory={ onClickPatternCategory }
    selectedCategory={ selectedPatternCategory }
/>

BlockPatternsTabPatternCategoryPreviewsをホストしますが、
onInsertを横流しします。

PatternCategoryPreviewsはパターン一覧を表示するBlockPatternListをホストします。
この時、パターンがクリックされたときに呼び出されるコールバック、onClickPatternをonClickPatternプロパティに渡します。

<BlockPatternsList
    ref={ scrollContainerRef }
    shownPatterns={ pagingProps.categoryPatternsAsyncList }
    blockPatterns={ pagingProps.categoryPatterns }
    onClickPattern={ onClickPattern }
    onHover={ onHover }
    label={ category.label }
    orientation="vertical"
    category={ category.name }
    isDraggable
    showTitlesAsTooltip={ showTitlesAsTooltip }
    patternFilter={ patternSourceFilter }
    pagingProps={ pagingProps }
/>

マイパターンの場合はこのコールバックに仕掛けがあります。
このコールバック、onClickPatternusePatternsState()の戻り値です。

const [ allPatterns, , onClickPattern ] = usePatternsState(
    onInsert,
    rootClientId
);

onInsertをラップしてます。

ではusePatternsState()の中身の一部を見てみます。

block-editor/src/components/inserter/hooks/use-patterns-state.js

onClickPatternの実装の部分だけ切り出してみます。

const onClickPattern = useCallback(
    ( pattern, blocks ) => {
        const patternBlocks =
            pattern.type === PATTERN_TYPES.user &&
            pattern.syncStatus !== 'unsynced'
                ? [ createBlock( 'core/block', { ref: pattern.id } ) ]
                : blocks;
        onInsert(
            ( patternBlocks ?? [] ).map( ( block ) => cloneBlock( block ) ),
            pattern.name
        );
        createSuccessNotice(
            sprintf(
                /* translators: %s: block pattern title. */
                __( 'Block pattern "%s" inserted.' ),
                pattern.title
            ),
            {
                type: 'snackbar',
                id: 'block-pattern-inserted-notice',
            }
        );
    },
    [ createSuccessNotice, onInsert ]
);
pattern.type === PATTERN_TYPES.user

の部分は、バージョンによっては

pattern.id

というようにidが存在するかどうかで判断されているようです。
どっちが新しいか確認はしてません。

pattern.syncStatus !== 'unsynced'

この二つの条件からパターンが同期ブロックのものかどうかをチェックしてます。

[ createBlock( 'core/block', { ref: pattern.id } ) ]

同期ブロックのものであれば、選択したブロックではなくcore/blockのブロックエディタに変換されているのが分かります。

そしてonInsert()にブロックのリストを渡しているのですが、
ここで重要なのがcloneBlock()でクローン化していることです。

マイパターンのカテゴリはどこからやってくる?

状態からカテゴリ一覧を取得できますが、その中にマイパターンは存在しません。
どの段階でマイパターンが一覧に加わっているのでしょう!?

投稿ページのエディタの初期化時に__experimentalReusableBlocksの名前で再利用ブロックが保存されます。
その流れをエディタから見ていきます。

Editor

  • 投稿のEditorでプロバイダが呼ばれる。

ExperimentalEditorProvider

  • プロバイダではエディタの設定情報(blockEditorSettings)が取得される

useBlockEditorSettings

  • この関数はエディタの初期設定を返す。この時、再利用ブロックはgetEntityRecords('postType', 'wp_block', ... ) から取得される。

ではカテゴリがレンダリングされる際にどうやって一覧を取得しているかです。

BlockPatternsTab内ではカテゴリ一覧を取得するため以下のコードがあります。
ちょっと複雑です。

const categories = usePatternCategories( rootClientId );

block-editor/src/components/inserter/block-patterns-tab/use-pattern-categories.js

この関数は、もしパターンにidを持つオブジェクトがあれば(つまり同期ブロック/非同期ブロックが一つ以上あれば)、カテゴリにマイパターンが追加される仕組みになってます。

他のタブ

通常のブロック追加のタブは以下のコードに書かれてます。

block-editor/src/components/inserter/block-types-tab.js

BlockEditor certificate css DataGrid Docker Gutenberg Hyper-V iframe MUI openssl PHP React ReduxToolkit REST ubuntu WordPress オレオレ認証局 フレームワーク