ブロックにオリジナルのアイコンを入れたいな<WordPressのブロック制作>

とりあえずブロックのひな型は作れたので、早速オリジナルブロックの作成に取り掛かろうということになるのですが、本番の中味に入る前にちょっと見た目を整えましょう。 ブロックの象徴は何といってもアイコンでしょ。やっぱりオリジナルのアイコンを入れたいですよね。ということで、その方法を説明します。

FontFontAwesomeからダウンロード

まず、アイコンを作るのですが、一から作るのはかなり面倒ですので、FontAwesomeなどからフリーのSVGをダウンロードします。

Font Awesome.png

好きなアイコンをクリックして

choise.png

SVG形式でないとダメですよ。

ブロックに登録

そしてブロックプロジェクトのsrcフォルダに入れて下さい。 アイコンの登録は通常block.json

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 2,
    ・・・
    "icon": "button",
    ・・・

というように入っています。しかし、オリジナルアイコンはblock.jsonで登録することはできません。したがって、このiconの行を消して、index.jsでのJavaScriptによる登録に切り替えます。 index.jsは次のようになります。

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


import Edit from './edit';
import save from './save';
import metadata from './block.json';
import { ReactComponent as MyIcon } from './my-icon.svg';


registerBlockType(metadata.name, {
    icon: <MyIcon />,
    edit: Edit,
    save,
});

このときReactコンポーネントとして登録するので、アイコンのコンポーネント名はReactの規則に沿って頭文字は大文字でなければいけません。 なお、ChatGPTに聞くとwordpress/create-blockで作ったプロジェクトはデフォルトでSVGファイルを扱えないというような情報を示されましたが、2023年5月現在では扱えるようになっています。古いプロジェクトの場合はwebpackの上書きをしてSVGをトランスパイルするようにセットする必要があるようですが、その必要はありません。

この際、Buttonコンポーネントに表示する方法も覚えておこう

Buttonコンポーネントにもアイコンを入れたいこともあるでしょう。 これはブロックコントロール内のButtonコンポーネントのアイコンの設定例です。svgファイルの用意の仕方は同じです。

import { ReactComponent as play } from './circle-play.svg';
import { ReactComponent as pause } from './circle-pause.svg';

<BlockControls>
    <Toolbar>
        <Button
            //属性 isEditMode の値により表示するラベルを切り替え
            label={is_anime ? "実行中" : "停止中"}
            //属性 isEditMode の値により表示するアイコンを切り替え
            icon={is_anime ? pause : play}
            //setAttributes を使って属性の値を更新(真偽値を反転)
            onClick={() => {
                setAttributes({ is_anime: !is_anime })
            }}
        />
    </Toolbar>
</BlockControls>

このコードはpauseと playという名前でアイコンを読み込み、Buttonのクリックでis_animeを反転させて、アイコンの表示を切り替えるという例です。 この場合はicon属性にセットするのがオブジェクトなのでReactコンポーネント名の頭文字は小文字でも大丈夫です。

WordPressが用意してくれているアイコンについて

WordPressが用意しているアイコンはいわゆるダッシュアイコンで一覧のURLは以下のとおりです。 ダッシュアイコンのページ https://developer.wordpress.org/resource/dashicons/#database-import これを使うのは一番簡単で先ほどのButtonコンポーネントの例でいうと

<Button
    icon="sticky"
/>

とするだけです。指定する文字列は以下のスクリーンショットのとおりです。

Dashicons.png

その次に種類があるのは@wordpress/icons パッケージからインポートするアイコンです。 手順としては@wordpress/iconsのインストール

npm install @wordpress/icons --save

そしてインポート

import { cloud, color } from '@wordpress/icons';
<Button
    icon=cloud
/>

という手順を踏みます。でもこれだけだとアイコン名がわからないので、こちらのページから参照してください。

Icons .png

この赤丸で囲んだところが指定すべき名称です。

同一プラグインで複数ブロックを仕込む方法<WordPressのブロック制作>

ブロック開発していくにあたって、プラグインを使うのは一般的です。そのひな型を作成するツールが「wordpress/create-block」で、プラグインフォルダに移動して、ターミナルから npx @wordpress/create-block と入力します。 これで、簡単にプラグインのひな型ができるのはいいのですが、プラグイン一つにつき、出来上がるブロックは一つです。 これではプラグインをインストールする手間がかかって、インストールしてもらえないかもしれません。 「新しいブロックを作りました。」といって公開しても、それじゃ寂しいですよね。 だから、最低でも3つか4つはプラグインの中に仕込みたいわけですが、この方法が結構難しいんです。 ポイントが理解できていないと結構苦労します。 そこで、今回できるだけ具体的にその方法を説明したいと思います。

やっぱり最初は@wordpress/create-blockからスタート

このコマンドが基本となることは間違いありません。

npx @wordpress/create-block

出来上がったフォルダ構成を変更

最初は次のようになっています。

my-block //作成されたプラグインのディレクトリ
├── block.json  
├── build //ビルドで出力されるファイル(本番環境で使用するファイル)のディレクトリ
├── node_modules 
├── my-block.php  //PHP 側でブロックを登録するプラグインファイル
├── package-lock.json
├── package.json 
├── readme.txt
└── src  //開発用ディレクトリ(この中のファイルを編集)
    ├── edit.js  //edit 関数を記述するファイル
    ├── editor.scss  //エディター用スタイル
    ├── index.js  //ブロック用スクリプト(エントリーポイント)
    ├── save.js  //save 関数を記述するファイル
    └── style.scss 

これを次のようにします。

 my-block
├── build
├── node_modules 
├── my-block.php  
├── package-lock.json
├── package.json 
├── readme.txt
└── src
    └── blocks //新規作成
        └── new-block //新規作成
            ├── block.json //移動
            ├── edit.js //移動
            ├── editor.scss //移動
            ├── index.js //移動
            ├── save.js //移動
            └── style.scss //移動

お判りでしょうか。srcフォルダの下にblocksフォルダを作り、その下に複数のフォルダを増やしていこうというわけです。この例ではまずnew-blockというフォルダを作りました。

PHPファイル内のregister_block_typeを書き換え

PHPファイルには次のようにブロックを登録するPHPの関数が仕込んであり、initというWordPressのアクションフックでこの関数が実行されます。

function itmar_single_block_init() {
    register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'itmar_single_block_init' );

ここで__DIR__ . '/build'という部分は自分(PHPファイル)がいるディレクトリの中のbuildフォルダを指しています。そして、その中にblock.jsonというファイルがあれば、その内容に基づいてブロックを登録するという機能を果たします。 これを複数のフォルダに対してその数だけ実行すれば複数のブロックが一度に登録されるという仕組みをつくります。 どんなふうにコード書くかというと

function itmar_multi_block_init() {
    foreach (glob(plugin_dir_path(__FILE__) . 'build/blocks/*') as $block) {
        // Static block
        register_block_type($block);
    }
}
add_action( 'init', 'itmar_multi_block_init' );

glob(plugin_dir_path(__FILE__) . 'build/blocks/*')でbuild/blocks内のフォルダ名が配列で渡るので、それをループで回してブロック登録を行うというわけです。 なので、build/blocks内にフォルダを増やしていけば、登録されるブロックは増えていきます。 ここまで出来たらとりあえずmy-blockフォルダ直下に移動してターミナルから

npm start

を実行してブロックが登録されていることを確認してください。プラグインを有効にするのを忘れないでくださいね

@wordpress/create-block --no-pluginでブロックを増やしていく

ブロックを増やすのは、src/blocksフォルダに移動してターミナルを開き、次のようにコマンドを実行しますbuild/blocksではないですよ。間違わないようにしてください。

npx @wordpress/create-block --no-plugin

これで会話モードになるのでブロック名などを指定できます。フォルダ名をオプションにしてnpx @wordpress/create-block new-block2 --no-pluginなどとしない方がいいと思います。これだと会話モードにならずname spaceがデフォルトの「create-block」になります。普通は独自のものを使うと思います。特に複数のブロックになると他のブロックと合わせる必要があるのが通常なので、あとで手作業で変更する羽目になります。 仮にそうなったらblock.jsonだけでなく、editor.scssとstyle.scssのクラス名を変更するのを忘れてはいけません。これを忘れるとスタイルがあたらずハマってしまうことになります。 これでnpm startが効いていれば複数のブロックが登録されているのを確認できると思います。

WordPress .png

ちなみにこの段階でフォルダ構成は次のようになっています。

my-block
├── build
├── node_modules 
├── my-block.php  
├── package-lock.json
├── package.json 
├── readme.txt
└── src
    └── blocks 
        └── new-block
            ├── block.json  
            ├── edit.js 
            ├── editor.scss 
            ├── index.js
            ├── save.js
            └── style.scss
        └── new-block2 //npx @wordpress/create-block --no-pluginで新たに作成されたフォルダとファイル
            ├── block.json  
            ├── edit.js 
            ├── editor.scss 
            ├── index.js
            ├── save.js
            └── style.scss

私は最初 このページ で勉強させてもらいました。大変助かりました。非常にわかり安いんですが、それでもブロックの登録が確認できずハマったことが何度もありました。でも何回かやっているうちにハマらなくなったんで、初めての方は何度かやるつもりで長い目で見てやりましょう。

TwitterAPI v2に切り替えることになりました(TwitterOAuthでの移行)

TwitterAPIの有料化とv1.1の廃止

TwitterAPI v1.1がついに使えなくなりました。2023年2月頃から発表されていたいようですが、私は5月になって知りました。正直なところDeveloper Portalにはそんなこと何も書いていないかったし、ずっと半信半疑だったのです。でも、それは悪い情報から目を背けたかっただけでした。だって、今回の仕様変更で - FREE - BASIC - PRO

という3段階のプランができてFREEでは使えるエンドポイントはたったの3つです。 - POST /2/tweets - DELETE /2/tweets/:id - GET /2/users/me

これで何がつらいかというと、 GET /2/users/:id/tweets はエンドポイントから外れているということです。 要するにAPIを通じてできるのは、投稿だけで、閲覧に関してはユーザーのプロフィールだけですよということです。 自分のツイートのタイムラインも取れないということです。 さらに、投稿できるのも1日50ツイートまでかつ月1500ツイートまでと大幅に数量制限もかかりました。 それで問題があるなら上のランクのプランに格上げしてねということですが、その上のBASICからは有料で、BASICは月100$、PROは月5000$というんです。日本円にしたらBASICで月1万円以上、PROで月60万円以上になります。とてもフリーランスのWeb制作者が払える金額ではありません。 したがって、FREEでできる範囲に機能を限定せざるを得ません。それでも無料で利用できる枠を残してくれただけでも良しとせざるを得ませんかね。

そして、問題は残されたエンドポイント

  • POST /2/tweets
  • DELETE /2/tweets/:id

ですが、ご覧のとおり、これってAPIバージョンが2なんです。ですから、今までv1.1のエンドポイントをv2のエンドポイントに切り替えないといけません。これまで、移行期間でv1.1のエンドポイントで使えていたのですが、ついに使えなくなりました(私が2023/6/16に確認したということでいつから使えていなかったかは不明です。)。 今までできていた自動投稿が無情にもエラーを出してしまったのです。

いずれ手入れしなければいけなかったので、重い腰を上げる機会になりました。

v1.1とv2の比較は以下のサイトにあります。 https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/migrate しかし、わたしはTwitterOAuthというライブラリを使っているので、これだけ見てもよくわかりません。 それともう一つ非常に気になるのが、画像や動画付きのツイートはできるのかということです。

ということで、以下の3点が課題となりました。 TwitterOAuthを使うことを前提として

  1. v1.1およびv2でのツイートの方法の違いの把握
  2. 画像や動画付きのツイート
  3. v1.1およびv2でのツイートの削除方法の違いの把握

これらの課題を何とか克服することができたので、そのノウハウを情報提供させていただくことにします。同じ課題を抱えておられる方のお役に立てば幸いです。

v1.1およびv2でのツイートの方法の違いの把握

早速コードからお見せします。

v1.1のコード

require_once "vendor/autoload.php";

use Abraham\TwitterOAuth\TwitterOAuth;

$apiKey            = "your_api_key";
$apiSecret         = "your_api_secret";
$accessToken       = "your_access_token";
$accessTokenSecret = "your_access_token_secret";

$connection = new TwitterOAuth($apiKey, $apiSecret, $accessToken, $accessTokenSecret);
$result = $connection->post("statuses/update", ["status" => "API v1.1のテスト"]);

v2のコード

require_once "vendor/autoload.php";

use Abraham\TwitterOAuth\TwitterOAuth;

$apiKey            = "your_api_key";
$apiSecret         = "your_api_secret";
$accessToken       = "your_access_token";
$accessTokenSecret = "your_access_token_secret";

$connect = new TwitterOAuth($apiKey, $apiSecret, $accessToken, $accessTokenSecret);
$connect->setApiVersion("2");・・・⓵
$result = $connect->post("tweets", ["text" => "API v2のテスト"], true); # trueを忘れないように・・・⓶

ポイントは3つあります。 1. TwitterOAuthのオブジェクトを①でv2用にしておくこと。 2. ②のとおりエンドポイントがstatuses/updateからtweetsに変わり、POSTするオブジェクトのキーがstatusからtweetsに変わっていること。 3. POSTするデータの形式はJSONとなるので、POSTメソッドの第3引数をtrueにすること

画像や動画付きのツイート

これが今回の大きな課題でした。すこし、この手のツイートの仕組みを説明しておきます。

画像や動画付き投稿の仕組み.jpg

この図解のとおり②でAPIが必要です。このAPIはどうなっているのか。この情報がなかなか見つかりませんでした。 結論をいうと、このAPIはまだv2には存在していません。 こちらのページで確認できます。 https://developer.twitter.com/en/docs/twitter-api/migrate/twitter-api-endpoint-map

Twitter Develope.png

つまり、このエンドポイントはv.1.1のものを使います。したがって、先に説明した「1.TwitterOAuthを①でv2用にしておくこと。」は やってはいけません。 これをするとIDは返ってきません。 ということでコードはこのようになります。

v1.1、v2共通

require_once "vendor/autoload.php";

use Abraham\TwitterOAuth\TwitterOAuth;

$apiKey            = "your_api_key";
$apiSecret         = "your_api_secret";
$accessToken       = "your_access_token";
$accessTokenSecret = "your_access_token_secret";

$connect = new TwitterOAuth($apiKey, $apiSecret, $accessToken, $accessTokenSecret);
//画像や動画のアップロード
if($img_list){
    foreach($img_list as $img_url){
        //画像や動画のダウンロード
        $image = file_get_contents($img_url, false, stream_context_create($options));
        //一時保存場所
        $save_path = SNS_PLUGIN_PATH.'/img/temp'.$ext;
        //ファイルのダウンロードに成功した場合
        if(file_put_contents($save_path, $image)){
          $up_result = $connect->upload('media/upload', [
            'media'=>$save_path,
          ]);・・・⓵
          $media[]= $up_result->media_id_string;・・・⓶
        }
      }
    }
  }

v1.1

//続きです
//ツイートを実行
  
  $result = $connect->post('statuses/update', [
      'status' => $content,
      'media_ids' => implode(',', $media),・・・⓷
    ];);
}

v2

//続きです
//ツイートを実行
  $connect->setApiVersion('2');・・・⓸
  $result = $connect->post('tweets', [
      'text' => $content,
      'media' => [
        'media_ids' => $media,
      ]・・・⓹
    ];);
}

①が画像や動画をアップロードするAPIです。これはFREEでも使えました(ありがたかった)。 ②で無事にアップロードした画像や動画のIDが蓄積されました。この時、$connect(TwitterOAuthオブジェクト)はv1.1用になっています。 v1.1ではエンドポイントはstatuses/updateでテキストはstatusのキーをつけたオブジェクトで渡すのは先に説明したとおりですが、画像や動画のIDは③のようにmedia_idsというキーを持ち、カンマ区切りの文字列を値とするオブジェクトを渡していました。

v2では、ここからAPIの使用方法が変わります。 ④で$connect(TwitterOAuthオブジェクト)をv2用に切り替えます。 ⑤には2つのポイントがあります。 1. 画像や動画のIDの渡し方ですmediaというキーを持つオブジェクト内にmedia_idsというキーを持つオブジェクトを入れて渡すということ。 2. もう一つはmedia_idsというキーを持つオブジェクトには配列を渡すこと。

この2つです。 これを間違うと容赦なくエラーが返ります。 1日50回した使えないツイートを無駄に消費しないようにしましょう。

v1.1およびv2でのツイートの削除方法の違いの把握

これはエンドポイントとオブジェクトの渡し方がわかれば簡単です。コードを示します。 v1.1

require_once "vendor/autoload.php";

use Abraham\TwitterOAuth\TwitterOAuth;

$apiKey            = "your_api_key";
$apiSecret         = "your_api_secret";
$accessToken       = "your_access_token";
$accessTokenSecret = "your_access_token_secret";

$connect = new TwitterOAuth($apiKey, $apiSecret, $accessToken, $accessTokenSecret);

$id = 'ツイートID';
$statuses = $connect->post("statuses/destroy/".$id, array());・・・⓵

v2

require_once "vendor/autoload.php";

use Abraham\TwitterOAuth\TwitterOAuth;

$apiKey            = "your_api_key";
$apiSecret         = "your_api_secret";
$accessToken       = "your_access_token";
$accessTokenSecret = "your_access_token_secret";

$connect = new TwitterOAuth($apiKey, $apiSecret, $accessToken, $accessTokenSecret);

$id = 'ツイートID';
$connect->setApiVersion('2');・・・⓶
$response = $connect->delete('tweets/'.$id);・・・⓷

①のようにv1.1では、エンドポイントが'statuses/destroy/'.idでした。 これに対して、v2の場合は③のようにエンドポイントが'tweets/'.idで、postメソッドでなく、deleteメソッドを使います。この手法がなかなか見つからず苦労しました。下記のサイトで助かりました。他のエンドポイントの使い方もまとめてあるのできっと参考になると思います。 https://diy-programming.site/twitter/twitter-api-v2/#toc7 $connect(TwitterOAuthオブジェクト)を②でv2用にセットするのを忘れないでください。

ということで何とかv1.1からv2への移行作業が終わりました。私のようにTwitterAPIでアプリを作っている方はきっと大変だろうと思い、役立ててほしいと思って執筆しました。このブログがお役に立てば幸いです。

プログラマ泣かせのブラウザキャシュ

直したはずのCSSが当たっていない

CSSの修正って結構面倒ですよね。もちろん最近はCSSを直接編集せずSASSの技術を使うことが多いと思います。それでも、細かい数字を調整するのは苦労します。 それでやっとできたと思って、確認すると何も変わっていない。これほどガックリすることはありません。 SASSのコンパイルがうまくいってないのか?CSSファイルを直接エディタで確認しても、ちゃんとコンパイルされています。 実際、これで1時間ぐらい悩むのことは何度かありました。

そんな時は強制リロード

まず、試してもらいたのがこの強制リロードです。それってなにかというと、修正したCSSファイルやJSファイルを強制的に再読み込みすることです。

このブログをご覧になっている方々にとっては当然のこととは思いますが、あえて説明しておくとブラウザは常に画面に表示すべき内容(専門的な表現だとレンダリングすべきDOM要素)を常に全てサーバーから読み込んでいるわけではありません。DOM要素以外のCSSファイルやJSファイル、イメージファイルなども同様で、一度読み込んだデータはキャッシュと呼ばれるメモリに保存され、変化がないと判断するとそれを使いまわすのです。これは重要な機能でパフォーマンスの維持には欠かせません。この機能があるから画面がサクサク切り替わるのです。

しかし、これがCSSやJSを修正したのに直っていないという原因になるのです。ですから、Web制作に携わる方々はそのことを常に念頭に置いて、クライアント様にはそのことを伝えないといけません。

「そんなのリロードすれば勝手にやってくれてんじゃないの?」「再起動すればさすがに再読み込みするんでしょ。」なんて思われがちでしょうが、そうはいきません。下記の操作以外では、強制リロードと同じ効果は得られません。

でも、実際には強制リロードって難しい

キーボード操作で簡単に実行できる強制リロードですが、一般ユーザーはその方法をほぼ知らないでしょう。そのため、これを素人のクライアント様に伝えるのはかなり困難です。

また、パソコンは上記の方法で簡単に操作できますが、スマホの場合はそんなキー操作ができません。かなり面倒な操作が必要で、私などなんどやっても覚えられません。この方法を説明しだすとそれだけで一記事になるので、ここではしません。参考となるURLを張っておきますのでそちらをご覧ください。

スーパーリロードのやり方は?スマホやiPhoneとブラウザ別で紹介

ということで結論です

ご存じのとおり、ブラウザは次のようなコードでcssファイルやjsファイルを読み込んでいます。

link rel="stylesheet" href="./css/example.css

先ほどブラウザは、「前に読み込んだファイルに変化がないと判断するとそれを使いまわす」と説明しましたが、このようにファイル名が固定されているとファイル名が変わらない限り、過去に読み込んだファイルと判断してしまうのです。 ですが、逆にいうとファイル名が変われば再度読み込みを行います。つまり、再読み込みの際にファイル名が変わっていればよいということです。その方法は以下のとおりです。

link rel="stylesheet" href="./css/example.css?&lt;?php echo date('YmdHis')

つまり、ファイル名のあとに?'.date('YmdHis')というスクリプトをつけてファイル名をかえるというわけです。date('YmdHis')は時間を返すPHPの関数です。したがって、このスクリプトはブラウザに読み込んだ時間付のファイル名を渡すことになり、違うファイル名になるため、過去に読み込んだことがあるという判断はできないことになります。これで常に新しいCSSが読み込まれるという仕組みが出来上がります。 しかし、この方法はPHPスクリプトが機能することが前提なので、静的なHTMLファイルでは実現できません。また、どんなファイルにも、この設定をしてしまうとキャッシュ機能が失われるのでmp4の動画ファイルのような大容量のファイルに、この手法を適用するのはまずいと思います。そんなファイルについては、やはり強制リロードによるしかないと考えます。 CSSやJSはいくら長いといっても1MBに達することはないでしょう。また、これらのファイルはバージョンアップの度に変わるもので、不特定多数のユーザーが利用するプラグインなどでは強制リロードの説明は困難ですから、利用価値は非常に高いと思います。 ということで最後にWordpressサイトでよく使われるスクリプトの書き方を紹介します。テーマならfunction.phpに、プラグインならエントリポイントとなるPHPファイルに埋め込んで使用してみてください。

function my_script_init()
{
  wp_enqueue_style('my', get_template_directory_uri() . '/css/style.css?'.date('YmdHis'), array(), '1.0.0', 'all');
}
add_action('wp_enqueue_scripts', 'my_script_init');
 ```