WordPressのデフォルトブログカードってなんかそっけないし、SP時も場所取るな。。。

それに他サイトの情報も取得できないし何かと不便。。。

ということで、今回は他サイトの情報取得にも対応したブログカードを割りとガチで作ってみたいと思います。

といっても他サイトに迷惑がかかってはいけないので、画像の直リンクは避けて、できるだけミニマムに取得してきます。

また、ブログカードはデザインしやすいように、関数ファイルとテンプレートファイルで分けます。

尚、ブログカード自体はショートコードで内容を設定できるようにしていきます。

それではいってみましょう!

# この記事で解決すること

  • デザインの自由度が高いブログカードを作成することができる
  • 外部サイトのURLに対応したブログカードを作成することができる

 

手順

  1. 事前準備
    • 前提
    • フォルダ構成の確認
    • 仕様の確認
  2. 初期設定
    • メインの変数について
    • テンプレートURLの判別
    • 外部サイトの情報取得
  3. メイン関数の処理
    • 初期変数の設定
    • ショートコード用のattributeの設定
    • ドメインの判別
    • サイトアイコン・サイト名の処理
    • idを取得
    • タイトル・説明文の処理
    • 画像の処理
    • データの整理
    • templateにargumentを渡す
  4. テンプレートの実装
    • phpファイルの処理
    • cssファイルの処理
  5. ショートコードの呼び出し設定
    • jsの処理
    • phpの処理
  6. 実装

事前準備

前提

今回はWordpressに機能を追加しますので、Wordpressを使用している必要があります。

また、1行だけですがfunctions.phpを編集しますので、それができる方が対象となります。

フォルダ構成の確認

  • theme_name
    • functions
      • addBlogCard
        • assets
          • no-image.png
          • site-icon.png
          • youtube.png(救済)
        • addBlogCard.php(メイン)
        • addBlogCard-template.php(UI)
        • addBlogCard.css(UI)
        • addBlogCard-tinymce.php(shortcode)
        • addBlogCard.js(shortcode)
    • functions.php

仕様の確認

最終形態のサンプルとしては以下の内容となります。

同一ドメイン

【投稿全件取得編】WP REST APIで投稿を非同期で全件取得した話
【投稿全件取得編】WPRESTAPIで投稿を非同期で全件取得した話
WPRESTAPIは、全件取得しようと思ってもper_pageが100までしかきいてくれないので、何も考えなければ100件しか取得することができません。 そのため、全件取得しようと思ったら、下記...

外部URL

【プラグイン無】WordPressで外部ドメインに対応したブログカードを作った話
Wordpressにプラグイン無しで、「外部ドメインのサイトにも対応したブログカード」を追加する関数の作り方を紹介しています。

極力ファイルは増やしたくないので、今回は外部URLの情報をcurlとxpathで取得します。

また、外部URLの場合はサイトアイコンとサイト名、アイキャッチ画像は以下の理由から取得しません。
※youtubeなどのよくリンクさせるであろうサイトのために救済作を用意しております。

サイトアイコン, アイキャッチ画像

  • 画像直リンクになるため
  • 自サバに画像を保存することはできるがそこまでの価値を感じないため

サイト名

  • サイトによっては設定していないため

また、ショートコードは下記の3パターン用意します。([]は省いてます)

// 簡易
blog_card url=""

// カテゴリー等一覧(タクソノミー用)
blog_card url="URL" tax="taxonomy_name" id="term_id"

// フル(外部サイト・詳細設定用)
blog_card url="URL" title="タイトル" excerpt="説明" thumb="media_id"

初期設定

メインの変数について

今回ショートコードから取得するattributeは下記のとおりです。

name remark
url 表示させるページのURL
title タイトルを自分で設定したい場合に使用
excerpt 抜粋文を自分で設定したい場合に使用
thumb 表示アイキャッチ画像を自分で設定したい場合に
メディアのIDを設定して使用
tax カテゴリー一覧などのタクソノミーページを取得する際に使用
id タクソノミーページを取得する際にterm_idを設定

テンプレートURLの判別

WordPressを使用している方の中には子テーマを使用している方もいますので、その処理を書いておきます。

参照先はaddBlogCard内部のみなのでaddBlogCardまで書いておきます。

addBlogCard.php

function addBlogCard_get_template_url() {
  if(is_child_theme()) {
    return get_stylesheet_directory_uri().'/functions/addBlogCard';
  } else {
    return get_template_directory_uri().'/functions/addBlogCard';
  }
}

外部サイトの情報取得

外部サイトの情報を取得しないといけないことがわかっていますので、先に取得しておきましょう。

addBlogCard.php

function addBlogCard_get_data( $url ) {
  $headers = array(
    "HTTP/1.0",
    "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language:ja,en-US;q=0.9,en;q=0.8",
    "Connection:keep-alive",
    "User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36"
  );
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  $result = curl_exec($ch);
  curl_close($ch);
  $dom = new DOMDocument();
  $html = mb_convert_encoding($result, "HTML-ENTITIES", "auto");
  @$dom -> loadHTML($html);
  $xpath = new DOMXPath($dom);
  $xpath -> registerNamespace("php", "http://php.net/xpath");
  $xpath -> registerPHPFunctions();
  return $xpath;
}

header情報は何でも良いので、このサイトの情報からご自身の環境に差し替えて使用してください。

「説明すると長くなる」+「古い技術」ということで、curlxpathに関してはあえて説明しません。

また機会があれば別記事で説明します。

メイン関数の処理

メインの関数はaddBlogCard()とします。

addBlogCard.php

function addBlogCard($atts) {
}

$attsにショートコードのurl="hoge"とかが入ってくると思ってください。

初期変数の設定

画像の大きさやアイコンのURLなどはファイルの上の方に書いておいたほうが編集に迷わないので、グローバル関数にしてaddBlogCardに呼び出します。

addBlogCard.php

$img_width  = 90;     // アイキャッチ画像サイズの幅指定
$img_height = 90;     // アイキャッチ画像サイズの高さ指定
$length     = 100;    // post_excerptがなかった場合の文字数
$site_icon  = addBlogCard_get_template_url().'/assets/site-icon.png';  // サイトアイコンの画像URLを指定
$no_image   = addBlogCard_get_template_url().'/assets/no-image.png';   // アイキャッチ画像がない場合の画像URLを指定

function addBlogCard($atts) {
  global $img_width, $img_height, $site_icon, $no_image;
}

$lengthに関してはテストしていたサイトが、post_excerptがなかったので入れています。

それ以外は見ての通りです。

ショートコード用のattributeの設定

では、受け取ったurl="hoge"とかを配列に入れます。

addBlogCard.php: addBlogCard

extract(shortcode_atts(array(
  'url' => "",
  'title' => "",
  'excerpt' => "",
  'thumb' => "",
  // taxonomy用
  'tax' => "",
  'id' => ""
), $atts));

extractを使用することで'url' => ""$urlに変わってくれます。

extractshortcode_attsの組み合わせは非推奨なのですが、
すごく便利なので重要な情報を扱う場合以外は使用しても大丈夫だと思っています。
※個人的見解です

これでショートコードから受け取ったattributeを変数として受け取ることができました。

ドメインの判別

URLが内部のものか、外部のものかを判別していきます。

addBlogCard.php: addBlogCard

$my_site = stristr($url, $_SERVER['HTTP_HOST']);

$_SERVER['HTTP_HOST']

表示しているページのドメイン(ex: brandnew.work)

$url

ショートコードから受け取ったURL

stristr(hoge, fuga)

大文字小文字を区別せずhogeからfugaを検索

stristrの返り値は、文字列があればhogeをそのまま、なければfalseとなります。

これで、内部URLか外部URLかの判別ができました。

サイトアイコン・サイト名の処理

前述の通り、外部サイトの場合はサイトアイコンとサイト名を取得しませんので、それも踏まえて処理していきます。

addBlogCard.php: addBlogCard

if($my_site !== false) {
  // 同サイトの場合
  $site_name = get_bloginfo( 'name' );
  $site = isset($site_icon) ? "<img src='{$site_icon}' width='16' height='16' alt='{$site_name}'>{$site_name}" : "";

} elseif(stristr($url, 'youtube.com') || stristr($url, 'youtu.be')) {
  // youtubeの場合
  $site_name = 'youtube';
  $site = "<img src='".addBlogCard_get_template_url()."/assets/youtube.png' width='16' height='16' alt='{$site_name}'>{$site_name}";

}

7行目~

youtubeなどのよく使用するサイトは救済措置としてサイト名とサイトアイコンを設定しています

idを取得

次項でtitleexcerptを取得しますので、事前にidを取得しておきます。
※前項のコードと合体させて問題ないです。

addBlogCard.php: addBlogCard

if($my_site !== false) {
  $id = empty($id) ? url_to_postid($url) : $id;
}

「$idがショートコード側で設定されていない場合」=「タクソノミー一覧ではない場合」は、url_to_postididを取得します。

タイトル・説明文の処理

addBlogCard.php

function addBlogCard_excerpt($post_id) {
  global $length;
  $post = get_post($post_id);

  if($post->post_excerpt) {
    // excerptが入力されている場合
    $return = $post->post_excerpt;

  } else {
    // excerptが入力されていない場合
    $return = $post->post_content;
    $return = addBlogCard_flat_text($return);

    if(mb_strlen($return) > $length) {
      // $lengthよりも文字数が多い場合の対処
      $return = mb_substr($return, 0, $length);
      $return .= '...';
    }
  }
  return $return;
}

2行目

一番最初に定義した$lengthを呼び出します。

また、抜粋文自体が「決められた文字数の中で内容をわかりやすく説明する」という概念なので、
この$lengthは抜粋文が設定されていない場合にしか機能しません。

15~19行目

$returnの長さと$lengthを比較して、$lengthよりも$return文字数が多い場合に、
mb_substrで文字数を$lengthの長さにあわせています。

抜粋文後の「…」が必要ない場合は18行目を削除してください。

上記を用いて表示するタイトルと説明文を取得していきます。

addBlogCard.php: addBlogCard

if( $my_site === false ) {
  // youtube以外の外部サイトの場合 ------------------------------------------------------------
  // 外部サイトの情報取得
  $xpath = addBlogCard_get_data($url);

  // title取得
  if(empty($title)) {
    $title = $xpath->query('//head/title[1]') ? $xpath->query('//head/title[1]')->item(0)->nodeValue : "";
  }
  // excerpt取得
  if(empty($excerpt)) {
    if($xpath->query('//meta[@property="og:description"]')[0]) {
      $excerpt = $xpath->query('//meta[@property="og:description"]/@content')[0]->textContent;
    } else if($xpath->query('//meta[@name="description"]')[0]) {
      $excerpt = $xpath->query('//meta[@name="description"]/@content')[0]->textContent;
    } else {
      $excerpt = "";
    }
  }

} else if(empty($tax)) {
  // タクソノミー一覧ではない場合の取得 ------------------------------------------------------------
  $title = empty($title) ? get_the_title($id) : $title;
  $excerpt = empty($excerpt) ? addBlogCard_excerpt($id) : $excerpt;

} else if(!empty($tax)) {
  // タクソノミー一覧の取得 ------------------------------------------------------------
  $term = get_term( $id, $tax );
  $title = empty($title) ? $term->name : $title;
  $excerpt = empty($excerpt) ? term_description($id, $tax) : $excerpt;

}

xpathの説明は省きますので、リファレンスをご覧ください。

10行目

og:descriptionの取得を先に持ってきているのは、検証していたサイトになぜかdescriptionがなかったからです。

画像の処理

アイキャッチ画像のURLを取得していきます。

事前次項として、ショートコードのthumbに画像idを設定することで画像が表示されるようにします。

addBlogCard.php: addBlogCard

if(wp_get_attachment_url($thumb)) {
  // サムネイルが設定されている場合 ------------------------------------------------------------
  $img = wp_get_attachment_image_url($thumb, array($img_width,$img_height));
  $img_tag = "<img src='{$img}' alt='{$title}' width='{$img_width}' height='{$img_height}'>";

} elseif( empty($tax) && $my_site !== false ) {
  // 通常 ------------------------------------------------------------
  if(has_post_thumbnail($id)) {
    $img = wp_get_attachment_image_src(get_post_thumbnail_id($id), array($img_width,$img_height));
    $img_tag = "<img src='{$img[0]}' alt='{$title}' width='{$img[1]}' height='{$img[2]}'>";
  }

} elseif(!empty($tax)) {
  // taxonomyが設定されている場合(ACFの場合) ------------------------------------------------------------
  if(get_field('image', "{$tax}_{$id}")) {
    $attachment = get_field('image', "{$tax}_{$id}");
    $img_tag = "<img src='{$attachment["sizes"]["thumbnail"]}' alt='{$title}' width='{$img_width}' height='{$img_height}'>";
  }
}

全体的にすごく簡単なwordpress関数で構成しています。

13~18行目

termに画像が設定されている場合の呼び出しになります。

私の場合はACFで設定することが多いので上記の設定にしていますが、皆さんはご自身の環境にあわせて必要であればご利用ください。

データの整理

ここでは、titleとexcerptに入っているであろう余計な文字列(空白やhtmlタグ、ショートコードなど)を削除します。

また、エラー回避のために定義されていない変数を空変数として定義しておきます。

まずは不要文字列削除のための関数を用意します。

addBlogCard.php

function addBlogCard_flat_text( $text ) {
  $text = strip_shortcodes($text);               // ショートコードの削除
  $text = wp_strip_all_tags($text);              // htmlの削除
  $rp = array(' ', ' ', "\xc2\xa0", "&nbsp;");
  $text = str_replace($rp, '', $text);           // $rpの文字列削除
  return $text;
}

strip_shortcodeswp_strip_all_tagsでショートコードとhtmlを削除します。

str_replace(Array, '', $text)Array内に含まれる文字列を$textから削除してくれます。

正確には”に置換してくれます。

\xc2\xa0はUTF-8の半角スペースを意味します。

上記のコードを用いてデータを整理します。

addBlogCard.php: addBlogCard

$title   = addBlogCard_flat_text($title);
$excerpt = addBlogCard_flat_text($excerpt);
$site    = isset($site) ? $site : "";
$img_tag = isset($img_tag) ? $img_tag : "";

$site$img_tagは定義されない場面が出てくるので再定義しています。

templateにargumentを渡す

近年では、get_tempalte_partの第三引数でargumentを受け取れるようになりましたので、それに伴う処理をしていきます。

addBlogCard.php: addBlogCard

$args = array(
  'url'     => $url,     // リンク先URL
  'img_tag' => $img_tag, // アイキャッチ画像
  'title'   => $title,   // タイトル
  'excerpt' => $excerpt, // 抜粋文
  'site'    => $site,    // サイトアイコン
);

これをget_template_partで受け取って表示するのですが、ショートコード内ではすごく困ることになります。

というのもWordpressの仕様上、コンテンツが表示される前にテンプレートが呼び出されます。

そのため、ショートコードを書いた箇所とは別に、ページTOPにもテンプレートが呼び出されることになります。

そのため、受け取る用の関数を用意しておきます。

addBlogCard.php

function addBlogCard_template( $args ) {
  ob_start();
  get_template_part( 'functions/addBlogCard/addBlogCard', 'template', $args );
  return ob_get_clean();
}

ob_startで一旦表示とは別の場所に保存し、ob_get_cleanで保存した内容を出力していると考えてください。

そして、上記をreturnします。

addBlogCard.php: addBlogCard

return addBlogCard_template( $args );

最後にショートコード化を書いて完了です。

addBlogCard.php

add_shortcode("blog_card", "addBlogCard");

テンプレートの実装

テンプレートを実装していきますが、各々やりたいデザインは異なると思いますので、ざっくり説明していきます。

phpファイルの処理

前項で受け取ったargumentを元に書いています。

addBlogCard-template.php

<a href="<?php echo $args['url'] ?>" target="_blank" class="blog-card">
  <div class="blog-card__header">
    <?php if(!empty($args['img_tag'])): ?>
      <div class="blog-card__thumbnail">
        <?php echo $args['img_tag'] ?>
      </div>
    <?php endif; ?>
    <div class="blog-card__content">
      <div class="blog-card__title"><?php echo $args['title'] ?></div>
      <div class="blog-card__excerpt"><?php echo $args['excerpt'] ?></div>
    </div>
  </div>
  <div class="blog-card__footer">
    <div class="blog-card__site"><?php echo $args['site'] ?></div>
    <div class="blog-card__more"><span>続きを読む</span></div>
  </div>
</a>

cssファイルの処理

あくまでサンプルですのでやりたいようにやってください。

addBlogCard.css

.blog-card {
  display: block;
  width: 100%;
  margin-bottom: 1.5rem;
  padding: 1rem;
  background-color: #fff;
  border: 2px solid #eee;
  text-decoration: none;
  color: #4C4C4C;
}
.blog-card__header {
  width: 100%;
  display: flex;
  gap: 1.5rem;
}
.blog-card__footer {
  display: flex;
  justify-content: space-between;
  gap: .5rem;
  width: 100%;
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid #eee;
}
.blog-card__site {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: 80%;
}
.blog-card__site img {
  flex-shrink: 0;
  margin-bottom: 0 !important;
}
.blog-card__more span {
  display: inline-block;
  padding: 0px 2rem;
  border: 1px solid #eee;
  font-size: 80%;
  font-weight: bold;
}
.blog-card__thumbnail {
  flex-shrink: 0;
}
.blog-card__title,
.blog-card__excerpt {
  line-height: 1.7;
  text-align: justify;
}
.blog-card__title {
  font-weight: bold;
}
.blog-card__excerpt {
  font-size: 85%;
}
@media screen and (max-width: 599px) {
.blog-card__excerpt {
  display: none;
}
}

また、cssの読み込みが必要になりますので、addBlogCard.php内でフックをかけておきます。

addBlogCard.php

add_action( 'wp_enqueue_scripts', 'addBlogCardScript' );
function addBlogCardScript() {
  $css = addBlogCard_get_template_url().'/addBlogCard.css';
  wp_enqueue_style( 'addBlogCard_style', $css, array() );
}

ショートコードの呼び出し設定

この時点でショートコード自体は機能します。(functions.phpで呼び出せば)

ただ、下記のようにエディタから選択できるようにしておきたいので作業を続けます。

jsの処理

addBlogCard.js

tinymce.create('tinymce.plugins.blogcard', {
  init: function(ed, url) {
    ed.addButton('blog_card', {
      title: 'ブログカード',
      text: 'ブログカード',
      type: 'menubutton',
      menu: [
        {
          text: '簡易',
          onclick: function () {
            ed.insertContent('[blog_card url=""]');
          }
        },
        {
          text: 'カテゴリー等一覧',
          onclick: function () {
            ed.insertContent('[blog_card url="" tax="category ea etc" id="category_id"]');
          }
        },
        {
          text: 'フル',
          onclick: function () {
            ed.insertContent('[blog_card url="" title="タイトル" excerpt="説明" thumb="media_id"]');
          }
        }
      ]
    });
  },
  createControl : function(n, cm) {
    return null;
  },
});
tinymce.PluginManager.add('blogcard_plugin', tinymce.plugins.blogcard);

※[]はなぜかエスケープできなかったので全角で入れていますので半角に戻してください。

説明すると長くなるので割愛しますが、TinyMCE用にblogcardというプラグインを作成しています。

また、menu内でブログカード配下に3つのメニューを作成しています。

phpの処理

次に作成したTinyMCE用のプラグインをTinyMCEに登録していきます。

addBlogCard-tinymce.php

// 作成したプラグインを登録
add_filter( 'mce_external_plugins', 'register_mce_blogcard_plugins' );
function register_mce_blogcard_plugins( $plugin_array ) {
  if(is_child_theme()) {
    $js = get_stylesheet_directory_uri().'/functions/addBlogCard/addBlogCard.js';
  } else {
    $js = get_template_directory_uri().'/functions/addBlogCard/addBlogCard.js';
  }
  $plugin_array[ 'blogcard_plugin' ] = $js;
  return $plugin_array;
}

// 作成したプラグインのボタンを登録
add_filter( 'mce_buttons', 'add_blogcard_buttons' );
function add_blogcard_buttons( $buttons ) {
  $buttons[] = 'blog_card';
  return $buttons;
}

こちらも長くなるので割愛しますが、TinyMCEに先ほど作成したプラグイン「blogcard」を登録しています。

最後にaddBlogCard-tinymce.phpをaddBlogCard.phpに呼び出せば完了です。

addBlogCard.php

require_once 'addBlogCard-tinymce.php';

実装

では、今まで作成したものを実装します。

といっても簡単で、functions.phpに1行書けば終わります。

functions.php

include 'functions/addBlogCard/addBlogCard.php';

これで実際に機能するようになります。

最後に

プラグインにすると余計な説明が増えるので、今回はfunctionsを作って対応する方法にて紹介しました。

私自身、このfunctionsに自分で作成した関数をとりためているのですが非常に便利ですので、皆さんも利用してみてください。

また、今回のポイントは下記となります。

# point

  • str_replace&nbsp;を置き換えできない時は\xc2\xa0を試してみる
  • ショートコード作成ではget_template_partをそのまま使用できない
  • 外部ドメインのものは直リンクしないのがWEBのマナー
  • 作成した関数をfunctionsフォルダにまとめておくと便利

それではよいWPライフを!