【WordPress】Twitterのツイート埋め込みをLINE風吹き出しにする

こんにちは、タカフです。

Webサイトやブログで商品を紹介する時に第三者の口コミがあると信憑性が増すので口コミを活用する事はとても有用です。

特に、セールスライティングの記事においては必須のテクニックですよね。

ではどこで口コミを探すかというと、今だとTwitterが便利です。

リアルな「お客様の声」を拾えるし、
Twitter自体も他のサイトへのツイートの埋め込みを許可していて、
ツイートのサブメニューから簡単に埋め込みタグを用意出来ます。


Twitterのツイートのサブメニューから簡単にツイート埋め込みタグを作れます。

ただ、個人的な所感として、このツイートの埋め込みって枠が大きいんですよね。。。

スマホで見てると、かなりスクリーンを占領してしまうので少し野暮ったい。。

ほらこんなに大きく幅を取りますよね↓

というわけで、今回は現役Webエンジニアである僕の技術をフル活用して、
Twitter APIからツイートデータを取得してLINEのような吹き出し風にするWordPressのショートコード」(長)を作ってみました。

こんな感じになります↓

ちゃんとblockquoteタグで囲ってあるので文章構造的に引用であることも明示してますし、クリックするとそのツイートに遷移します。

今回はこれを実現する為のソースコードを完全公開していきたいと思います。

注)本記事の内容を実現するにはTwitter APIの利用登録(審査必要)とDBへのテーブル作成やPHPカスタマイズなどが必要です。

何故これを作ろうとしたのか

当初、Twitterの埋め込みにはshadowRootという中身のcssを加えることが出来る技術があるのを知ったので、
その方法を使ってフロントだけでやっていました。

ただ、何故かこの方法だとTwitterのご機嫌が悪いとき?はshadowRootアクセスが出来なくて、いつもの埋め込みスタイルに戻ってしまっていたんですよね。

jsとcssだけでツイート埋め込みのスタイルを変えられてサクッと出来たので便利だったのですが、
いかんせんTwitter側の挙動が不安点で全く使えなかったので、今回TwitterAPIを使ってデータを取得して安定してスタイルをあてるショートコードを作る事にしました。

せっかくなのでまずはそのshadowRootによるスタイルを変える方法も紹介しておきます。

TwitterのshadowRoot経由でスタイルを変える方法(没ネタ)

まずWordPressの方で簡単なショートコード用意します。
ツイートIDを渡したら、適当なclassのつけたdivでhtmlを展開するだけの記述です。

functions.php
/**
 * LINE風Tweet埋め込み
 */
function tweet_like_func($args) {
  // TweetIDは必須なのでない場合空文字返却
  if ( !is_array($args) || count($args) == 0 ){
    return "";
  }
  $tweet_id = $args[0];

  // overflow:hiddenは中のtwitter-widgetがheightを持たないので高さを持たせる対策。
  return '<div class="tweet_line" data-tweet_id="' . $tweet_id . '" style="overflow: hidden;"></div>';
}
add_shortcode('tweet_line', 'tweet_like_func');

あと、functions.phpのどこかでTwitterのウィジェットを読み込めるようしておきます。

functions.php
// Twitterのウィジェットスクリプトを読み込む
wp_enqueue_script('twitter_widget.js', 'https://platform.twitter.com/widgets.js', [], '', true);

そしたら、そのサイトのscript.jsとかで以下の関数を書いてどこかで呼び出します。

script.js
function readyEmbedTweetLikeLine(){

  var styleTag = '<style>.EmbeddedTweet{max-width:100%;border:none}.CallToAction{display:none}.Tweet-brand{margin-left:4px;margin-top:-2px}.TweetAuthor-nameScreenNameContainer{display:flex;flex-direction:row}.TweetAuthor-decoratedName,.TweetAuthor-screenName{font-size:10px;color:#999}.EmbeddedTweet{min-width:300px}.EmbeddedTweet-tweet{padding:0!important}.Tweet{position:relative}.TweetAuthor{margin-left:0}.Tweet-body{position:relative;margin-top:-20px!important;margin-left:50px;box-sizing:border-box;padding:10px;border-radius:14px;background:#edf1ee}.Tweet-body:after{content:"";display:inline-block;position:absolute;top:3px;left:-19px;border:8px solid transparent;border-right:18px solid #edf1ee;-webkit-transform:rotate(35deg);transform:rotate(35deg)}.Tweet-text{float:left;font-size:14px;width:70%}.Tweet-card{float:right;width:30%;max-width:120px;margin-top:0!important}.TweetInfo{clear:both;font-size:12px!important;padding-top:10px;justify-content:flex-end}.TweetInfo-timeGeo{flex:0 1 auto}.tweet-InformationCircle{display:none}</style>';

  $('#entry .entry-content .tweet_line').each(function(){
    // 属性にtweetIDなければ次
    if ( !$(this).attr('data-tweet_id') ){
      return true;
    }

    twttr.widgets.createTweet ($(this).attr('data-tweet_id'), this,{
      align: "left" ,
      lang: "ja" ,
      conversation: "none",
      //cards: "hidden",    // 写真動画なし
      width: 550  // The maximum width of the rendered Tweet in whole pixels. This value should be between 250 and 550 pixels.
    }).then(function (el) {
      
      // style要素を追加
      $(el.shadowRoot).append(styleTag)

    });
  });
}

Twitterのウィジェットライブラリを使ってツイート埋め込みを出して、その後thenでshadowRootに対してcssを追加しています。

僕はWordPressのテーマにSANGOを使っているので、
$('#entry .entry-content .tweet_line').each(function(){});
というような記述で.tweet_lineを探していきます。

そして、実際の記事の中でTwitter埋め込みをいれたいところでショートコードとして記述します。

[tweet_line 1272459203752280064]

そうすれば、以下のような表示が出来たんですが、前述の通りこの方法はTwitterのご機嫌によってshadowRootに触ることが出来ない時があるので安定して使えなかったわけなのです。

TwitterAPIでデータを取得してスタイルを変える方法

というわけで、Twitter APIを使って特定ツイートのデータを取得して、DBに取り込んでおいて、それを独自のcssでスタイルを変えるという方式を開発したので、その手順とソースコードを全公開します。

Twitter API登録

まず、大前提としてこの方法はTwitterAPIの利用登録が必要です。
昔はすぐ利用登録するだけで使えたんですが、今は審査が入るようになったんですよね。

ちょっと面倒です。

Twitter APIの利用登録はググればたくさん出てくるのでこの内容は割愛します。

Qiitaあたりで探すといいかと思います。

参考URL)
https://qiita.com/kngsym2018/items/2524d21455aac111cdee

DBのテーブル作成

毎回ページ開かれる度にTwitterAPIにアクセスしてツイートデータを取得していては時間かかるし、TwitterAPIの制限もあるでしょうから、
TwitterAPIで取得したツイートデータはDBにためておくようにします。

ということで以下のテーブルを作成します。

sql
CREATE TABLE `wp_tweet_line` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `tw_id` varchar(30) DEFAULT NULL,
  `tw_created_at` datetime DEFAULT NULL,
  `tw_user_name` varchar(45) DEFAULT NULL,
  `tw_screen_name` varchar(45) DEFAULT NULL,
  `tw_text` varchar(255) DEFAULT NULL,
  `tw_user_profile_image_url` varchar(255) DEFAULT NULL,
  `tw_retweet_count` int(11) DEFAULT NULL,
  `tw_favorite_count` int(11) DEFAULT NULL,
  `tw_json` json DEFAULT NULL,
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
  `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `tw_id_UNIQUE` (`tw_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ミソとしてはTwitterのレスポンスをtw_jsonというカラムにそのまま保存しておくことです。
しかもMySQL5.7.8から使えるようになったJSON型で入れておきます。(JSON型がないならLONGTEXT型でおk)

WordPressショートコードからTwitterAPIでデータ取得してhtml展開する

TwitterAPIの操作はライブラリを使用します。一番有名なabraham/twitteroauth使います。

composerから入れましょう。

terminal
composer require abraham/twitteroauth

wp-config.phpからcomposerをautoloadするようにします。

wp-config.php
require_once __DIR__ . "/../../vendor/autoload.php";

次がメインの処理です。functions.phpに定義してWordPressのショートコードとして呼び出せるようにします。
CONSUMER_KEY などは前述のTwitterAPI利用登録から取得した文字列をはめ込んでいきます。

functions.php
use Abraham\TwitterOAuth\TwitterOAuth;

define("CONSUMER_KEY", "YOURS");
define("CONSUMER_SECRET", "YOURS");
define("ACCESS_TOKEN", "YOURS");
define("ACCESS_TOKEN_SECRET", "YOURS");


function get_db_instance() {
  static $db = null;
  if (!$db) {
    $dsn  = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME;
    $user = DB_USER;
    $pass = DB_PASSWORD;

    $options = [
      PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4'
    ];

    $db = new EasyPDO($dsn, $user, $pass, $options);
  }
  return $db;
}

/**
 * LINE風Tweet埋め込み for DB
 */
function tweet_like_func($args) {
  try {
    // TweetIDは必須なのでない場合空文字返却
    if (!is_array($args) || count($args) == 0) {
      return "";
    }
    $tw_id = $args[0];

    $db = get_db_instance();

    $row = $db->fetchRow("select * from wp_tweet_line where tw_id = ?", [$tw_id]);
    
    /* テーブルになかった場合は新規で取得する */
    if (!$row) {
      $row = store_tweet_data($tw_id);
    } else {
      /* レコードとして存在する場合は数日超えていたら再度取得とかでいいと思う */
      /* お気に入りの数値とかが更新される */
      // WP上ではdata_i18nを指定しないと日本時間とれない
      $expired_time = strtotime(date_i18n("YmdHis") . " -3 days");
      $updated_time = strtotime($row["updated_at"]);
      if ( $updated_time < $expired_time ) {
        $row = store_tweet_data($tw_id, $row);
      }
    }
    
  }catch(Exception $ex){
    $row = null;
  }
  
  $html = "";
  if ( $row ){
    $html = setup_tweet_line_html($row);
  }
  
  return $html;
}

/**
 * Twitter APIからデータを取得してDBに格納する
 * そしてDBデータを返却する
 * @param      $tw_id
 * @param null $exist_row
 * @return bool
 */
function store_tweet_data($tw_id, $exist_row = null){
  try{
    $db = get_db_instance();
    
    $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET);
    $param      = [
      "id" => $tw_id,
      "tweet_mode" => "extended"  // これがないと本文が省略されてしまう
    ];
    $content    = $connection->get("statuses/show?tweet_mode=extended", $param);

    // そもそも取れなかったり、idがなかったりしたらfalse返却
    if (!$content || !isset($content->id_str)) {
      return false;
    }

    $tw_id = $content->id_str;

    // TwitterはGMT時間なのにGMT+9とか書いてくれないのでこちらで+9 hoursしてあげる
    $data = [
      "tw_id"                     => $tw_id,
      "tw_created_at"             => date_i18n('Y/m/d H:i:s', strtotime($content->created_at . " +9 hours")),
      "tw_user_name"              => $content->user->name,
      "tw_screen_name"            => $content->user->screen_name,
      "tw_text"                   => isset($content->full_text) ? $content->full_text : $content->text,
      "tw_user_profile_image_url" => $content->user->profile_image_url_https,
      "tw_retweet_count"          => $content->retweet_count,
      "tw_favorite_count"         => $content->favorite_count,
      "tw_json"                   => json_encode($content, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
    ];
    
    if (!$exist_row) {
      $res = $db->insert("wp_tweet_line", $data);
    } else {
      $res = $db->update("wp_tweet_line", $data, ["tw_id" => $tw_id]);
    }

    $row = $db->fetchRow("select * from wp_tweet_line where tw_id = ?", [$tw_id]);
    
    return $row;
    
  }catch(Exception $ex){
    
    return false;
  }
}

/**
 * Twitterのレコードをもとにhtmlを組み立てます。
 * @param $record
 */
function setup_tweet_line_html($record){
  $r = $record; // shortcode
  $t = json_decode($record["tw_json"], true); // Tweetデータそのもの

  $tweet_url = "https://twitter.com/{$r['tw_screen_name']}/status/{$r['tw_id']}";
  $media_img = "";
  if ( isset($t["entities"]["media"]) && count($t["entities"]["media"]) > 0 ){
    $media_url = $t["entities"]["media"][0]["media_url_https"];
    $media_img = "<img src=\"{$media_url}\" />";
  }
  $user_url = "https://twitter.com/{$r['tw_screen_name']}";
  $tweeted_date = $r["tw_created_at"];

  $tweet_text = $r["tw_text"];

  // テキスト最後にそのツイートURLが入るのでそれを除去する
  $tweet_text = preg_replace ( "/\s?https:\/\/t\.co.+$/", "", $tweet_text );

  // 最後に改行を<br>変換
  $tweet_text = nl2br($tweet_text);

  $html = <<<HTML
<blockquote class="tweet_line_box" cite="{$tweet_url}">
  <div class="col-1">
    <a href="{$user_url}" target="_blank" rel="noopener noreferrer">
      <div class="tw_author_icon" style="background-image: url({$t['user']['profile_image_url_https']})"></div>
    </a>
  </div>
  <div class="col-2">
    <a href="{$user_url}" target="_blank" rel="noopener noreferrer">
    <div class="tw_author">
      <span class="tw_author_display_name">{$t['user']['name']}</span>
      <span class="tw_author_screen_name">@{$t['user']['screen_name']}</span>
    </div>
    </a>
    <a href="{$tweet_url}" target="_blank" rel="noopener noreferrer">
    <div class="tw_content">
      <div class="tw_media">
        {$media_img}
      </div>
      <p class="tw_text">
        {$tweet_text}
      </p>
      
      <div class="tw_content_footer">
        <!-- ♡<span class="tw_favoriot_count">{$t['favorite_count']}</span> -->
        <span class="tw_created_at">{$tweeted_date}</span>
      </div>
    </div>
    </a>
  </div>
  
</blockquote>
HTML;
  
  return $html;
}

add_shortcode('tweet_line_db', 'tweet_like_func');

簡単に説明すると、
ショートコードから呼び出されて、そのツイートIDがDBにあるかを見ます。
なかったらそのままAPI取得して、もしあるなら最後に保存した時間がある程度の期間を過ぎているなら再度API取得としてます。

そしてTwitterAPIから取得したツイートデータをhtmlに展開して返却しています。

あと、ミソとしてはblockquoteタグを使ってちゃんとこれが引用であることを明示していることです。ついでにcite属性も使って引用元URLもちゃんと定義します。

LINEの吹き出し風にするcss

style.css
blockquote.tweet_line_box {
  display: flex;
  min-width: 300px;
  max-width: 600px;
  padding: 10px;
  border: 0;
}
blockquote.tweet_line_box .col-1 {
  flex: 0 0 55px;
}
blockquote.tweet_line_box .col-1 .tw_author_icon {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: top center;
}
blockquote.tweet_line_box .col-2 {
  flex-grow: 1;
}
blockquote.tweet_line_box .col-2 .tw_author {
  font-size: 11px;
  color: #999;
  cursor: pointer;
}
blockquote.tweet_line_box .col-2 .tw_author:hover {
  text-decoration: underline;
}
blockquote.tweet_line_box .col-2 .tw_content {
  position: relative;
  box-sizing: border-box;
  padding: 10px;
  border-radius: 14px;
  background-color: #edf1ee;
  color: #444;
}
blockquote.tweet_line_box .col-2 .tw_content:after {
  content: "";
  display: inline-block;
  position: absolute;
  top: 3px;
  left: -19px;
  border: 8px solid transparent;
  border-right: 18px solid #edf1ee;
  -webkit-transform: rotate(35deg);
  transform: rotate(35deg);
}
blockquote.tweet_line_box .col-2 .tw_content .tw_media {
  float: right;
  width: 35%;
  min-width: 100px;
  min-height: 100px;
  margin-left: 10px;
  margin-bottom: 10px;
}
blockquote.tweet_line_box .col-2 .tw_content .tw_media img {
  width: 100%;
  height: auto;
  box-shadow: 0 0 4px grey;
  border-radius: 4px;
}
blockquote.tweet_line_box .col-2 .tw_content .tw_text {
  margin: 0;
  font-size: 14px;
}
blockquote.tweet_line_box .col-2 .tw_content .tw_content_footer {
  clear: both;
  font-size: 11px;
  text-align: right;
}
blockquote.tweet_line_box .col-2 .tw_content .tw_content_footer .tw_favoriot_count {
  display: none;
}
blockquote.tweet_line_box .col-2 cite {
  font-size: 11px;
}
blockquote.tweet_line_box:before {
  content: none;
}
blockquote.tweet_line_box a:hover {
  text-decoration: none;
}

これで完成です。あとは記事の中からWordPressのショートコードとして呼び出せばOKです。

Twitterのツイート埋め込みは著作権に関しては全く問題ない

Twitterのツイート埋め込みは勝手に自分のサイトに埋め込んでも著作権として良いのかという疑問がありますが、

これに関しては全く問題ありません。

Twitterの利用規約には以下のように記述しています。
https://twitter.com/ja/tos

ユーザーは、本サービス上にまたは本サービスを介してコンテンツを送信、投稿または表示することによって、当社が、既知のものか今後開発されるものかを問わず、あらゆる媒体または配信方法を使ってかかるコンテンツを使用、コピー、複製、処理、改変、修正、公表、送信、表示および配信するための、世界的かつ非独占的ライセンス(サブライセンスを許諾する権利と共に)を当社に対し無償で許諾することになります(明確化のために、これらの権利は、たとえば、キュレーション、変形、翻訳を含むものとします)。このライセンスによって、ユーザーは、当社や他の利用者に対し、ご自身のツイートを世界中で閲覧可能とすることを承認することになります。

つまり、ユーザーがTwitterを使うということは世界中で閲覧されることを承諾した上で使っているということです。

実際鍵付きアカウントでもない限り普通に閲覧されますからね。

そして、ツイート埋め込みもTwitterAPIもTwitter社が公式に提供しているので問題ないというわけです。

結果として口コミとして使っても全く問題ないわけです。

むしろ好意的なツイートを紹介するとしたら、Twitterユーザー・紹介者・企業にとってもWin-Win-Winですよね。

まとめ

以上がTwitterのツイート埋め込みをLINEのふきだし風にする方法でした。

最後に本記事のおさらいをすると、

  1. shadowRootによるスタイル変更は不安定で使えない
  2. Twitter APIからデータ取得して独自html/cssで展開すれば自由に表現出来る
  3. Twitter埋め込みは著作権としては問題ない

ということでした。

Twitterの口コミをうまく使えば、第三者の評価として信憑性が増し、販促力を高められることかと思います。

アフィリエイトで使うのもひとつのテクニックですね。

現場からは以上です。

2 COMMENTS

きいろ

タカフさん、はじめまして。

本記事読ませていただきました。
とても興味深い内容でした。

同じようなことをしたいと思っておりましたので参考になります。

しかしバックエンドの知識(DBに関して)疎いのでDBに関する実装を省きたいと思っておりましてDBを経由せずにtwitterAPIの情報をショートコードにて表示する場合には、
本記事の本記事記載のコードをどう書き換えればよろしいでしょうか。

またここでこのような質問をすることが適切で無い場合に他にタカフさんに対して
本記事について聞く方法はありますか。

返信する
タカフ

ありがとうございます!こんなマニアな記事に興味持って頂き嬉しいです😁
フロントエンドだけでこれをやるのは難しいかと思います。何故ならこの記事にも書いたようにTwitterのiframeをサイト内から直接操作するのは難しいためです(これが出来たら僕もそうしていました)。
iframe内のhtmlをいじるのにshadowRootという技を使いますがそれが何故か不安定なんですよね。できる時もあれば出来ない時もあるって感じでした。
ということでバックエンドでAPI経由でTweetを取得してテキストやアイコン画像URLなどを保存して表示させるのが確実です。ただ、最近Twitter APIが有料化する話があり、この方法ももうすぐ出来なくなりそうなのでそしたらまた何か方法考えて記事にしたいと思います。

返信する

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です