かもメモ

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

WordPress タクソノミー名によっては予約語でなくてもアーカイブページに投稿が表示されないことがあるっぽい

遥か昔、100億年ほど前に作っていたテーマを使用していたサイトで突然アーカイブページに何も表示されなくなったと連絡がありました。ブラウザの表示でなければ、何もして無ければ変化が起こるはずがないので、何を変更したか訊いたところ「WordPressプラグイン」をアップデートしたという事でした。はい。100%コレが原因で挙動が変わったわけですね。
「突然動かなくなった」じゃなくて、「アップデートしたら」って最初から言ってください...
(´-`).。oO(普段からもっとこまめにアップデートしておいておくれよ...)
よく解ってない運用者に期待するのが間違っているのです。はい。愚痴を言っても始まらないので、調査しましょう。

Note.

  • アップデートされたWordPressのバージョン: 4.6.1

title」というスラッグ名のタクソノミーのアーカイブページで投稿が表示されていない

問題のページを確認したところ、titleという名前のタクソノミーアーカイブページで問題が発生していました。
タクソノミーの登録はこのようになっていました。

<?php // function.php
add_action('init', 'my_custom_init');
function my_custom_init() {
  // 中略 //
  $arg = array(
    'labels'         => $labels,
    'public'         => true,
    'show_ui'        => true,
    'hierarchical'   => true,
    'show_tagcloud'  => true,
  );
  register_taxonomy('title', 'product', $arg);
  // 中略 //
}

product というカスタム投稿タイプのタクソノミーになっています。
他のproductに紐付けられたタクソノミーのアーカイブページは問題なく表示されており、タクソノミーの登録方法にも特に違いはありませんでした。
また、function.php でこのタクソノミーの際に何か処理を行ってるコードも特にありませんでした。

何が原因になっているか探す

問題がよくわからない時は、糸口を掴むために問題が起きているページと正常なページのWordPressグローバル変数 $wp_queryダンプして見比べて違いを探していきます。
問題となっているタクソノミーのアーカイブページのテンプレートはtaxonomy-title.phpです。

<?php var_dump( $wp_query ); ?>

上記コードをテンプレートに記述して見たところ、["posts"]=> &array(0) となっており取得できている投稿が0件となっていました。

どうやら投稿を取得するクエリに問題がありそうです。
このページを表示するのに使われたクエリは$wp_queryオブジェクトから下記で見ることができます。

<?php var_dump( $wp_query->request ); ?>

タクソノミー名が投稿タイトルと一致するものを探していた!

titleタクソノミーがhogeアーカイブページ (url: http://site-name/title/hoge) の時、次のようなクエリが使用されていました。

SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID FROM wp_posts
LEFT JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
WHERE 1=1
  AND wp_posts.post_title = 'hoge'
  AND ( wp_term_relationships.term_taxonomy_id IN (8) )
  AND wp_posts.post_type = 'product'
  AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date
DESC LIMIT 0, 20

WHERE句におかしなものがありますね...

_人人人人人人人人人人人人人人人人人人_
> AND wp_posts.post_title = 'hoge' <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

どうみても、投稿タイトルとタクソノミー名が一致しているものを探しています。本当に有難うございます。
当然、他の問題なく表示されているタクソノミーのアーカイブページにはpost_title =という条件はありませんでした。

リファレンスを見るかぎり、titleというタクソノミー名は予約語にはなっていない様なのですが、titleというタクソノミー名でアーカイブなどの絞込の条件がquery_varsの中にあると、投稿のタイトルの検索と誤認してしまってうようです。

<?php var_dump( $wp_query->query_vars ); ?>  
array(66) {
  ["title"]=>
  string(4) "hoge" // ← これが post_title = 'hoge' の条件になってるっぽい
  ["error"]=>
  string(0) ""
  ["m"]=>
  // 以下略

pre_get_posts アクション内でクエリの条件を修正する。

意図しない条件が入ってしまっているので、WordPressのクエリを変更する pre_get_posts アクション内でクエリの条件を変更します。

<?php // function.php
function my_posts_query( $query ) {
  if( !is_admin() && $query->is_main_query() ) {
        if( is_archive() && is_tax('title') ) {
      // query_vars["title"]を削除
      unset($query->query_vars["title"]);
      // query_vars["title"]を削除 するとナゼかタクソノミーの指定も無くなってしまうので、改めて追加する
      $term = $query->query["title"];
      $tax_query = array(
        array(
          'taxonomy' => 'title',
          'field'    => 'slug',
          'terms'    => $term,
        ),
      );
      $query->set('tax_query', $tax_query);
    }
  }
}
add_action('pre_get_posts', 'my_posts_query');

これで、問題なくtitleというタクソノミーのアーカイブページが問題なく表示されるようになりました。
リファレンスの予約語の欄にtitleという単語は無かったのですが、WordPress内で一般的に使われている単語はカスタム投稿やタクソノミー名に使うにはキケンがあると思っておいた方が良いかもしれません。


[参考]

フィンランドでSIMフリーiPhoneで快適インターネッツライフしたい

Moi. Suomi (フィンランド)に来ています。
フィンランドはホテルや町中いたるところにFreeWifiがあるのですが、観光客が多く回線が繋がりにくかったり移動中とかでも気にせずインターネッツをしたいと思いプリペイドのSIMを購入してSIMフリーiPhoneを使っています。
SIMカードの購入も設定もとても簡単だったので情報共有を!!

プリペイド式のSIMの購入

ヴァンター国際空港やヘルシンキ中央駅などにあるR kioskiというコンビニのような所で購入する事ができます。
f:id:kikiki-kiki:20160831201850j:plain
フィンランドは英語がかなり通じるので「プリペイド SIMカード プリーズ」と言えばOKです。
いくつか種類があるとどれが良いか聞かれます。僕はネットでも情報の多かったフィンランドの新興企業のDNAのSIMを選択しました。
f:id:kikiki-kiki:20160831174447j:plain
DNAのSIMはデータ通信だと1日定額0.89€です。店舗には7日分のものしか無いということだったので、「◯日分チャージしてください。」と言って滞在日分前もってチャージしてもらいました。
レシートを見ると少し割引になっていたので、もしかするとSIM購入時に一緒にチャージするとディスカウントがあるのかもです。対応してくれたお姉さんがとても美しかった(´∀`)ポワワ
(白いパッケージは自分でプランを選ばないとネット使い放題にならないようなのでピンクのパッケージが良いと思います。)

SIMを端末に入れて設定する

パッケージを開けると、色んなサイズにできる切り込みが入ったカードが出てきます。
f:id:kikiki-kiki:20160831174544j:plain これを自分の使う端末に合ったサイズに切り出します。この際 micro SIM だと少し切り取りにくいので、カッターナイフ等を持ってきておいた方が切り取りやすいと思います。(預け入れ荷物で!!) カッターナイフ持ってこなかったので切り取りに苦労しましたw

f:id:kikiki-kiki:20160831174603j:plain
カードの裏面に自分の電話番号と、PINコードなどが書かれています。PINコードはスクラッチになっているのでコインなどで削れば番号が分かる仕組みです。
また、お店で通信料をチャージしてもらう時にこのカードが有ればバーコードを読み取って直接チャージしてもらうことができるっぽいのでカードは財布などに入れておくと便利だと思います。

iPhoneでの設定

  1. 電源をOFFにしてSIMカードiPhoneに入れ、電源をONにします。
  2. 電源を入れるとPINロックがされているアラートが出るので解除するを選択
    カード裏のPIN1の番号を入力すればOK
  3. iPhoneの「設定 > モバイルデータ通信」からモバイルデータ通信をONにします。
  4. SMSにメッセージが届きます。一番下のリンクをクリックすれば設定画面に飛ぶのですが、wifiで繋いでいるとアクセス出来ないのでwifiをOFFにしてリンクをクリックします。
    f:id:kikiki-kiki:20160902142426p:plain
  5. webサイトに繋がり設定画面が表示されます。
    上のメニューで「EN」を選択してすれば英語メニューになります。
  6. SUBSCRIPTION INFOメニューを選択するとSIMの有効期間や残りのチャージ金額を見ることができます。予めチャージしてもらった金額が入っていることが確認できました。
    また以降、英語をメニューをデフォルトにする場合はLanguageを変更して「SET PAUSE MODE ON」をクリックすればOKです。
    f:id:kikiki-kiki:20160902143916p:plain
    電話番号先頭の358はフィンランドの国番号、日本でいうところの+81です。

SIMカードの管理

日本で使っている端末を持って行く時や他の国にも行くときは、日本で使ってたSIMやその国ごとのSIMを交換する必要があるのでSIMケースを持って行くと便利です。
f:id:kikiki-kiki:20160831181327j:plain
ぼくは、粘着式の薄っぺらいモノを購入して持って行きました。これはiPhoneのSIMを開ける道具も付いていて、ケース内に一緒に管理できるのでなかなかのアイディアグッズだと思いました。

感想

iPhoneの場合は「APN設定」は特に必要なく、ぶっちゃげSIMカードを入れてPINロックを解除した時点で使用可能になっていたので、とても簡単にインターネットに繋ぐ事ができました。
データ回線は4Gなので、今のところヘルシンキ内だととても快適にTwitterInstagramを使用することができています。

フィンランドでのプリペイド式のSIMの購入と設定はとても簡単だし、金額も安く高速なので、フィンランド旅行にはSIMフリーの端末を持って行くと幸せになれると思います!!


[参考]

PHP Slim 2.x Ajaxでputで送った値を取得したい。

"Slim is a PHP micro-framework" Rubyのテンプレートエンジンじゃない娘のお話です。

3系が既に出ているのですが、先方のサーバーのPHPのバージョンの関係で2系で開発をしています。今回 Ajaxのputメソッドでデータを送ったのですが、Slim側で値をとるのに少しハマったのでメモ。

Ajaxを送る側はこんな感じ

$(function () {
  var appAPI = {
    update: function(id, data) {
      var defer = $.Deferred(),
          apiPath = '/api/item/';
      $.ajax({
        type: 'PUT',
        url: apiPath + id,
        data: data,
        dataType: 'json',
        success: defer.resolve,
        error: defer.reject
      });
      return defer.promise();
    },
  };
  
  // 編集を保存する時に実行される関数
  var onEdited = function(itemID) {
    var formData = $('#my-form').serialize();
    appAPI.update(itemID, formData)
      .done(function(res) {
        // SECCESS
      })
      .fail(function(XMLHttpRequest, status, errorThrow) {
        // ERROR
      });
    return;
  };
});

Slimの設定

<?php
require('./vendor/autoload.php');

class MyAPP {
  public $slim;

  public function __construct() {
    $this->slim = new \Slim\Slim();
    $this->setupRouter();
  }

  // Routing
  private function setupRouter() {
    // API
    $this->slim->get('/api/item/:id', array($this, 'getItemByID'));
    $this->slim->post('/api/item', array($this, 'insertItem'));
    $this->slim->put('/api/item/:id', array($this, 'updateItem'));
    $this->slim->delete('/api/item/:id', array($this, 'deleteItem'));
  }
  
  // GET
  public function getItemByID($id) {
  }
  
  // POST
  public function insertItem() {
    // Ajaxから送られてきたデータは $_POST で配列形式で取得できる
    $postData = $_POST;
  }
  
  // PUT
  public function updateItem($id) {
    // Ajaxから送られたデータ $_POST は空配列隣取得できない。
    // Slimの機能を使って取得する
    $res = $this->slim->request();
    // getBody() で取得できるデータは `id=1&name=job1&...`のような形式になる
    $postData = $res->getBody();
    // parse_str で配列に変換する
    parse_str($postData, $postDataArray);
  }
  
  // DELETE
  public function deleteItem($id) {
  }

  public function run(){
    $this->slim->run();
  }
}

$myAPP = new MyApp();
$myAPP->run();

Ajax側から$form.serializeArray()で送れば、Slim側ではjson形式で取得できるのではないか?
と思って試してみたのですが、$res->getBody()で取得できる値は&つなぎの文字列になっていて、json_decode()で配列にすることはできませんでした。メソッドによって取得できる値の形式が異なるとメンドーなので、ぜんぶjson形式で取得できるのが楽なのですが...
もしかすると、Ajaxで送信するデータを自前でjson形式にしてしまうと、Slim側の$res->getBody()で取得できる値もjson形式になるかもしれません。