かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

WordPress ネストしてるテンプレートに変数を渡したい

普段 React とかでフロントエンドをやってます。たまに WordPress の案件があると PHP タグが大量に埋め込まれたテンプレートめっちゃ見通し悪いな…となりついテンプレートを分割しまくってしまいます。その際に React で言う所の props みたいに inclide 元のファイルから変数を渡したい時に 2回くらいググってしまったのでメモして残しておきます。(どうせ忘れる自分用のメモ)

環境

1. global な変数で渡してしまう

分割したファイルを include すると読み込み位置と同じスコープになるので、読み込み元の変数がそのまま使える

<?php // 親テンプレート
$foo = 'MY STARWAY';
include_once( __DIR__ . '/path/to/template.php');
<?php // 読み込まれるファイル
echo $foo;
// => "MY STARWAY"

正直 include したファイルで突然定義してない変数が出てきて見通しが良くないように感じる…
他にも同じスコープになるので読み込んだファイルで変数を変更すると、予期しないバグを発生させる温床にもなりうる

<?php // 読み込まれるファイル
$foo = 'TRAVEL RIBBON';
<?php // 親テンプレート
$foo = 'MY STARWAY';
include_once( __DIR__ . '/path/to/template.php');
echo $foo;
// => "TRAVEL RIBBON"

2. get_template_part の第三引数で変数を渡す

get_template_part( string $slug, string $name = null, array $args = array() ): void|false
  • 第三引数に配列形式で値を渡せる
  • 渡した値は $args という配列に格納される
<?php // 親テンプレート
$foo = 'MY STARWAY';
get_template_part('path/to/template', null, ['foo' => $foo]);
<?php // 読み込まれるファイル
$my_foo = $args['foo'] ?: '';
echo $my_foo;
// => "MY STARWAY"

get_template_part で渡した変数は別参照になるので安全

<?php // 読み込まれるファイル
$my_val = $args['foo'] ?: 'default';
$unit = $args['unit'] ?: [];

$foo = 'TRAVEL RIBBON';
$unit[] = ['akari'];
<?php // 親テンプレート
$foo = 'MY STARWAY';
$unit = ['ichigo', 'aoi', 'ran'];
get_template_part('path/to/template', null, ['foo' => $foo, 'unit' => $unit]);
echo $foo;
// => "MY STARWAY"
var_dump($unit);
// array(3) { [0]=> "ichigo" [1]=> "aoi" [2]=> "ran" }

まとめ

WordPress のテーマでテンプレートを分割して分割したテンプレートに呼び出し元から変数を渡したい場合は公式の get_template_part を使うのがパスの問題も無く意図しない変数の上書きもないので良さそうです!


[参考]

アイカツ!未来へのSTARWAY 全人類見てください!

WordPress OGP 情報を自前で出力してみる

WordPress で OGP の情報を出力するには All in One SEO などのプラグインを使えば実現できるのですが、WordPress からしばらく離れていたこともあり WordPress 独特のプラグインGitHub にない場合も多くスター数やどれが現段階でのベストプラクティスなの判断できない・セキュリティの問題がどうなっているのか判らないという事があり素振りも兼ねて自前で実装してみたのでメモ

OGP に必要なメタタグ

<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article#">
<title>ページのタイトル</title>
<meta property="og:site_name" content="サイト名" />
<meta property="og:title" content="ページのタイトル" />
<meta property="og:type" content="ページの種類" />
<meta property="og:url" content="ページのURL" />
<meta property="og:image" content="サムネイル画像のURL" />
<meta property="og:description" content="ページのディスクリプション" />
<meta property="fb:app_id" content="App-ID(15文字の半角数字)" /> <!-- for Facebook -->
<meta name="twitter:card" content="Twitterカードの種類" /> <!-- for Twitter -->

cf. Facebook・TwitterのOGP設定方法まとめ|ferret

仕様

  • WordPress: v6.0
  • og:title … 現在のページのタイトル
  • og:typewebsite, 投稿の場合は article にする
  • og:url … 現在のページの URL
  • og:image … 投稿、固定ページでサムネイルがあればサムネイル。なければデフォルト画像。
  • og:site_name … 固定値 (サイト名)
  • og:description … 抜粋があれば抜粋。なければ本文から生成。それもなければデフォルト値
  • fb:app_idFacebook のアプリID なので固定値
  • twitter:card … 投稿の時は summary, 固定ページの場合は summary_large_image

※ コードを見やすくするために冗長に書いています

og:title 現在のページのタイトルを取得する

ページのタイトルを return する wp_get_document_title() を使って取得する
固定値のサイト名は get_bloginfo('name')管理画面 > 一般 > サイトのタイトル に指定されている値にする

<?php // functions.php
function mt_ogp() {
  $siteName = get_bloginfo('name');
  // ページタイトル
  $title = wp_get_document_title();

$ogp = <<< EOM
<meta property="og:site_name" content="{$siteName}" />
<meta property="og:title" content="{$title}" />
EOM;
  
  echo $ogp;
}

タイトル自体のカスタマイズは pre_get_document_title のようなフィルターを使うことで head 内の title と og:title を同じにすることができる

cf.

og:type ページのタイプ

website, 投稿の場合は article にする

is_singular() は投稿ページ以外に固定ページ is_page(), 添付ファイルページ is_attachment() を含むので is_single() を使うのが良さそう

<?php // functions.php
function mt_ogp() {
  $siteName = get_bloginfo('name');
  // ページタイトル
  $title = wp_get_document_title();
  // ページタイプ
  $pageType = 'website';
  if ( is_single() ) {
    $pageType = 'article';
  }

$ogp = <<< EOM
<meta property="og:site_name" content="{$siteName}" />
<meta property="og:title" content="{$title}" />
<meta name="og:type" content="{$ogpType}">
EOM;
  
  echo $ogp;
}

og:url 現在のページの URL

WordPress の URL は global の $post などを使って取得することができるが、投稿がない場合やカテゴリーの投稿が 0 件の時 $postNULL になったり、アーカイブページの $postアーカイブに含まれる最初の投稿だったりで分岐や条件分けがかなり複雑になってしまうので $_SERVER を使って現在の URL を取得するようにした

<?php // functions.php
function mt_ogp() {
  $siteName = get_bloginfo('name');
  // ページタイトル
  $title = wp_get_document_title();
  // ページタイプ
  $pageType = 'website';
  if ( is_single() ) {
    $pageType = 'article';
  }
  // 現在の URL
  $permalink = (is_ssl() ? 'https' : 'http') . '://' . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];

$ogp = <<< EOM
<meta property="og:site_name" content="{$siteName}" />
<meta property="og:title" content="{$title}" />
<meta name="og:type" content="{$ogpType}">
<meta property="og:url" content="{$permalink}" />
EOM;
  
  echo $ogp;
}

og:image

投稿、固定ページでサムネイルがあればサムネイル。なければデフォルト画像。

サムネイルを設定できるページは is_singular() (is_single() + is_page() + is_attachment()) なので、これが true の時にサムネイルの有無をチェックする
サムネイル画像の URL は wp_get_attachment_image_src($attachment_id, $size); で取得できる。デフォルトでは thumbnail が取得されるので OGP なら largefull を指定すれば良さそう

<?php // functions.php
define('OGP_IMAGE', get_home_url() . "/ogp.png");
function mt_ogp() {
  global $post;
  $siteName = get_bloginfo('name');
  // ページタイトル
  $title = wp_get_document_title();
  // ページタイプ
  $pageType = 'website';
  if ( is_single() ) {
    $pageType = 'article';
  }
  // 現在の URL
  $permalink = (is_ssl() ? 'https' : 'http') . '://' . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
  // OGP Image
  $ogpImage = OGP_IMAGE;
  if ( is_singular() ) {
    $thumbnail = wp_get_attachment_image_src(
      get_post_thumbnail_id($post->ID), 'full'
    );
    $ogpImage = $thumbnail[0] ?: OGP_IMAGE;
  }

$ogp = <<< EOM
<meta property="og:site_name" content="{$siteName}" />
<meta property="og:title" content="{$title}" />
<meta name="og:type" content="{$ogpType}">
<meta property="og:url" content="{$permalink}" />
<meta property="og:image" content="{$ogpImage}" />
EOM;
  
  echo $ogp;
}

cf. WordPress アップロードした画像を width, height 属性無しで取得したい - かもメモ

og:description ディスクリプション

抜粋があれば抜粋。なければ本文から生成。それもなければデフォルト値

抜粋は global オブジェクトの $post から $post->post_excerpt, 本文は $post->post_content で取得できる。本文の場合はタグの除去を行う必要がある
アーカイブページや 404 ページ、検索結果などはデフォルト値を返すようにする

<?php // functions.php
define("OGP_IMAGE", get_home_url() . "/ogp.png");
define("DESCRIPTION", "デフォルトのディスクリプション");
// $post から description を返す関数
function get_my_descriptiom_by_post($post, $limit = 140, $more = '...') {
  $summary = $post->post_excerpt;
  if ( empty($summary) ) {
    $content = apply_filters('the_content', $post->post_content);
    // タグの除去
    $summary = strip_tags($content);
    $summary = str_replace(array("\r\n","\n","\r"), ''
  }

  if ( empty($summary) ) { return NULL; }

  $description = wp_trim_words($summary, $limit, $more);
  return $description; 
}

function mt_ogp() {
  global $post;
  $siteName = get_bloginfo('name');
  // ページタイトル
  $title = wp_get_document_title();
  // ページタイプ
  $pageType = 'website';
  if ( is_single() ) {
    $pageType = 'article';
  }
  // 現在の URL
  $permalink = (is_ssl() ? 'https' : 'http') . '://' . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
  // OGP Image
  $ogpImage = OGP_IMAGE;
  if ( is_singular() ) {
    $thumbnail = wp_get_attachment_image_src(
      get_post_thumbnail_id($post->ID), 'full'
    );
    $ogpImage = $thumbnail[0] ?: OGP_IMAGE;
  }
  // description
  $description = DESCRIPTION;
  if ( is_singular() ) {
    $description = get_my_descriptiom_by_post($post);
  }
  if ( empty($description) || empty($post) || is_404() ) {
    $description = DESCRIPTION;  
  }

$ogp = <<< EOM
<meta property="og:site_name" content="{$siteName}" />
<meta property="og:title" content="{$title}" />
<meta name="og:type" content="{$ogpType}">
<meta property="og:url" content="{$permalink}" />
<meta property="og:image" content="{$ogpImage}" />
<meta property="og:description" content="{$description}" />
EOM;
  
  echo $ogp;
}

Facebook fb:app_id, Twitter twitter:card 用の設定

fb:app_idFacebook で取得するアプリの ID なので固定値を使えば OK twitter:card は 投稿の時は summary, 固定ページの場合は summary_large_image にする

<?php // functions.php
define("OGP_IMAGE", get_home_url() . "/ogp.png");
define("DESCRIPTION", "デフォルトのディスクリプション");
define("FACEBOOK_APP_ID", "APP ID");
// 略
function mt_ogp() {
  global $post;
  $siteName = get_bloginfo('name');
  // ページタイトル
  $title = wp_get_document_title();
  // ページタイプ
  $pageType = 'website';
  if ( is_single() ) {
    $pageType = 'article';
  }
  // 現在の URL
  $permalink = (is_ssl() ? 'https' : 'http') . '://' . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
  // OGP Image
  $ogpImage = OGP_IMAGE;
  if ( is_singular() ) {
    $thumbnail = wp_get_attachment_image_src(
      get_post_thumbnail_id($post->ID), 'full'
    );
    $ogpImage = $thumbnail[0] ?: OGP_IMAGE;
  }
  // description
  $description = DESCRIPTION;
  if ( is_singular() ) {
    $description = get_my_descriptiom_by_post($post);
  }
  if ( empty($description) || empty($post) || is_404() ) {
    $description = DESCRIPTION;  
  }
  // Facebook App ID
  $fbAppID =  FACEBOOK_APP_ID;
  // Twitter card
  $cardType = 'summary_large_image';
  if ( is_single() ) {
    $cardType = 'summary';
  }

$ogp = <<< EOM
<meta property="og:site_name" content="{$siteName}" />
<meta property="og:title" content="{$title}" />
<meta name="og:type" content="{$ogpType}">
<meta property="og:url" content="{$permalink}" />
<meta property="og:image" content="{$ogpImage}" />
<meta property="og:description" content="{$description}" />
<meta property="fb:app_id" content={$fbAppID} />
<meta name="twitter:card" content="{$cardType}" />
EOM;
  
  echo $ogp;
}

cf.

所管

なんとなくいい感じに OGP のメタタグを出力できるようになりました!
WordPress の全部を functions.php にマージして実行する仕組みにいまいち慣れない…


[参考]

おじP… ネタ切れ…

WordPress アップロードした画像を width, height 属性無しで取得したい

WordPress でアップロードした画像 (thumbnail) を img タグで取得する the_post_thumbnail() には自動的に width, height 属性がついてくるので場合によっては使いづらいときがあります。

1. post_thumbnail_html フィルターで the_post_thumbnail の出力内容を置き換える

<?php // functions.php
add_filter( 'post_thumbnail_html', 'remove_thumbnail_dimensions');

function remove_thumbnail_dimensions( $html, $post_id, $post_image_id, $size, $att ) {
    $html = preg_replace( '/(width|height)=\"\d*\"\s/', "", $html );
    return $html;
}

cf. How do you remove hard coded thumbnail image dimensions? - WordPress Development Stack Exchange

常に width, height 属性が不要であれば filter を使うのが手っ取り早そうです

2. wp_get_attachment_image_src を使って画像の URL を取得する

img タグを自分で作ってしまうなら画像の URL が取得できれば問題がない

wp_get_attachment_image_src(
  int $attachment_id, string|int[] $size = 'thumbnail', bool $icon = false
): array|false

cf. wp_get_attachment_image_src() | Function | WordPress Developer Resources

サムネイルの attachment_idget_post_thumbnail_id(int|WP_Post $post = null): int|false で取得できる

<?php
$thumbnail = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), $size );
$thumbnail = $thumbnail[0] ?: 'placeholder.jpg';
$title = get_the_title( $post->ID );

echo '<img src="' . $thumbnail . '" alt="' . esc_attr($title) . '" />';

画像の URL を取得してしまえばタグは自由にできますね!

おわり
WordPress のこと本当に何も覚えてない…


[参考]

2030年:すべてが「加速」する世界に備えよ 面白かったです!