ブロックパターンと再利用ブロックについて!
再利用ブロックについて調べました。
記事はインサーターや初期設定など広範囲に及んでます。
まず再利用ブロック
と呼ばれていたものは同期ブロック
に変更されました。
また独自のブロックパターンを追加することもできます。
-
ここでは独自のブロックパターンを
非同期ブロック
と呼ぶことにします。 -
ここでは独自のブロックパターンを
非同期ブロック
と呼ぶことにします。 -
ここでは独自のブロックパターンを
非同期ブロック
と呼ぶことにします。
どういうこと?
記事を書く際に、毎回同じ形式のブロックを追加したくなることがあります。
これをひな形にして使いまわそうという発想があります。
ブロックをパターン化しておくんですね。
それが非同期ブロックです。
非同期ブロックの場合はブロック挿入した時点でそれ以降はただのブロックの集まりでしかありません。
以後自由にブロックの形式を変えることが出来ます。
それとは逆に、そのひな形をサイト全体で共有しましょう的な発想が同期ブロックです。
同期ブロックに変更があると、別のページから参照されている同期ブロックにも同じように反映されます。
厳密には非同期ブロックの実態は一つで、他からはそれが参照される形になります。
プログラミング言語的な発想でいうと非同期ブロックが値
で、
同期ブロックが参照値
やポインタ
的なものです。
どういう仕組み?
同期ブロック/非同期ブロックは投稿タイプ名が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を保有しています。
ref
の757
は追加した同期ブロックの投稿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
を準備してBlockPatternsTab
のonInsert
に渡します。
<BlockPatternsTab
rootClientId={ destinationRootClientId }
onInsert={ onInsertPattern }
onSelectCategory={ onClickPatternCategory }
selectedCategory={ selectedPatternCategory }
/>
BlockPatternsTab
はPatternCategoryPreviews
をホストしますが、
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 }
/>
マイパターン
の場合はこのコールバックに仕掛けがあります。
このコールバック、onClickPattern
はusePatternsState()
の戻り値です。
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でプロバイダが呼ばれる。
- プロバイダではエディタの設定情報(
blockEditorSettings
)が取得される
- この関数はエディタの初期設定を返す。この時、再利用ブロックはgetEntityRecords('postType', 'wp_block', ... ) から取得される。
ではカテゴリがレンダリングされる際にどうやって一覧を取得しているかです。
BlockPatternsTab
内ではカテゴリ一覧を取得するため以下のコードがあります。
ちょっと複雑です。
const categories = usePatternCategories( rootClientId );
block-editor/src/components/inserter/block-patterns-tab/use-pattern-categories.js
この関数は、もしパターンにid
を持つオブジェクトがあれば(つまり同期ブロック/非同期ブロックが一つ以上あれば)、カテゴリにマイパターン
が追加される仕組みになってます。
他のタブ
通常のブロック追加のタブは以下のコードに書かれてます。