WordPress

Gutenberg インナーブロック 発展編

先に作ったサンプルのブロック―フレームワークに関する説明リストのブロックを元にインナーブロックのTips的なカスタマイズ、活用を紹介します。

参照: Gutenbergのブロックを作る – Autumnsky

ブロック追加時にインナーブロックを予め入れておく

ブロック挿入時にインナーブロックを入れておく

InnerBlocks の template プロパティを設定する。普通の説明リストなら、常に dt ブロックと dd ブロックを組で挿入する等もできる。

src/index.js

edit: ( { className } ) => {
    return (
        <dl className={ className }>
            <InnerBlocks
                allowedBlocks={ [ 'framework-dl/description-block' ] }
                template={ [ [ 'framework-dl/description-block', {} ] ] }
            />
        </dl>
    );
},

template プロパティには配列を渡す。配列は[ ブロック名, 引数オブジェクト ]の配列を1つ以上含める。

これで、flamework 説明ブロックを挿入した時に、description ブロックが一つ入ります。

追加ボタンをカスタマイズする

インナーブロックの追加は Add ボタンに変えてみた。

renderAppenderプロパティを設定すると、Gutenberg デフォルトのbutton-block-appender コンポーネントを上書きして、任意のタグと動作を指定できる。
ただ、このプロパティはハンドブックなどに記載がみつかりませんでした。

edit: ( { className } ) => {
    return (
        <dl className={ className }>
            <InnerBlocks
                allowedBlocks={ [ 'framework-dl/description-block' ] }
                renderAppender={ 
                    // JSXを返す関数 //
                }
	    />
        </dl>
    );
},

JSX を返す無名関数を実装。

renderAppender={ () => (
	<button
		type="button"
		onClick={
                     // ブロックを追加する関数 //
                }
        >
		{ __( 'Add', 'framework-dl-block' ) }
	</button>
	)
}

クリック時の動作も自分で設定する必要があるので、ブロック追加の関数を改めて実装する。ということで、onClickメソッドは下記の通り。

onClick={ () => {
    dispatch('core/block-editor').insertBlocks(
        createBlock('framework-dl/description-block'), 9999, clientId
    ) 
} }

dispatch オブジェクトは@wordperss/dataから、createBlock 関数は@wordpress/blocksからインポートしておく。

import { registerBlockType, createBlock } from '@wordpress/blocks';
import { InnerBlocks } from '@wordpress/block-editor';
import { dispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

それとclientIdという実行時に割り振られるプロパティが要るが、特に宣言なしで呼び出せるので、editメソッドの引数として渡しておく。

editメソッド全体は下記の通り。

edit: ( { className, clientId } ) => {
	return (
		<dl className={ className }>
			<InnerBlocks
				allowedBlocks={ [ 'framework-dl/description-block' ] }
				template={ [ [ 'framework-dl/description-block', {} ] ] }
				renderAppender={ () => (
					<button
						type="button"
						onClick={ () => { 
							dispatch('core/block-editor').insertBlocks(
								createBlock('framework-dl/description-block'), 9999, clientId)
						} }
					>
						{ __( 'Add', 'framework-dl-block' ) }
					</button>
				) }
			/>
		</dl>
	);
},

親ブロックでインナーブロックの値を利用する

インナーブロックの値を取得するにはwithSelect を使います。
例えば、インナーブロックの登場年を取得して { minYear }~{ maxYear }年 という表示をすることもできる。

witheSelect( 関数1 )( 関数2 ) のように書く。関数1にはデータを取得する関数、関数2にレンダー関数などを渡す。withSelect自身は高階関数のように動作するとのことです。withSelect@wordperss/dataからインポートします。

editメソッドにするとこうなる

edit: withSelect( ( select, blockData ) => {
		return {
			innerBlockProps: select( 'core/block-editor' ).getBlocks( blockData.clientId ),
		};
	} )( ( { className, clientId, innerBlockProps } ) => {
		return (
			<dl className={ className }>
                           …

関数1、関数2ともに無名関数として実装。関数1でのデータ取得結果は、関数2の引数として利用できる。上記のコードでは、innerBlockProps プロパティとした。

このinnerBlockPropsの中身は配列になっており、インナーブロックの数だけオブジェクトが格納されている。オブジェクトの中身はインナーブロックの内容である。

(2) […]
    0: {…}
​​        attributes: {…}
​​​            description: "Framework1 description."
​​​            language: ""
​​​            name: "Framework1"
​​​            since: "2011"
​​​            <prototype>: Object { … }
​​        clientId: "60bcf62c-034f-45dd-82ba-f6b1b98c217c"
​        innerBlocks: Array []
​​        isValid: true
​​        name: "framework-dl/description-block"
​​        originalContent: "<dt>Framework1</dt><div class=\"flex\"><dd><span class=\"since\">2011</span>年~</dd><dd>言語:<span class=\"language\"></span></dd></div><dd class=\"description\">Framework1 description.</dd>"
​       validationIssues: Array []
​​       <prototype>: Object { … }

​    1: ​{…}
​​​        attributes: {…}
​​​        clientId: "60bcf62c-034f-45dd-82ba-f6b1b98c217c"
​​        innerBlocks: Array []
​​        isValid: true
​             …
length: 2
​<prototype>: Array []

単純にエディター内で表示するだけなら、これでもいい。

…
} )( ( { className, clientId, innerBlockProps } ) => {
    const years = innerBlockProps.map( prop => prop.attributes.since );
    return (
          <>
          <p>{ Math.min( ...years ) }~{ Math.max( ...years ) }年</p>
          <dl className={ className }>
              …
もちろんリアクティブ

実用的にはattributesに格納して、saveメソッドでも使えるようにしておく。

attributes: {
	minYear: {
		type: 'string',
	},
	maxYear: {
		type: 'string',
	},
},

edit: withSelect( ( select, blockData ) => {
	return {
		innerBlockProps: select( 'core/block-editor' ).getBlocks( blockData.clientId ),
	};
} )( ( { className, clientId, innerBlockProps, attributes, setAttributes } ) => {
	const years = innerBlockProps.map( prop => prop.attributes.since );
	setAttributes( { minYear: Math.min( ...years ) } );
	setAttributes( { maxYear: Math.max( ...years ) } );
	return (
		<>
		{ attributes.minYear !== attributes.maxYear &&
			<p>{ attributes.minYear }~{ attributes.maxYear }年</p>
		}
		<dl className={ className }>
			<InnerBlocks
				... 中略 ...
		</dl>
		</>
	);
} ),

save: ( { attributes } ) => {
	return (
	<div>
		{ attributes.minYear !== attributes.maxYear &&
			<p>{ attributes.minYear }~{ attributes.maxYear }年</p>
		}
		<dl>
			<InnerBlocks.Content />
		</dl>
	</div>
	);
},

インナーブロックが一つしかないとか、同じ年ばかりだと、2010~2010年 と表示されて不自然なので、条件判定をいれた。

{ attributes.minYear !== attributes.maxYear &&
	<p>{ attributes.minYear }~{ attributes.maxYear }年</p>
}

withSelectに関しては、文法的なところも今ひとつ理解できていないし、ハンドブックにもEditメソッドでの使い方の記載がないんですが、Gutenberg のイッシューで「Edit に渡すなら withSelect を使う」と返答があって、実際これで期待通り動きます。
How to know if an InnerBlock is selected · Issue #22282 · WordPress/gutenberg

withSelect関数自身は、内部的にはReactのフックを呼んでいるらしい。
参考: Introducing useDispatch and useSelect – Make WordPress Core

ブロック内部のデータを取得するだけなら、useSelect関数を使うのがよいそうです。

全部入りコード

ここまでのTipsを全て組み込むと、コードはこの通り。
@wordpress/create-block でインストールしたリンターに基づいて、コーディングスタイルの整形をかけてあります。

import { registerBlockType, createBlock } from '@wordpress/blocks';
import { InnerBlocks } from '@wordpress/block-editor';
import { dispatch, withSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

import './editor.scss';
import './style.scss';

import './framework-dd-block.js';

registerBlockType( 'framework-dl/framework-dl-block', {
	title: __( 'Framework description list', 'framework-dl-block' ),

	description: __(
		'Example block written with ESNext standard and JSX support – build step required.',
		'framework-dl-block'
	),

	category: 'widgets',

	icon: 'feedback',

	attributes: {
		minYear: {
			type: 'string',
		},
		maxYear: {
			type: 'string',
		},
	},

	edit: withSelect( ( select, blockData ) => {
		return {
			innerBlockProps: select( 'core/block-editor' ).getBlocks(
				blockData.clientId
			),
		};
	} )(
		( {
			className,
			clientId,
			innerBlockProps,
			attributes,
			setAttributes,
		} ) => {
			const years = innerBlockProps.map(
				( prop ) => prop.attributes.since
			);
			setAttributes( { minYear: Math.min( ...years ) } );
			setAttributes( { maxYear: Math.max( ...years ) } );

			return (
				<>
					{ attributes.minYear !== attributes.maxYear && (
						<p>
							{ attributes.minYear }~{ attributes.maxYear }年
						</p>
					) }
					<dl className={ className }>
						<InnerBlocks
							allowedBlocks={ [
								'framework-dl/description-block',
							] }
							template={ [
								[ 'framework-dl/description-block', {} ],
							] }
							renderAppender={ () => (
								<button
									type="button"
									onClick={ () => {
										dispatch(
											'core/block-editor'
										).insertBlocks(
											createBlock(
												'framework-dl/description-block'
											),
											9999,
											clientId
										);
									} }
								>
									{ __( 'Add', 'framework-dl-block' ) }
								</button>
							) }
						/>
					</dl>
				</>
			);
		}
	),

	save: ( { attributes } ) => {
		return (
			<div>
				{ attributes.minYear !== attributes.maxYear && (
					<p>
						{ attributes.minYear }~{ attributes.maxYear }年
					</p>
				) }
				<dl>
					<InnerBlocks.Content />
				</dl>
			</div>
		);
	},
} );

Gutenbergのブロックを作る

目次

  1. ブロック作成の概説
    1. 必要知識とスキル
    2. 作例ブロック
  2. 開発環境の用意
    1. ビルドしてみる
  3. ブロック本体 index.js の調整
  4. InnerBlockのjsファイルを作成・登録
    1. InnerBlock用 jsファイルの作成
    2. InnerBlockを使えるようにする
  5. InnerBlockの実装
    1. edit メソッドにコンポーネントを登録
    2. attributes を設定
    3. HTMLでの保存をSaveメソッドに実装
  6. index.js の edit/save メソッド
  7. スタイルの調整
  8. 完成

1. ブロック作成の概説

ブロックの作成には、WordPress用のnpmパッケージである@wordpress/create-blockを利用します。

Create Block は公式でサポートされるブロック作成方法です。WordPress プラグインを使用してブロックを登録します。Create Block はモダンなビルド設定を提供します。構成は必要ありません。PHP、JS、CSS コード、その他、プロジェクトの開始に必要なすべてのファイルを生成します。

@wordpress/create-block – Japanese Team — WordPress.org

npx コマンドを実行すると、プラグインのPHPファイル、ブロック用jsファイルの雛形、scssファイルなど、Gutenbergブロック用のファイルと、ビルド環境が一式用意されます。jsはECMAScript2015( ES6 / ESNext )で書いていきます。

1.1 必要知識とスキル

ECMAScript2015 と JSX で書いて Babel でビルドしますが、ビルド環境は公式スクリプトが自動で構築してくれます。npm runができれば大体、大丈夫でしょう。JavaScript と JSX は基本的な構文や仕組みが分かっていれば問題ないかと思います。npm も package.json が読めてnpmコマンドが使えれば十分でしょう。

他には、プラグインのPHPでブロック用js/CSSを読み込ませますので、WordPressにおけるスクリプトのエンキュー方法など、確認しておくといいかと思います。 wp_register_script() | WordPress Developer Resources

ローカルでのWordPress実行環境は今回 wp-env を利用しました。ローカル実行環境は各自でご用意下さい。

1.2 作例ブロック

このようなフレームワークに関する定義リストのブロックを作ってみたいと思います。イメージとしてはACFの繰り返しフィールドに近いものです。

左図がGutenbergエディタでの表示と入力のモックアップで、右が表示のモックです。

gutenberg-block
block-page-view

フレームワーク名をdt要素、登場年、言語、説明をそれぞれ3つのdd要素とします。このdt×1・dd×3を一組として、一つの定義リスト(dl要素)内に複数のフレームワークの説明を入力し、表示します。

InnerBlockとGutenbergコンポーネントを組み合わせて作っていきます。

2. 開発環境の用意

適当なフォルダで、npx @wordpress/create-block で対話モードとして実行し、スラッグと名前空間、ブロックのタイトルを指定しました。各項目で特に指定がない場合は、何も入力せずenterでデフォルト値が使われます。「The short description for your block」やライセンス項目などは今回、特に指定していません。

下記のように、最低限スラッグだけ指定すればいいのですが、ブロックの識別子がcreate-block/となってしまう上に、後から識別子を変更するのは結構手間がかかるため、最初に指定しました。

npx @wordpress/create-block framework-dl-block

セットアップが完了すると、このように表示されます。

npxを実行したフォルダ内に、framework-dl-blockというスラッグ名のフォルダが生成され、フォルダ内にはプラグインとして使えるようにファイル一式が入っています。

生成されるファイルは下記の通りです。
editorconfig や gitignore も揃っているので、このままgit init / git commit -am "initial commit"してGitでのトラッキングを始められます。

.editorconfig
.gitignore
block.json
framework-dl-block.php
package-lock.json
package.json
readme.txt
build
|       index.asset.php
|       index.css
|       index.js
|       style-index.css
|               
src
|       edit.js
|       editor.scss
|       index.js
|       save.js
|       style.scss
|       
node_modules

ビルドコマンドを叩くと、src 内の jsファイルが babel によってビルドされてbuid フォルダに書き出されます。build/index.js ファイルを framework-dl-block.php 内のwp_register_script()関数がエンキューすることで、Gutenberg内でオリジナルのブロックが使えるようになります。

ビルドしてみる

@wordpress/eslint-pluginとしてeslintもインストールされていますので、必要なら.eslintrcをプロジェクトルートに配置します。

一度ビルドしてみます。

npm run build

srcのjs、scssがビルドされてbuildフォルダに出力されます。

セットアップ完了後に表示される、最初のコマンドnpm startを試します。

npm start

こちらはファイル監視をして、変更が保存されると自動でビルドします。eslintで文法チェックもされているので、jsの文法が間違っているとエラーメッセージが出力され、ビルドは行われません。

「return文が関数の外側である」という構文エラー

終了する時は、ctrl-cです。

コーディングスタイルに準拠するよう自動整形も利用できます。

npm run format:js
Gutenbergのコーディングスタイルに合わせるよう自動整形してくれる

3. ブロック本体 index.js の調整

index.jsではregisterBlockType関数に、ブロックの識別子とオブジェクトが渡されています。オブジェクト内は、各種設定・editメソッド・saveメソッドなど、Gutenbergのブロックとして必要な機能や設定が記述されています。edit/saveでのHTML出力はJSXで書かれています。
参照: ブロックの登録 – WordPress.org / JSX の導入 – React

npx @wordpress/create-blockで生成されたindex.jsは、そのままだと今回は使いにくいので調整します。

コメントは全削除。プロパティやコンポーネントの説明と参照URLが書いてありますが、コードの流れが追いにくくなるため削除します。説明とURLは目を通しておいて下さい。
edit.js/save.js も削除して import 文も削除。分割するほどの行数ではなく、InnerBlock の js にも edit / save のメソッドを実装するため、ファイル構成が冗長になってしまいますので、今回は削除。
多言語対応の __関数のインポートも削ってしまってもいいのですが、今回は残してます。

index.jsは一旦、このようになります。

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import './style.scss';

registerBlockType( 'framework-dl/framework-dl-block', {
	title: __( 'Framework description list', 'framework-dl-block' ),
	description: __(
		'Example block written with ESNext standard and JSX support – build step required.',
		'framework-dl-block'
	),
	category: 'widgets',
	icon: 'smiley',
	supports: {
		// Removes support for an HTML mode.
		html: false,
	},
	edit: ( { className } ) => {
		return (
			<p className={ className }>
				{ __(
					'Framework description list',
					'framework-dl-block'
				) }
			</p>
		);
	},
	save: () => {
		return null;
	},
} );

この状態で、ビルドを通せば最低限のブロックとして動作します。

4. InnerBlockのjsを作成、登録

4.1 InnerBlock用 jsファイルの作成

InnerBlockとして、もう一つ、ブロックのjsファイルを作ります。

src/framework-dd-block.js

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import './style.scss';

registerBlockType( 'framework-dl/description-block', {
    title: __( 'Framework description ', 'framework-dl-block' ),
    description: __( 'Framework descriptions block', 'framework-dl-block' ),
    category: 'widgets',
    parent: [ 'framework-dl/framework-dl-block' ],
    supports: {
        // Removes support for an HTML mode.
        html: false,
    },
    edit: ( { className } ) => {
        return (
            <p className={ className }>
                { __(
                    'Framework description block, this is inner block!',
                    'framework-dl-block'
                ) }
            </p>
        );
    },
    save: () => {
        return null;
    },
} );

index.jsファイルをコピーして、framework-dd-block.js にリネームして中身を書き換えました。
変更点は主に二つ。ブロック名と parent プロパティです。

registerBlockType( 'framework-dl/description-block', {

index.jsで登録するdlブロックと被らないよう、第1引数であるブロックの一意識別子を変えておきます。index.jsにてInnerBlockとして許可するブロックを指定する際に、この識別子を利用します。ファイル名と識別子は一致していなくても構いません。

また、下記のように「親」ブロックを指定することで、ページ内に自由に挿入できなくなります。InnerBlockとしてのみ使用するので、今回は「親」ブロックを設定して、framework-dl-block 以外では description-block を使えなくします。

parent: [ 'framework-dl/framework-dl-block' ],

この時点での、srcフォルダ内のファイルは下記の通りです。

src
        framework-dd-block.js
        index.js
        style.scss

4.2 InnerBlockを使えるようにする

先に作ったframework-dd-block.jsをInnerBlockとして使えるように、index.jsのedit/saveメソッドを書き換えます。
参照:ネストしたブロック: InnerBlocks の使用 – WordPress.org

src/index.js – import 文

import { registerBlockType } from '@wordpress/blocks';
import { InnerBlocks } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';
import './style.scss';
import './framework-dd-block.js';

InnerBlocksと、先に作った インナーブロック用jsファイルをインポートします。framework-dd-block.jsのregisterBlockType()が実行されればいいので、変数に代入しません。そもそもframework-dd-block.jsにはexport宣言がないので、何も代入できません。

src/index.js – editメソッド

edit: ( { className } ) => {
    return (
        <dl className={ className }>
            <InnerBlocks
                allowedBlocks={ [ 'framework-dl/description-block' ] }
            />
        </dl>
    );
},

<InnerBlocks>タグのプロパティで、許可するブロックを指定します。allowedBlocksには framework-dd-block.js で登録したブロックの識別子を入れます。一種類しか許可しない場合でも、配列でないと allowedBlocksプロパティに渡せないので配列に入れて代入します。

saveメソッドも、InnerBlocksの内容を保存するよう実装します。

src/index.js – saveメソッド

save: () => {
    return <InnerBlocks.Content />;
},

この時点で、Gutenbergエディタでは「Framework description list」ブロック内に、 「Framework description block, this is inner block! 」というテキストのブロックを複数、挿入できるようになっているはずです。

Gutenbergのエディターモードを切り替えてコードエディターモードにすると、ブロックの開始と終了を表すHTMLコメントのみが保存されていると思います。

<!-- wp:framework-dl/framework-dl-block -->
<!-- wp:framework-dl/description-block /-->

<!-- wp:framework-dl/description-block /-->

<!-- wp:framework-dl/description-block /-->
<!-- /wp:framework-dl/framework-dl-block -->

5. InnerBlockの実装

5.1 edit メソッドにコンポーネントを登録

Gutenbergエディタでブロック内に表示するフォームは、Gutenbergのコンポーネントを使って実装していきます。入力フォームは下図のようにControlコンポーネントを利用します。
参照: Component Reference | Block Editor Handbook | WordPress Developer Resources

使うコンポーネントは、

framework-dd-block.jsにて@wordpress/componentsモジュールから上記コンポーネントをインポートするよう宣言を追記しておきます。

framework-dd-block.js

import {
	Flex,
	TextControl,
	__experimentalNumberControl as NumberControl,
	SelectControl,
	TextareaControl,
} from '@wordpress/components';

登場年を入力する NumberControl は WordPress5.5のGutenbergでは実験的な実装となっているため、__experimentalNumberControlと長いエクスポート名になっています。NumberControl変数として扱えるようにエイリアスを使ってインポートします。

Flex コンポーネントは、タグで囲うだけでフレックスボックスにしてくれます。エディターでのレイアウトを組むのに便利なので、合わせてインポートしています。(editor-style.cssでフレックスボックスを指定することもできます)

続いてframework-dd-block.jsのeditメソッドにJSXでコンポーネントを記述します。

edit: ( { attributes, setAttributes } ) => {
	const { name, since, language, description } = attributes;
	return (
		<>
		<TextControl
			placeholder="Framework name"
			value={ name }
			onChange={ ( value ) => setAttributes( { name: value } ) }
		/>
		<Flex>
			<NumberControl
				label="登場年"
				shiftStep={ 1 }
				value={ since }
				onChange={ ( value ) =>
						setAttributes( { since: value } )
				}
			/>
			<SelectControl
				label="言語"
				options={ [
					{ label: 'PHP', value: 'PHP' },
					{ label: 'JavaScript', value: 'JavaScript' },
					{ label: 'Ruby', value: 'Ruby' },
					{ label: 'Erlang', value: 'Erlang' },
					] }
				value={ language }
				onChange={ ( value ) =>
						setAttributes( { language: value } )
				}
			/>
		</Flex>
		<TextareaControl
			placeholder="概要"
			value={ description }
			onChange={ ( value ) =>
				setAttributes( { description: value } )
				}
		/>
		</>
	);
},

attributessetAttributes はブロックへの入力内容を保持するために必要なオブジェクト、関数です。editメソッド内で利用するため、引数として渡しておきます。グローバル変数のような感じで、特に宣言なく利用できます。attributesについて詳しくは次節で説明します。

HTML要素に規定されている属性に対しても、placeholder="概要"のようにプロパティを設定できます。

valueに代入した内容が、各input要素の内容としてGutenbergエディタで表示されます。

onChangeには変更を検知した際の動作を書きます。今回はvalueattributesプロパティに代入しています。

余談:setAttributesの他にも、特に宣言なくregisterBlockType関数で利用できる変数がいくつかありますが、公式ハンドブックにも解説がない場合があります。気づいたらイシューを立ててみたり、ドキュメントを書いてみるといいでしょう。
Project Overview | Block Editor Handbook | WordPress Developer Resources
gutenberg/docs at master · WordPress/gutenberg · GitHub

5.2 attributesを設定

ブロック内で入力した内容はattributesプロパティを利用して保存、読み出しを行います。

attributes プロパティはすべての利用可能な属性と対応する値を表します。属性はブロックタイプ登録の際に attributes プロパティで記述されます。属性ソースを指定する方法については属性のドキュメントを参照してください。

edit と save – Japanese Team — WordPress.org

registerBlockType関数の第2引数のオブジェクトにattributesプロパティを追記します。attributesのプロパティ(=属性)には、保持したい値についての設定を記述していきます。

Framework dl blockでは、下表のように4種類の属性を保持します。

入力内容使用コンポーネント保存先
フレームワーク名TextControlattributes.name
登場年NumberControlattributes.since
言語SelectControlattributes.language
概要TextareaControlattributes.description

完成形ではHTMLから読み出してattributesにセットするため、年を表すsince属性もintegerでなく、stringとします。

parent: [ 'framework-dl/framework-dl-block' ],
supports: {
	// Removes support for an HTML mode.
	html: false,
},

attributes: {
	name: {
		type: 'string',
	},
	since: {
		type: 'string',
		default: 2010,
	},
	language: {
		type: 'string',
	},
	description: {
		type: 'string',
	},
},

edit: ( { attributes, setAttributes } ) => {

attributesで属性を設定したので、onChangesetAttributesを実行すると、入力内容がattributesに反映されます。

onChangeイベント時に、value と attributes を console.log に出力するとこのようになります。sinceプロパティはデフォルト値の2010が格納されています。

<TextControl
	placeholder="Framework name"
	value={ name }
	onChange={ ( value ) => {
		console.log( 'TextControl value: ' + value );
		setAttributes( { name: value } );
		console.log( attributes );
		}
	}
/>
name: framewor となっていて、保存完了までは若干ラグがある

5.3 HTMLでの保存をSaveメソッドに実装

saveメソッドがnullを返しているので、HTMLで保存するように実装します。

下記のようにブロックで入力したとして、

input-sample

現時点では入力内容はJSON形式で、ブロックの開始、終了を表すHTMLコメント内に保持されます。Gutenbergをコードエディターモードに切り替えれば実際の保存内容が確認できます。このままではHTMLが保存されないためプレビューや投稿ページには何も表示されませんので、saveメソッドでJSXを記述しHTMLでDBに保存するよう実装します。

逆にnullのままにしておけば、HTML構造に依存しない形でブロックの内容をDBに保存できます。このようにJSONで保存した場合は、HTMLを出力するレンダー関数を別途PHPにて実装する必要があります。

<!-- wp:framework-dl/framework-dl-block -->
<!-- wp:framework-dl/description-block {"name":"framework","since":2016,"language":"JavaScript","description":"A framework description.\nSomthing framework distinction."} /-->

<!-- wp:framework-dl/description-block {"name":"framework 2","since":2009,"description":"A framework description.\nSomthing framework distinction."} /-->
<!-- /wp:framework-dl/framework-dl-block -->

今回は、HTMLで保存するため、saveメソッドは下記のようになります。

save: ( { attributes } ) => {
	return (
	<>
		<dt>{ attributes.name }</dt>
		<dd>
			<span className="since">{ attributes.since }</span>年~
		</dd>
		<dd>
			言語:<span className="language">{ attributes.language }</span>
		</dd>
		<dd className="description">{ attributes.description }</dd>
	</>
	);
},

dt dd要素として表示したいフレームワーク名や言語、説明は、attributesに保持されているので、引数としてattributesを渡します。editメソッドのように、複数の変数へ分割代入せずに、直接attributes.nameなどで値を呼び出しています。

登場年と言語に関しては、attributes.since / attributes.languagespanタグで括っています。下書きや公開状態の投稿をGutenbergエディターを開いて編集する場合、GutenbergはDBを読み込んでattributesに値をセットして表示する仕組みになっています。

そのために、保存したHTMLのどこからname / sinceなどの値を読み取るかの指定をattributesプロパティに追記します。

attributes: {
	name: {
		type: 'string',
		source: 'text',
		selector: 'dt',
	},
	since: {
		type: 'string',
		default: '2010',
		source: 'text',
		selector: 'span.since',
	},
	language: {
		type: 'string',
		source: 'text',
		selector: 'span.language',
	},
	description: {
		type: 'string',
		source: 'text',
		selector: 'dd.description',
	},
},

SelectorプロパティでHTMLを指定します、jQueryでDOMを取得する感じです。
Sourceプロパティでは取得したHTML全体を使うか、内部テキストを使うか、などを指定します。
defaultでデフォルト値を入れておくこともできます。
属性 – Japanese Team — WordPress.org

例えばdescription属性は、descriptionクラスのddタグから内部テキストを取得する設定となっています。

ブロックが表示される時には、このsource / selectorの設定に基づいてDBのHTMLからパースした値をattributesにセットし、editメソッドではコンポーネントのvalueattributes内の値を代入します。このようにして、DBに保存している内容をGutenbergで表示しています。

実際にどういうタイミングで読取りや受け渡しが行われるかは、Reactコンポーネントのライフサイクルとかに基づいていると思いますが、詳しくは調べてません。 state とライフサイクル – React

InnerBlockのjsはこれで完成で、最終的なコードはこうなります。
src/framework-dd-block.js

import { registerBlockType } from '@wordpress/blocks';
import {
	Flex,
	TextControl,
	__experimentalNumberControl as NumberControl,
	SelectControl,
	TextareaControl,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';

import './style.scss';

registerBlockType( 'framework-dl/description-block', {
	title: __( 'Framework description ', 'framework-dl-block' ),

	description: __( 'Framework descriptions block', 'framework-dl-block' ),

	category: 'widgets',

	parent: [ 'framework-dl/framework-dl-block' ],
	supports: {
		// Removes support for an HTML mode.
		html: false,
	},

	attributes: {
		name: {
			type: 'string',
			source: 'text',
			selector: 'dt',
		},
		since: {
			type: 'string',
			default: 2010,
			source: 'text',
			selector: 'span.since',
		},
		language: {
			type: 'string',
			source: 'text',
			selector: 'span.language',
			default: '',
		},
		description: {
			type: 'string',
			source: 'text',
			selector: 'dd.description',
		},
	},

	edit: ( { attributes, setAttributes } ) => {
		const { name, since, language, description } = attributes;
		return (
			<>
				<TextControl
					placeholder="Framework name"
					value={ name }
					onChange={ ( value ) => setAttributes( { name: value } ) }
				/>
				<Flex>
					<NumberControl
						label="登場年"
						shiftStep={ 1 }
						value={ since }
						onChange={ ( value ) =>
							setAttributes( { since: value } )
						}
					/>
					<SelectControl
						label="言語"
						options={ [
							{ label: 'PHP', value: 'PHP' },
							{ label: 'JavaScript', value: 'JavaScript' },
							{ label: 'Ruby', value: 'Ruby' },
							{ label: 'Erlang', value: 'Erlang' },
						] }
						value={ language }
						onChange={ ( value ) =>
							setAttributes( { language: value } )
						}
					/>
				</Flex>

				<TextareaControl
					placeholder="概要"
					value={ description }
					onChange={ ( value ) =>
						setAttributes( { description: value } )
					}
				/>
			</>
		);
	},

	save: ( { attributes } ) => {
		return (
			<>
				<dt>{ attributes.name }</dt>
				<dd>
					<span className="since">{ attributes.since }</span>年~
				</dd>
				<dd>
					言語:
					<span className="language">{ attributes.language }</span>
				</dd>
				<dd className="description">{ attributes.description }</dd>
			</>
		);
	},
} );

6. index.js の edit/save メソッド

iconを変更して、edit/saveメソッドはdlタグを返すよう実装します。

icon: 'feedback',

edit: () => {
	return (
		<InnerBlocks
			allowedBlocks={ [ 'framework-dl/description-block' ] }
		/>
	);
},

save: () => {
	return (
		<dl>
			<InnerBlocks.Content />
		</dl>
	);
},

Gutenbergでのスタイルを調整したいのでクラス名を付与したいのですが、saveメソッドで返された最上位のHTML(ここではdl要素)には、自動的にclassNameが付与されます。よって、特に指定はなし。
editメソッドではルート要素(returnされる最上位の要素)が見た目と一致しない場合があるため、明示的にclassNameを指定する必要があるそうです。

実際にWordPressが投稿を表示する際にthe_content()関数で出力するHTMLはこのようになります。

<dl class="wp-block-framework-dl-framework-dl-block">
  <dt>framework</dt>
  <dd><span class="since">2007</span>年~</dd>
  <dd>言語:<span class="language">JavaScript</span></dd>
  <dd class="description">A framework description. Something framework distinction.</dd>
</dl>

参照: Edit and Save | Block Editor Handbook | WordPress Developer Resources

デフォルトで付与されるclassNameは、registerBlockType関数に渡された一意識別子から生成されるようです。
framework-dl/framework-dl-block → wp-block-framework-dl-framework-dl-block

7.スタイルの調整

ブロックに対しクラス名が付与されているので、スタイルを当てます。

scssファイルが既に用意されていて、background-colorpadding が指定されているので、追記していきます。

src/style.scss

.wp-block-framework-dl-framework-dl-block {
	background-color: none;
	padding: 2px;

	dt {
		border-bottom: solid 1px black;
	}
	dd, dt {
		margin-left: 0;
	}
}

src/index.jsで、このscssファイルがインポートされているので一緒にビルドされます。

import './style.scss';

また、ビルド済みのCSSファイルは build/style-index.css で、framework-dl-block.phpにてエンキューの登録がされています。

$style_css = 'build/style-index.css';
wp_register_style(
	'framework-dl-framework-dl-block-block',
	plugins_url( $style_css, __FILE__ ),
	array(),
	filemtime( "$dir/$style_css" )
);

完成

framework-dl-block-post

コードエディターモードにすると、下記のようなHTMLでDBに保存されていることが分かります。

<!-- wp:framework-dl/framework-dl-block -->
<dl class="wp-block-framework-dl-framework-dl-block"><!-- wp:framework-dl/description-block -->
<dt>CakePHP</dt>
<div class="flex"><dd><span class="since">2006</span>年~</dd><dd>言語:<span class="language">PHP</span></dd></div>
<dd class="description">Ruby on Railsの概念を多く取り込んだWEBアプリケーションフレームワーク。PHPでは老舗。ビューが柔軟に構築できる。</dd>
<!-- /wp:framework-dl/description-block -->

<!-- wp:framework-dl/description-block -->
<dt>Ruby on Rails</dt>
<div class="flex"><dd><span class="since">2005</span>年~</dd><dd>言語:<span class="language">Ruby</span></dd></div>
<dd class="description">WEBアプリケーションフレームワークとしては古くかつ有名。MVC、スキャフォルド、設定より規約などの特徴を持ち、DRYであることを基本理念としている。2020年現在も広く使われている。</dd>
<!-- /wp:framework-dl/description-block -->

<!-- wp:framework-dl/description-block -->
<dt>Vue.js</dt>
<div class="flex"><dd><span class="since">2014</span>年~</dd><dd>言語:<span class="language">JavaScript</span></dd></div>
<dd class="description">プロジェクトの一部分に使うことも、シングルページアプリケーションの構築もできる軽量シンプルなUIフレームワーク。開発者は元々GoogleでAngular.jsの開発に携わっていた。</dd>
<!-- /wp:framework-dl/description-block -->

<!-- wp:framework-dl/description-block -->
<dt>Laravel</dt>
<div class="flex"><dd><span class="since">2010</span>年~</dd><dd>言語:<span class="language">PHP</span></dd></div>
<dd class="description">後発ながら高いシェアを誇る。Webアプリケーションに必要な機能は全部入り。フロントはVue.js、バックエンドは依存性注入ができる。Artisanコマンドも種類が豊富。</dd>
<!-- /wp:framework-dl/description-block --></dl>
<!-- /wp:framework-dl/framework-dl-block -->

GitHubにコードをアップしています。

https://github.com/akiya64/framework-dl-block/releases/tag/v1.0.0

WP-CLIのスキャフォルドを使う

スキャフォルド、Ruby on Railsとか他のWEBフレームワークでお馴染みのものです。 WordPressはコアでなくWP-CLIで提供されています。

投稿タイプとタクソノミーを作るのは、一から書いても流用しても割と手間のかかるものですが、WP-CLIのスキャフォルドを使うと素早く雛形が作れます。

この記事では主に下記3つのスキャフォルドの説明をします。

あと、Gutebergブロックのスキャフォルドもできます。

今回、WP-CLIのインストール方法は割愛します。

プラグインのスキャフォルド

まずカスタム投稿タイプやタクソノミー、カスタムフィールドを設定するプラグインを用意します。

wp scaffold plugin add_post_type --skip-tests

オプションでプラグイン名を予めセットしたり色々機能がありますが、プラグインとして最低限動けばいいのでテストユニットもなし、スラッグだけ設定します。
投稿タイプ、タクソノミーを設定するぐらいなら、テストユニットはおそらく出番がないです。

これで、add_post_typeというプラグインの雛形ができました。

投稿タイプのスキャフォルド

投稿タイプの定義はテーマとプラグインのどちらでも行えますが、今回はプラグインで行います。
サンプルとして「プログラミング言語」という投稿タイプを作ります。

wp scaffold post-type language --label="programing language" --plugin=add_post_type

add_post_type プラグインフォルダ内に post_types フォルダと language.php が生成されました。

プラグインの add_post_type.php は空っぽなので、生成した language.phpを読み込むよう実装。

require_once dirname( __FILE__ ) . '/post-types/language.php';

プラグインを有効にすると投稿タイプ「プログラミング言語」が使えます。

表示文言とか全部、テキストドメインを利用する多言語対応の形式で設定されています。
日本語表示にしたい場合はpo/moファイルを作るか、こんな感じで書き換えても表示されます。
上記画面では、このように書き換えてテキストドメインを利用せずに日本語表示をしています。

タクソノミーのスキャフォルド

先に実行したのと同様に「プログラミング言語」投稿タイプに「型付け」タクソノミーを設定してみます。

wp scaffold taxonomy typing --post_types=language --label="typing" --plugin=add_post_type

投稿タイプと同様にadd_post_type.phpにtyping.phpを読み込むように記述します。

require_once dirname( __FILE__ ) . '/tasonomies/typing.php';

注意 予約語について

タクソノミーなどに使える単語には制約があります。

WordPressの予約語
WordPressコアが提供する機能に重複するスラッグを使うとエラーが発生したりレスポンスが404になります。
今回は「typing」なので問題ありませんが、「型だからスラッグは『type』にしよう」とやるとエラーで動きません。
タクソノミーには予約語があることは念頭に入れておくと良いでしょう。

「プログラミング言語」投稿タイプを使う

下記のようにタクソノミーの階層性を True にすると「投稿」に対する「カテゴリー」のように扱えます。

'hierarchical' => true

「プログラミング言語」投稿タイプと「型付け」タクソノミーを利用した投稿サンプルです。テーマはWordPress 5.4デフォルトのTwentyTwentyです。

このように投稿タイプとタクソノミーの設定が素早く確実にできます。
数多くの設定項目やラベルをコマンド1つでセットしてくれるのは便利です。

ぜひスキャフォルドを活用してください。

テーマカスタマイズ関係でWordPressコアのコードを調べた

テーマカスタマイザーにカラーピッカーを追加する際、引数をどうすればいいか分からなくて困ってしまったので、コアのコードまで辿って調べました。

関連記事:テーマカスタマイザーに自由にカスタマイズ項目を追加する方法

CodexのテーマカスタマイズAPIにはずばり、カラーピッカーを追加する際の例が載ってます。

$wp_customize->add_control( 
  new WP_Customize_Color_Control(
     $wp_customize,
    'link_color',
     array(
       'label'      => __( 'Header Color', 'mytheme' ),
       'section'    => 'your_section_id',
       'settings'   => 'your_setting_id',
     ) ) );

しかし、この引数になってる配列には何を指定すればいいのか、よく分かりません。というか、書いてありません。本家の英語版Codexも解説内容は同等です。「add_controlメソッドも参照せよ」とありますので移動します。

クラスリファレンスに飛びました、WordPressコアの仕様書です。
Class Reference/WP Customize Manager/add control

$wp_customize->add_controlメソッドが取る引数はCodexによると下記の通り。

Parameters
$id
(mixed) (required) A string or a specific customization controller object. Default: None
$args
(array) (required) Not used if $id is a control object, otherwise an instance of WP_Customize_Control() (plain text) is created using the specified arguments. Default: None

WP_Customize_Manager/add_control

第一引数は文字列かコントローラーオブジェクトが必須です。
第二引数の配列は、第一引数がオブジェクトの場合は使われないので、add_control()への引数はオブジェクト1個で、今回はnew WP_Customize_Color_Control()ですね。

ということは、WP_Customize_Color_Controlクラスのコンストラクタ引数を確認すればいいわけです。

しかし、その説明がWP_Customize_Color_Controlクラスのリファレンスにありません。

テーマカスタマイザーAPIのコード例と同じ!

Codexの右上の検索フォームからWordPress.org内を検索するとCodexでなくdeveloper.wordpress.orgのクラスリファレンスが見つかります。

Code Reference WP_Customize_Color_Control

しかし、コードリファレンスはまたしてもこれだけです。

説明らしい説明がありませんので、表示されているWordPressコアのコードもみてみます。

/**
 * Constructor.
 *
 * @since 3.4.0
 * @uses WP_Customize_Control::__construct()
 *
 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
 * @param string               $id      Control ID.
 * @param array                $args    Optional. Arguments to override class property defaults.
 */
  • 第一引数:カスタマイズマネージャーのインスタンス
  • 第二引数:ID(識別子)
  • 第三引数:デフォルトのプロパティを上書きするための配列 (オプショナル、渡さなくてもいい)

記述はありましたが、結局、コンストラクタ引数の配列には何を入れたらいいんでしょうか?

ここで注目すべきはclass-wp-customize-color-control.php の最初の宣言です。

class WP_Customize_Color_Control extends WP_Customize_Control

WP_Customize_Color_ControlWP_Customize_Controlを継承したクラスです。
WP_Customize_Controlをベースに、カラーピッカーを表示するために必要な変数、関数を追加する形になっています。

よって、インスタンス引数は WP_Customize_Control と同じでよい、ということです。

参考:PHP公式 オブジェクトの継承

ということで、 CodexのWP_Customize_Cotrolリファレンスを確認します。

ようやく、辿り着きました。Arguments以下の定義リストが WP_Customize_Color_Controlにも指定できる引数一覧です。

テーマカスタマイザーに自由にカスタマイズ項目を追加する方法の記事では、ここから必要な項目のみを指定しています。

ちなみにWP_Customize_Controlクラスのリファレンスからソースコードをたぐると、コメントのConstructorセクションに引数の説明があります。

テーマカスタマイザーに自由にカスタマイズ項目を追加する方法

このブログのテーマ、shirohanadaに実装している「コンテンツ背景色」のカスタマイズ項目の追加方法について解説します。

カスタムヘッダーでヘッダー画像を自由に変えられるようにしたところ、期せずしてヘッダー色のカスタマイズも付随していたので、背景色とかもカスタマイズ可能にしました。

「コンテンツ背景色」はshirohanadaで独自に追加したカスタマイズ項目です。投稿の背景色などデフォルトで薄いグレーを敷いている部分の色が変更できます。テーマカスタマイズAPIを使うと、割と簡単に自由にカスタマイズ項目が実装できます。

基本的にはWordPress Codex テーマカスタマイズAPIに沿って実装していきました。

目次

テーマカスタマイザーの仕組み

  1. functions.phpにカスタマイズ項目を追加する設定を書く
  2. ダッシュボードの「外観」→「カスタマイズ」から、スタイルを変更する
  3. 変更した内容はoptionsテーブルのtheme_mods_テーマ名 レコードに格納される
  4. functions.phpでカスタマイズの内容を読み出してCSSを作る
  5. ヘッダーにインラインCSSとして出力する
  6. style.cssはカスタマイズしたスタイルで上書きされる

ざっとこういう具合です。

コントロールの追加

まず、「コンテンツ背景色」という名前のカラーピッカーを表示する設定をfunctions.phpに書きます。

  • $wp_customize->add_setting( args )
  • $wp_customize->add_control( args )

この2つの関数がテーマカスタマイザーの呼び出し時に実行されるよう、add_action('customize_register') で指定してます。

add_settingでの設定

まずはadd_setting関数から。

新たに作るカスタマイズ項目の設定を$wp_customizeオブジェクトの関数add_setting()に引数で渡します。ここで設定しておくとget_theme_mod( ‘ID’ )でカスタマイズした設定を取得できて、後々で楽になります。

$wp_customize->add_setting(
    'contents_bg_color',
     array(
        'default' => '#f8faff',
        'transport' => 'refresh',
        'sanitize_callback' => 'sanitize_hex_color',
    )
);

引数の内容は

  • ID(テーマ内で一意な識別名): contents_bg_color
  • 配列
    • デフォルト値:#f8faff
    • カスタマイズ画面での更新用の設定:refresh
    • 呼び出し時に実行するサニタイズの種類:sanitize_hex_color

設定内容の保存先をoptionsテーブルか、optionsテーブルのtheme_mods_shirohanadaレコードか指定できます。デフォルトではtheme_modsレコードに保存なので、今回は指定しません。

optionsテーブルへの保存を指定するとカスタマイズ項目1つにつき1レコード生成されると思いますが、特にメリットもなさそうなのでテーマのカスタマイズ関係はtheme_modsに固めています。

transport設定は、とりあえずrefreshでいきます。この設定だと、色を変えた時にプレビュー画面が数秒フェードアウトしてしまいますが、ちゃんと使えます。別途jQueryを書いたjsファイルを用意しておくとプレビュー画面でフェードアウトなしの再描画をしてくれます。

sanitize_callbackは、新たに作りたい項目が色なので16進カラーに適用するサニタイズを指定します。

参照:https://codex.wordpress.org/Class_Reference/WP_Customize_Manager/add_setting

add_controlでの設定

同様にadd_control関数に、コントロールを表示するための設定を渡します。今回は色の設定になるので、カラーピッカーを使えるように引数を組みます。他にもテキストエリア、ラジオボタンやセレクトボックスなどが指定できます。
参照: https://codex.wordpress.org/Class_Reference/WP_Customize_Manager/add_control

Usageなんかを参考にして、下記のコードを書きました

$wp_customize->add_control(
	new WP_Customize_Color_Control(
		$wp_customize,
                'contents_bg_color',
                array(
                    'label' => 'コンテンツ背景色',  
                    'section' => 'colors',
		)
	)
);

カラーピッカーの場合は、ちょっと特殊でWP_Customize_Color_Controlクラスのインスタンスを引数にします。

で、add_control()関数にオブジェクトを渡す場合は、引数は1個でOK。
IDとかラベルはWP_Customize_Color_Controlクラスのコンストラクタ引数で指定します。

  • 第一引数 コントロールマネージャーのオブジェクト $wp_customize
  • 第二引数 テーマ内で一意なID(識別子)
  • 第三引数 配列 オプショナル、デフォルトの設定の上書きに使う

第三引数の配列は new WP Customize_Contorol()で渡せる内容と同一のようです。
今回はラベルとセクションだけでいいです。配列にsettingsがない場合はIDで一致するカスタマイズ設定を取得&書込してくれます。なので、先ほどadd_setting() で指定したのと同じIDを入れておきます。

今回はラベルとセクションだけでいいです。配列にsettingsがない場合はIDで一致するカスタマイズ設定を取得&書込してくれます。なので、先にadd_setting() で指定したのと同じIDを入れておきます。

settings
All settings tied to the control. If undefined, `$id` will be used.

https://codex.wordpress.org/Class_Reference/WP_Customize_Control

labelはコントロールの表示名です。

sectionは「基本情報」「ヘッダー」「色」などの区分のうち、どこにコントロールを入れるかを指定します。sectionを新たに作ることもできます。

WP_Customize_Color_Controlクラスのインスタンス引数とか、Codexに説明がなくてコアのコードを見てきたので、まず合ってると思います。
調べた際の記事はこちら:テーマカスタマイズ関係でWordPressコアのコードを調べた

add_actionでフック

カスタマイズ項目を追加するための最終的なコードは下記の通りです。
add_actionに渡す関数(下記コードではcontents_bg_customize_register)は、引数に$wp_customizeを取ります。WordPress本体が使用しているグローバル変数です。

function contents_bg_customize_register( $wp_customize ) {
	$wp_customize->add_setting(
		'contents_bg_color', 
                 array(
			'default' => '#f8faff',
			'transport' => 'refresh',
			'sanitize_callback' => 'sanitize_hex_color',
		)
	);
	$wp_customize->add_control(
		new WP_Customize_Color_Control(
			$wp_customize,
			'contents_bg_color',
			array(
				'label' => 'コンテンツ背景色',
				'section' => 'colors'
			)
		)
	);
};
add_action( 'customize_register', 'contents_bg_customize_register' );

CSSの生成とヘッダーへの埋め込み

カスタマイズ項目が追加できたので、これを読み出してCSSを生成します。生成したCSSはadd_actionで<head>に埋め込まれるようセットします。
ヘッダーにインラインで埋め込まれるため、外部ファイルであるstyle.cssより優先度が高くなります。

カスタマイザーで設定した値を取得するにはget_theme_mod()関数を使います。

function mytheme_customize_css()
{
    ?>
         <style type="text/css">
             h1 { color:<?php echo get_theme_mod('header_color', '#000000'); ?>; }
         </style>
    <?php
}
add_action( 'wp_head', 'mytheme_customize_css');

このようにCodexにあるような書き方でもいいですが、自分はヒアドキュメントを使って書きました。
ヒアドキュメントでCSSを書いて変数に入れてしまう方が読みやすいと思います。ただし、echoで出力する際にエスケープ処理が必要になります。(WordPressコーディング規約に準拠させるなら)

function customizer_css_output() {
	$bg_color = get_background_color();
	$contents_bg_color = get_theme_mod( 'contents_bg_color', '#f8faff' );
	$style = <<<CSS
<style type="text/css" id="shirohanada-customizer">
	body { background-color : #$bg_color; }
	.side-widgets,
	.page-title,
	.pagination,
	.nomatch-contents,
	.link-posts p,
	.type-page,
	.type-post {
		background-color : $contents_bg_color ;
	}
</style>
CSS;
	$allowed_style_tag = array(
		'style' => array(
			'type' => array(),
			'id' => array(),
		),
	);
	echo wp_kses( $style, $allowed_style_tag );
}
add_action( 'wp_head', 'customizer_css_output' );

get_background_color()はカスタムバックグラウンド機能で指定した色を取得します。これはBody要素に適用するので、一緒に読み出して設定してしまいます。

get_theme_mod( 'contents_bg_color', '#f8faff' )
第一引数にIDを指定します。add_setting/add_controlで指定したIDをセット。これだけでoptionsテーブルのtheme_mods_shirohanadaレコードから、該当するIDの設定値を取得してくれます。設定値が空の場合、第二引数のデフォルト値が使われます。

wp_kses( $style, $allowed_style_tag )
wp_kses()関数でエスケープ処理をします。第一引数にCSSヒアドキュメントを格納した変数、第二引数にタグ・属性の配列を渡すと、指定したタグ・属性のみをそのまま出力してくれます。今回はスタイルタグと、type・idを出力して欲しいので上記のような配列を作って引数として渡しています。

実際にヘッダーに出力されるCSSは以下の通り。

本番用はカスタムヘッダーの設定も一緒に取得&出力している

完成

add_settingの設定時に説明したように、カラーピッカーで色を変えるとプレビュー画面が数秒フェードアウトしてから反映されます。

テーマカスタマイズAPI ステップ 2: JavaScript ファイルの作成で説明されているように、更新用jsファイルを用意すれば、WordPressがデフォルトで用意しているカスタマイズ項目のように、フェードアウト無しで一瞬でプレビューが描画されます。

$wp_customize->add_controlの引数がやや複雑ですが、非常にシンプルな記述でオリジナルの項目を作成できます。

テーマフォルダの画像を表示するときに便利な関数

常用していた画像の呼び出し方法だと、srcsetプロパティを使った時にちょっと読みにくくなるので、get_theme_file_uri() 関数を使って書き換えてみました。

WordPressのテーマ作成で、テーマテンプレート内の画像を表示する方法としてget_stylesheet_uri関数を使用してきました。こういう感じですね。

<img class="site-banner"
   src="<?php echo get_stylesheet_directory_uri(); ?>/img/site-banner.png"
   alt="<?php bloginfo( 'name' ); ?>"
>

get_stylesheet_directory_uri()と書いてから、ディレクトリを続けて書いてファイル名。これで当サイトならimgのsrcプロパティは下記のように出力されます。

https://autumnsky.jp/wp-contents/themes/shirohanada/img/site-banner.png

ですが、レスポンシブ対応で複数の画像を指定する際は、この方法だと少し読みにくいように感じます。
最近だと画像やアイコンをassetsフォルダの中に、さらに分類していたりするのでパス指定も長くなりがちです。
参考: レスポンシブ画像の作り方 – 解像度の切り替え: 同じサイズ、異なる解像度  Mozzila

<img class="site-banner"
src="<?php echo get_stylesheet_directory_uri(); ?>/assets/img/site-banner.png"
srcset="<?php echo get_stylesheet_directory_uri(); ?>/assets/img/site-banner.png x1,
<?php echo get_stylesheet_directory_uri(); ?>/assets/img/site-banner@x2.png x2"
alt="<?php bloginfo( 'name' ); ?>" >

このように、すこし読みにくいように思います。

そこで、WordPress4.7以降で追加された get_theme_file_uri関数を使います。

<?php echo get_theme_file_uri( "/img/site-banner.png" ); ?>

get_theme_file_uri関数は<?php  ?>の後ろに書いていたファイルパスを引数として()内に指定できます。
上記のコードは、get_stylesheet_directory_uri関数を使った時と同じように下記の通り出力されます。

https://autumnsky.jp/wp-contents/themes/shirohanada/img/site-banner.png

srcsetプロパティを指定する際に
get_theme_file_uri関数を使うと、このように書き換えられます。

<img class="site-banner"
   src="<?php echo get_theme_file_uri( "/assets/img/site-banner.png" ); ?>"
   srcset="<?php echo get_theme_file_uri( "/assets/img/site-banner.png" ); ?> x1,
   <?php echo get_theme_file_uri( "/assets/img/site-banner@x2.png" ); ?> x2"
   alt="<?php bloginfo( 'name' ); ?>"
>

get_stylesheet_directory_uriを使うよりもスッキリと書けます。

参考にしたサイト

コミット時にWordPressコーディング規約に自動的に準拠させる方法

GitにはGitフックという仕組みがあります。
Git Documentation 8.3より

Gitのカスタマイズ ー Git フック
Gitにも特定のアクションが発生した時にカスタムスクリプトを叩く方法があります。

これを使って、WordPressのコーディング規約に準拠するようPHP/CSSをコミット時に自動修正します。
また、チェックにパスできない場合はコミットできないようにもなります。
コードのチェックにはPHP Code Snifferを使います。
さらに、Code Snifferが参照するWordPressのコーディング規約
WordPress-Coding-Standards
参考として、大元になるWordPressのコーディング規約集
WordPress コーディング規約 WordPress Codex 日本語版

PHP Code Snifferのインストールと設定

ローカルでPHPの実行環境が必要になりますので、インストールしておいて下さい。
WindowsでのPHP単体インストールは当サイトに記事があります。
参照:PHP7を単体でインストールする
Code Snifferをインストールします。
Composerでも行けるし、公式のGitHubにあるようにcurlwgetでダウンロードでもいいです。

ファイルを入れるだけで動かせます、binフォルダ内のphpcsを実行してバージョンを確認してみます。

$ phpcs/bin/phpcs --version

PHPCSの動作確認
別途、WordPress用のルールセットWordPress-Coding-Standardsをダウンロードしておきます。
今回はルールセットをwpcsフォルダにダウンロードしています。
このフォルダを下記コマンドでCode Snifferのinstalled_pathsに登録します。

$ phpcs/bin/phpcs --config-set installed_paths .\wpcs

コマンドラインで単体ファイルの修正とチェックを行ってみます。

$ phpcs/bin/phpcs --standard=wordpress front-page.php

PHPCSでコードをチェック
2箇所のエラーがありますね。

49行目
「次の関数はエスケープ処理を行うべきです(Codexの『データバリデーション』を参照)、’get_first_post_year’」
50行目
「関数呼び出しの閉じ括弧の後ろのスペースは禁止されています」
最後に
「マークされた1件の違反は、PHPCBFで自動修正が可能です」

50行目は自動修正が可能なようですので、CodeSnifferのphpcbfツールで修正します。

$ phpcs/bin/phpcbf --standard=wordpress front-page.php

PHPCBFにて自動修正
1カ所が修正されました。
もう一度、phpcsでチェックしてみます。
PHPCSで2回目のチェック
50行目のエラーが修正されたので、エスケープ処理がされていないエラーだけ残っています。
こちらは適切なエスケープ処理を書いてやる必要があります。
エラー処理前のコードは下図の通りです。50行目のhome_url()の直後だけ余計なスペースがあります。
また、get_first_post_year関数はfunctions.phpで定義している、年号を返す関数なのでエスケープ処理が必要です。
intvalにて整数型へキャストするようにWordPress Codexに記載があります。
PHPCSでエラーになる例

git hooksファイルの作成

先に試した修正とチェックをコミット前に行うよう、git hooksにスクリプトを書きます。
ローカルリポジトリの.git/hooks/にサンプルがあります。
中身はシェルスクリプトです。簡単に使い方など書いてあります。
githooksのフォルダ
ファイル名から.sampleを削除すると、該当するgitの処理が走る時に実行されます。
今回、pre-commitとして、このようなスクリプトを配置します。

#!/bin/sh
IS_ERROR=0
# コミットされるファイルのうち、.phpかcssで終わるもの
FILES=` git status --short | grep -E "^[M|A].*(.css|.php)$" | cut -c4-`
for FILE in $FILES
do
    ./phpcs/bin/phpcbf --standard=wordpress $FILE
    if ! ./phpcs/bin/phpcs --standard=wordpress $FILE ; then
        ./phpcs/bin/phpcs --standard=wordpress $FILE
        IS_ERROR=1
    else
        git add $FILE
    fi
done
exit $IS_ERROR

スクリプトの処理内容は、下記の通り。

  1. git addされたファイルから拡張子がphpかcssのファイル名を取り出す
    → FILES変数へ格納する
  2. 取り出したファイル一つずつに対し、自動修正をかける
    for FILES in $FILE do 内の処理
  3. さらに、そのファイルにCodeSnifferでチェックを行う
  4. エラーがなかったファイルは、git addでステージングし直す。
  5. エラーコードを返して終了

動作確認

git commitしてみます。
pre-commitフックはコミットメッセージを書くためのエディタが立ち上がる直前に実行されます。
githooksからPHPCSを走らせる
先ほどのように修正してチェックが実行されています。
エスケープ処理は自分で書かなくてはいけないので、エラーで終わっています。
この場合、エディタが起動しません。
SourceTreeからコミットしようとしてもこの通り。
SourceTreeからもgithooksは実行される
また、下記のように「手動で調査が必要」というアラートが出ることがあります。

Detected access of super global var $_POST, probably needs manual inspection.

手動での調査が必要なエラー
コードが規約に準拠していてもアラートが出て、エラーコード1となってしまうため、githooksでCodeSnifferが走る限りコミットできなくなります。
ルールセットの修正などで対応できるのだと思いますが、私は一度エラーメッセージを確認したらバイパスしてコミットしています。  githooksをバイパスする
gitコマンドではgit commit --no-verifyでバイパスできます。
さらにShirohanadaテーマはTravisCIでもCodeSnifferを実行するようにしています。
チェック後にテーマをビルドするので、規約違反のコードはデプロイされません。
(設定はローカルと違って、Warningは可としています)
TravisCIでのチェックログ

やってはいけないことは、できないようにしておく

例えば工場のプレス機など、プレスをするスイッチは両手で同時押しになっています。
手を挟まないように、両手が安全な位置になければ動作しない構造にしているわけです。
(それでも労働災害は、起きるときは起きる)
危険が伴う作業や現場、機械には、さまざまな安全装置があり原則があります。
有名なところでは「フェイル・セーフ」という原則があります。
例えば鉄道はブレーキの信号線が断線すると自動的に非常ブレーキがかかります。
安全に関わる原則の内「やってはいけないことは、できなくしておく」ことは「フール・プルーフ」という原則に含まれます。
私はデスクワークやコーディングも、このような原則を積極的に取り入れるべきだと思います。
今回紹介したように、コミット時とビルド時に自動修正をかけた上でチェックする仕組みを整えておけば、コーディング規約に準拠するコードだけがコミットされていきます。
こういった「安全確認」は本来やりたいこと、やるべきことに対する付随作業です。
また、コーディング規約の準拠は、手作業でやるには煩雑で、目視では間違えやすいところです。
たった一つの丸括弧の後ろの空白を、手作業で探すのは非現実的ではないでしょうか。
なので、機械に任せてしまう方が楽で確実です。

参考

gitのpre-commit hookを使って、綺麗なPHPファイルしかコミットできないようにする MANA-DOT
フェイルセーフとフールプルーフ~意味の違いと事例 レジリエントメディカル より安全な医療システムを実現する

WockerでWordmoveを使う

Wockerバージョン1.3にてWordmoveが実装されました。


今回はWordmoveをつかって、このブログのテーマをWocker内のWordPressに反映させてみます。
Wordmoveを使うと、PC内の開発用WordPressとサーバの本番環境のWordPressをコマンド一つで「同期」できます。
公式サイトでは「自動的なミラー」(複製)と書いています。

Wordmove is a gem that lets you automatically mirror local WordPress installations and DB data back and forth from your local development machine to the remote server.

Wocker自体の使い方は、公式サイトwocker.ioを参照して下さい。
Windowsでの使い方は当ブログに解説記事があります。WindowsでWockerを使う ― Autumnsky

ローカルのフォルダ構成、リモートの構成

作業前にローカルのフォルダ構成と、サーバ側のWordPressの設定を確認します。
当方ではDドライブ内にwockerのフォルダを作っています。
下記のコマンドでtest-wordmoveというコンテナを作りました。

$ wocker run --name test-wordmove

ローカルのWordPressが入っているフォルダはD:\wocker\data\test-wordmoveとなります。
wocker start test-wordmoveコマンドで、このフォルダ内のWordPressがPCで実行されます。
当ブログはさくらのレンタルサーバ スタンダードプランに構築されています。
サーバ側のWordPressは別途フォルダを作ってwordpress内にインストールしていますので、

WordPress アドレス (URL): https://autumnsky.jp
サイトアドレス: https://autumnsky.jp

上記の通りになっています。

Wordmoveのmovefile.ymlを生成する

ローカルのWordPressの設定、本番環境が動いているサーバの設定、等の設定をmovefile.ymlに記載します。
movefile.ymlに記載された設定を元に、Wordmoveは同期を行います。
.ymlは、YAML(ヤメル)というデータ形式のファイルです。私は「ヤムル」と読んでいます。
ローカルのWocker内には、初期状態ではmovefile.ymlはありません。
まず、Wockerコマンドでmovefile.ymlを生成します。

$ wocker wordmove init

result_wordmove_init
movefile.ymlが作成されました。テキストエディタで開いてみます。
(Windowsではメモ帳は使わないで下さい。)created_movefile
この時点で、Wocker内のWordPressは設定済みです。
下記のglobal:からproduction:の手前までの部分。
movefile_global_section

global:
  sql_adapter: wpcli # default is not available for Wocker
local:
  vhost: http://wocker.test
  wordpress_path: /var/www/wordpress # use an absolute path here
  database:
    name: wordpress
    user: wordpress
    password: "wordpress" # could be blank, so always use quotes around
    host: localhost

movefile.ymlを編集する

先に生成したymlファイルに本番環境(サーバー側、リモート)の設定を書いていきます。
production:以下、インデントが一つ下がっている項目は、サーバー側の本番環境の設定項目です。
例えば、database以下はproduction(本番環境)のデータベース設定です。databaseのname、databaseのuser、databaseのpasswordと設定が必要です。
これを本番環境のサーバー設定に書き換えていきます。
WordPressの設定、wp-config.phpとmovefile.ymlの設定を対応させると下図のようになります。
サーバ側 WordPress 4.9.2 / ローカル Wocker v1.3 の場合
prodction_server_settings
書き換え後の例

production:
  vhost: https://autumnsky.jp
  wordpress_path: /home/autumnsky/www/wordpress
  database:
    name: wp-config.phpからコピー
    user: wp-config.phpからコピー
    password: wp-configからコピー
    host: wp-configからコピー
    # port: 3308 # Use just in case you have exotic server config
    # mysqldump_options: --max_allowed_packet=1G # Only available if using SSH

vhost:は最後のスラッシュは書きません。リモートのデータベースをローカルへプルした際、データベース内にあるproduction: vhostのURLをlocal: vhostの文字列に書き換えています。サーバー側とローカルで、サイトURLが変わるため、URLを書き換えなければローカルのWordPressが正しく動きません。
正確に記載されていないと、書き換えに失敗します。
production:以下のwordpress_pathはサーバー側のWordPressを設置しているパス(フォルダ)の指定です。

サーバーのテーマフォルダをプル

それではサーバのテーマをローカルのWockerに引っ張ってみます。
今回はSSHという接続を使います。

SSH(Secure Shell) とは、物理的に遠いところから、サーバを操るための手段のひとつです。
さくらのレンタルサーバ SSHについて

SSHでの接続設定はサーバごとに異なりますので、各自ご確認下さい。
まず、先と同様にmovefile.ymlのSSH項目の設定を書きます。
SSHの設定は初期状態ではコメントアウトされているので、#を半角スペース2個で置き換えます。
左が編集前、右がコメントアウトを外した状態です。
下記の画像では分かりやすいように、エディタの設定でスペースを_で可視化しています。
enable_ssh
さくらレンタルサーバなら、hostとuser設定だけでもOKです。wordmoveする都度、パスワードを入れる必要がありますが。
インデントの半角スペースの個数、インデントの段数に気をつけて下さい。
YAML形式のインデントは半角スペース2個です。
このインデントの深さがデータの階層構造に対応します。
movefile.ymlは下記のツリーのような階層構造となっています。

movefile.yml
├─ local:(PCのローカルWordPressの情報)
│   ├─ vhost: http://wocker.test (サイトURLはwocker.test)
│   ├─ wordpress_path: /var/www/wordpress # use an absolute path here
│   └─ database:(ローカルのデータベース設定)
│        ├─ name: wordpress
│        ├─ user: wordpress
│        ├─ password: "wordpress" # could be blank, so always use quotes around
│        └─ host: localhost
│
└─ production:(本番環境のサーバ、WordPressの情報)
    ├─ vhost: http://example.com (本番環境のサイトURL)
    ├─ wordpress_path: /var/www/your_site (本番環境のサーバー内のパス)
    ├─ database: (本番環境のデータベース設定)
    │   ├─ name: database_name
    │   └─ ...
    ├─ exclude (除外ファイル)
    ├─ ftp (本番環境へのFTP接続の設定)
    ├─ ssh (本番環境へのSSH接続の設定)
    └─ ...

WordmoveはRubyで書かれています。YAML形式はRubyの構文に近いです。インデントが空白2個で文法上の意味があるのも、Rubyと同じです。WordPressはPHPなので、随分と勝手が違いますね。
ではいよいよWordmoveコマンドでテーマを取得します。
その前に

  • 注意 サーバーに存在しないテーマは、ローカルのフォルダから削除されます!

サーバー側にないファイルは、一旦待避してください。
では実行します。

$ wocker wordmove pull -t

pullはサーバー側のファイルを引っ張ってくる、-t はテーマのみとする指定です。
実行結果はこのようになります。
success_pulling
success_download
movefile.ymlが読み込めない時は、下記のようにRuby実行環境がエラーを出します。
yaml_parse_error
「あるべきはずの設定値が見つからなかった」という意味ですね。
この場合は「28行目を含むブロックを解析中に見つかりませんでした」とのことです。
(黄色背景の強調表示は筆者が独自につけています。)
YAMLファイルの書き方が正しいかチェックしてくれるWEBサービスもあります。
例)YAML フォーマットチェッカーCGI

プルしたテーマを確認する

Wordmove pullは、サーバー側とローカル側のテーマフォルダを完全に同期します。
サーバー側に存在しないテーマをローカルで使っている場合など、wocker.testを開くとホームは真っ白になる場合があります、
http://wocker.test/wp-login.phpからダッシュボードにログインして、テーマを確認します。
after_pulling
左側のスクリーンショットが空白のテーマが選択されています。
当サイトはWordPress4.9.2のデフォルトテーマであるTwentySeventeenを入れていないため、ローカルのWockerから削除されています。
右側のShirohanadaを有効化します。
WockerでShirohanadaテーマが表示されました。
mytheme_in_wocker

おわりに

今回はプルしか試していませんが、もちろんプッシュも可能です。
また、Wocker V1.3では若干、制限があります。

  • 公開鍵認証を使ったSSH接続
    できると思いますが、Wockerのコンテナ内にid_rsaファイルを設置する必要があります。
  • FTP接続
    FTP接続は今のところできません。これもWockerのコンテナの設定で出来るようになります。

多少の制約もありますが、使いやすく実装できていると思います。
是非活用して下さい。
movefile.ymlの書き方については、こちらを参考にしました。
Wordmove の正しい設定方法 Qiita @miya0001

WordPressのカテゴリーに「カスタムフィールド」を設定する

当サイトのテーマShirohanadaはカテゴリー表示の時、全文表示と抜粋表示を切り換える機能を実装している。
この抜粋・全文の表示フラグ、構想当初は「カスタムフィールドで保持して読み書きすればいいだろう」と考えていた。 投稿に対するカスタムフィールドはget_post_meta()などで取得する。多分、なんか楽にできる仕掛けがあるだろう、と。
結果、「カスタムフィールド」ではなく、theme_modで実装しました。その紆余曲折とtheme_modについての記事です。

wp_optionsテーブルがカオス

「カテゴリー カスタムフィールド」なんかでググったところget_options関数を使う例がいくつかみられた。
「これだこれだ、wp_optionsテーブルに入れてget_optionsで取得するのね。さてどういう感じで入れようかね」とコードを書いていたら何か上手く行ったり行かなかったり。
データベースの方はどうなってるんだ?というわけでphpmyadminでwp_optionsテーブルを見ると…
レコード番号が物凄い値になってる…(まあIDの連番がすごい数になってるぐらいは、どうということはないはず)
当サイトのwp_options スクリーンショット

「カスタムフィールド」とは何か?

改めてカスタムフィールドやデータベースについて、WordPress Codexの説明を読む。

WordPress には、投稿者が投稿に「カスタムフィールド」を追加できる機能があります。
WordPress Codex カスタムフィールドの使い方

このカスタムフィールドはpost_metaというテーブルに保存される。post_metaは投稿に対して付加されるので、カテゴリー番号やカテゴリー名でセットすることができないのだ。
WordPress Codexデータベース構造によると…

wp_postmeta
メタデータという各投稿記事特有の情報を格納。カスタムフィールドとして使用するほか、各投稿に情報や設定を付加するようなプラグインが、その情報を当テーブルに追加することがある。
wp_options
管理 > 設定で設定されたオプション設定情報を格納(オプション設定リファレンス参照)。プラグインの設定情報が格納されることも多い。

カスタムフィールドというのは、基本的に「投稿」を拡張する仕組みであって、カテゴリー単位の設定情報とかを保存する仕組みではない。
ならば、まさにwp_optionsを使うところなのだが……
どうも過去に使っていたプラグインの一時データやら設定やらで膨れあがっている。
そうするとテーマが削除されるタイミングをフックして、関係するレコードは削除しなくてはならないなあ……
カテゴリーが増えてきたら、沢山のプラグインと同居したら、きちんとレコードから「ウチのテーマの抜粋表示・全文表示フラグ」を取れるんだろうか、正しく書き込めるだろうか……

theme_modsレコード

wp_optionsテーブルを眺めていたら、mods_twentyelevenやtwelveなるレコードがある。
もしかして、枠線の色を変えたりとかTwenty系テーマのカスタマイズ値はここに格納されているのだろうか?
テーマ名を含むレコードのphp my admin のスクリーンショット

テーマカスタマイズ API
テーマ設定を取得し、ヘッダーにCSSを出力します。 上記のように customize_register アクションでテーマ設定を追加したなら、wp_head アクションで CSS を出力したり、get_theme_mod() / en で設定値を取得するだけです。

正解。これだ。
テーマをカスタマイズしたら、wp_optionsテーブルの
theme_mods_[テーマ名] レコードにキーと値が格納されているので、get_theme_mod(‘Key’, default)で取得してテンプレートやCSS、Javascriptで煮るなる焼くなりしている。

get/set メソッドで簡単に使える

set_theme_mod( $name, $value )
抜粋表示フラグをセットする。
cat_を接頭辞にしてカテゴリー番号を使ってキーを作成する。キーと値との組を引数にしてset関数実行。

$cat_id = "cat_$term_id";
if ( 'yes' === $flag ) {
  set_theme_mod( $cat_id, true );
} else {
  set_theme_mod( $cat_id, false );
}

get_theme_mod( $name, $default )
抜粋表示フラグを取り出す時は、同じくカテゴリー番号からキーを作ってget関数を実行。

$key = 'cat_' . get_query_var( 'cat' );
$is_show_excerpt = get_theme_mod( $key );

シンプル!
theme_modsレコードがない時はWordPressコアの方で上手いことやってくれると思います(未検証)
wp_optionsテーブルのtheme_mods_shirohanadaレコードに格納されている値はこんな感じ。

a:9:
{
  i:0;
  b:0;
  s:81:"https://autumnsky.jp/wp-content/uploads/2016/11/161029-web.jpg";
  s:17:"header_image_data";
  O:8:"stdClass":5:
    {
    s:13:"attachment_id";
    i:15747;
    /* このへんカスタムヘッダー関係の設定値. */
    s:6:"height";
    i:700;
    s:5:"width";
    i:700;
    }
  /* set_theme_mod()でセットした値. */
  s:6:"cat_10";
  b:1;
  s:7:"cat_269";
  b:0;
  s:7:"cat_157";
  b:0;
  s:7:"cat_253";
  b:1;
}

外観のカスタマイズ用ということもあって、かなり大量の要素を扱えるようになっているみたいです。
本来の用途はダッシュボード > 外観 > カスタマイズ で設定した値を簡単に保存・読出するための関数です。
ダッシュボード > 外観 > カスタマイズ スクリーンショット
この画面の左側、カスタマイズ項目を自由に追加するために用意されている関数・クラスです。本来の使い方はテーマカスタマイズ APIを参考にして下さい。私もあまりよく分かっていません。

まとめ

テーマ固有の値を保持したいときは、WordPressのtheme_mod関係の関数を使って簡単にできます。
投稿の抜粋・全文の表示の制御など、プラグインでやることじゃないか?
と思いましたが、しかし、どちらにも対応したテンプレート構造・CSSを用意しないといけないので、Shirohanadaではテーマ側で実装しています。
結局、カスタムフィールドとwp_optionsは使ってませんが、タイトルはあえて「カスタムフィールド」としました。

さくらインターネット+PHP7.1+WordPress

さくらのレンタルサーバ PHP7.1 提供開始のお知らせ 2016年12月14日

早速、当サイトも切り替えてみました。

WordPressの互換性

最新版である4.7ではPHPの推奨バージョンは7.0以上である。
動作は5.2.4以上で確認されているが、4.4~5.4は既にEOLで開発終了。
このサイトはPHP5.6でWordPressを走らせていました。
よって、WordPressコアはPHP7.0にアップデートして問題ない。
自作テーマもこの時に備えてPHP7.0でテスト済み。
プラグインは…まあ大丈夫だろう!
TravisCIスクリーンショット

設定方法

というわけで、PHP5.6 -> 7.1へ変更してみる。

  1. サーバーコントロールパネルにログイン
  2. PHPのバージョン選択 -> 新しいバージョン
  3. PHP 7.1を選ぶ

以上。
さくらインターネット PHP7設定

速度

WordPressのダッシュボード画面で速度を計測しました。
Firefoxのツール -> Web開発 -> ネットワーク でレスポンス速度など計測できます。
上がPHP5.6の時、下がPHP7.1の時。
Firefox ネットワークのスクリーンショット
比較しやすいよう、1.28秒のラインを揃えてみました。
ダッシュボードでの速度比較 グラフ
9.34秒かかっていたダッシュボードの表示が、3.27秒です。速い!
「WordPressをHHVM(Facebookが開発したすごい速いPHP実行環境)で実行するとダッシュボードが一瞬で開く」「PHP7はHHVMに匹敵する処理速度になる」とのことでした。PHP開発元によるWordPressのベンチマークでもほぼ同等。


WordPressの弱点「読込が重い」という点が、あっさり改善しました。
なんでもPHP本体の開発陣もWordPressでのパフォーマンスは注視しているそうで、ありがたい限りです。
ふと思ったのですが、4.7から標準機能になったWordPress REST APIは「PHP7の処理速度ならいける」という目算かも知れないですね。