WordPressでは、oEmbed という方法を使って Twitter, youtube, Vimeo, 等の画像や動画を簡単に記事に挿入できるようになっています。これが WordPress5.8 からはブロックウィジェットへも埋め込むことが出来るようになりました
でも、WP5.8時点では、ちょっと問題も残っています。あまり多用するとデータベースの肥大化やパフォーマンス低下につながる可能性もあり、そのへんの対策を含めて紹介していきたいと思います 😄
oEmbed
oEmbed に対応している埋め込みは、埋め込む動画等のURLをコピーして、記事作成時にビジュアルモード編集画面にそのまま貼り付けて挿入するだけで埋め込まれた動画等が表示されます
裏側ではプログラムが一生懸命に動作しているのですが、表側では何事もなかったようにスマートに動画や画像を表示してくれるクールな機能です (^^)
仕組みを簡単に紹介すると、oEmbed という方法で各サービスの提供元にURLを指定して埋め込むための HTMLコードを取得して、そのコードを記事毎のポストIDとURLを紐付けて、データベースの wp_postmeta テーブルに保存しています
記事がアクセスされた時には、保存されている HTMLコードをデータベースから取得してブラウザへ出力するだけで、そのHTMLコードにより各サービス元の動画や画像が記事に埋め込まれて表示されます。(多くのサービスで iframe により埋め込みが行われます)
ブロックウィジェットでの問題?
先ほど簡単に oEmbed 埋め込みの仕組みを紹介しましたが、WordPressでは、埋め込みデータの保存は、ポストIDとURLを紐付けて行われていることを紹介しました
でも、ちょっと待って
ウィジェットは、各記事とは独立した存在です。そこで無理やりウィジェットに oEmbed 埋め込みのURLを書き込むと、指定されたページを表示する時に、同時にそのページで表示されるウィジェットに指定されているURLをそのページのポストIDを使って処理されることになります
どういうことかと言うと、もしもあなたのサイトに100記事が公開されていたら、ブロックウィジェットに埋め込んだデータがポストID毎に作られ100個作成されるってことです 😱
とても無駄なことで、せっかくのスマートな機能が台無しです
表示さえ出来れば問題ないよという方もいるかもしれませんが、データベースの肥大化やパフォーマンスに影響するかも知れません
実際こんな細かなこと気にするのは、プログラマーぐらいかな 😋
そこで、その辺りの問題に対応した ブロックウィジェット埋め込み用のカスタマイズを紹介します
肝は、ブロックウィジェットの埋め込みブロックを WordPress のコアが処理する前にフックして、ポストIDに紐付けずに別のデータベーステーブルを使って先に処理して保存することです
表示時もコアより先に別に保存したテーブルから読み出して表示すればよいので、サイトに何百という記事があっても無駄なデータを保存することはありません
カスタマイズコードサンプル
サンプルコードを紹介します
widget_block_content フィルターにフックして、コアで行っている oEmbed 処理に準拠しています。データの保存先は、transient api を使用して wp_options に保存しても良かったのですが、保存データのクリア処理等の操作をやりやすくするために独自テーブル wp_celtis_transient を作成して使用しています
// widget_block_content filter function widget_block_oembed_result( $content, $instance, $block) { $content = preg_replace_callback( '#(<!\-\-\swp:embed.+?\-\->)(.+?)(<!\-\-\s/wp:embed\s\-\->)#su', function($matches) use(&$content) { $ehtml = $matches[0]; if ( function_exists( '_wp_oembed_get_object' ) ){ if ( preg_match( '|^(\s*)(https?://[^\s<>"]+)(\s*)$|im', $ehtml, $murl ) ) { global $cp_transient; $url = $murl[2]; $url = str_replace( '&', '&', $url ); $attr = wp_parse_args( array(), wp_embed_defaults( $url ) ); $wp_oembed = _wp_oembed_get_object(); $provider = $wp_oembed->get_provider( $url, array( 'discover' => false ) ); $keysuffix = md5( $url . serialize( $attr ) ); $cache = false; $cache_age = WEEK_IN_SECONDS; if(!empty($provider)){ $opname = '_oembed_' . $keysuffix; $gval = $cp_transient->get_cache( $opname ); $cache = ($gval[0] === false)? false : $gval[1]->value; if (empty($cache) || ('{{unknown}}' === $cache && is_user_logged_in()) ) { $cache = wp_oembed_get( $url, $attr ); if ( empty($cache) ) { $cache = '{{unknown}}'; } $cp_transient->set_cache( $opname, $cache, $cache_age); } } if (!empty($cache)) { if ( '{{unknown}}' === $cache ) { $unembedurl = apply_filters( 'embed_maybe_make_link', $url, $url ); $ehtml = str_replace($url, $unembedurl, $matches[2]); } else { $dummy_id = 0; $ehtml = str_replace($url, $cache, $matches[2]); $ehtml = apply_filters( 'embed_oembed_html', $ehtml, $url, $attr, $dummy_id ); } } } } return $ehtml; }, $content); return $content; } add_filter( 'widget_block_content', array( $this, 'widget_block_oembed_result' ), 6, 3 );
これは Celtispack Plugin Ver4.8.1 に実装した処理の一部を抜粋したものです
動作を試して見たい場合は、Celtispack ver4.8.1 をインストールして、oEmbed extend モジュールを有効化してみてください
以上、ブロックウィジェットの埋め込みブロックに対する問題点と対処方法について紹介しました