概要
複数の投稿の内容を一括で置換する際、ブロックを使ってみた。
背景
千本を超える投稿の数百に対して編集をかける際、これまでは正規表現でコンテンツ (post_contents) を検索・置換 (preg_match_all) していましたが、ブロック単位で実施するやり方を試してみました。
内容
例として、複数の投稿に存在している次に示すような段落ブロック
(参考文献)
(1) ~
を、次に示す見出しブロックと段落ブロックに変えました。(参考文献)だけでなく〈参考文献〉としている箇所もあるのでまとめて。
参考文献
(1) ~
これはHTMLレベルでいえば、次の段落ブロック
<!-- wp:paragraph {"className":"lf_postscript"} -->
<p class="lf_postscript">(参考文献)<br>(1) ~</p>
<!-- /wp:paragraph -->
を次のように変えることです。
<!-- wp:heading {"level":4,"className":"lf_refernces"} -->
<h4 class="lf_refernces">参考文献</h4>
<!-- /wp:heading -->
<!-- wp:paragraph {"className":"lf_postscript"} -->
<p class="lf_postscript">(1) ~</p>
<!-- /wp:paragraph -->
だいたい次のようなコードでいけました(クエリのパラメーターやエラー処理は省略して引用)。
$q = new WP_Query( array(
'post_type' => 'post',
'post_status' => 'publish',
's' => '参考文献',
'posts_per_page' => -1,
) );
$block_h4 = array(
array(
'blockName' => 'core/heading',
'attrs' => array(
'level' => 4,
'className' => 'lf_refernces',
),
'innerBlocks' => array(),
'innerHTML' => '',
'innerContent' => array(
PHP_EOL.'<h4 class="lf_refernces">参考文献</h4>'.PHP_EOL
),
),
array(
'blockName' => null,
'attrs' => array(),
'innerBlocks' => array(),
'innerHTML' => '',
'innerContent' => array( PHP_EOL.PHP_EOL ),
),
);
$pattern = '#((|〈)参考文献()|〉)\<br\>(.+)#';
foreach ( $q->posts as $p ) {
$blocks = parse_blocks( $p->post_content );
for ( $i = 0; $i < count( $blocks ); $i++ ) {
if ( 'core/paragraph' == $blocks[$i]['blockName'] ) {
if ( 1 == preg_match( $pattern, $blocks[$i]['innerContent'][0], $matches ) ) {
$blocks[$i]['attrs']['className'] = 'lf_postscript';
$blocks[$i]['innerContent'][0] = PHP_EOL.'<p class="lf_postscript">'.$matches[3].PHP_EOL;
array_splice( $blocks, $i, 0, $block_h4 );
$content = serialize_blocks( $blocks );
$mypost = array(
'ID' => $p->ID,
'post_content' => $content,
);
wp_update_post( $mypost );
break;
}
}
}
}
L1-6: 編集対象となる投稿をざっくり抜き出します。
L8-28: 挿入するブロック。ブロックを post_content に格納するために HTML に変換する serialize_block() を見ると、blockName, attrs, innerContent しか使っていないので他の要素は要らないのですが、一応。
L12: level は文字 ‘4’ でなく 数字 4 でなければなりません(ブロックエディターで読み込むとエラーが出ます)。
L18: ブロック要素の前後に改行を入れておきます(標準でそのようになっているため)。
L21-27: なぜか標準ではブロック間に空ブロックが挟まっているので踏襲しました。
L22: blockName は ‘’ でなく null でなければなりません。空ブロックかどうかを、serialize_block() で is_null() で判定しているため。
L30: 置換対象となる文字列。(参考文献)と〈参考文献〉を同時に置換するため、結局正規表現を使いました。
L32-52: 候補の投稿を順次処理していきます。
L33: 投稿をブロック化します。
L35-51: ブロックを順次処理していきます。ブロック自体を書き換えるので foreach() でなく for() を選択。
L37: 段落ブロックなら……
L38: 段落が、L30 で指定したパターンだったら……
L39-40: その段落を書き換えます。
L41: その段落ブロックの直前に、L8-28 で用意した見出しブロックを挿入します。
L42: ブロックを HTML 化します。
L43-47: その HTML で投稿を更新します。
L48: 次の候補投稿へ。
従来やっていた、投稿コンテンツ全体を正規表現検索・置換するよりはミスが少なそうな気がします。手間はあまり変わらないかな。