かもメモ

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

WordPress pre_get_posts・WP_Query 別々の条件で記事を取得したい

WordPressWP_Querypre_get_postsアクション内で投稿を取ってくる時に、一緒にできない複数の条件(複数のSELECT文が必要な条件)で投稿を取得したいような時のメモ。

メインループ外の場合は2回WP_Queryを実行(SQLを発行)してそれぞれ取得したデータの配列をゴニョゴニョとマージしてしまうか、2回while( $the_query->have_posts() )で回してしまう方法もあります。(処理が多いかもですが、現実的にはこれが単純で簡単だと思います。)

サンプル

実現したいこと

story というカスタム投稿(post_type)を全て取得
数字の入るカスタムフィールド_sotry_noの値の順に並べて、
その後ろに、_sotry_no が存在しない投稿を続ける

UNION (MySQL)

WordPressで使われているMySQL的にはUNIONを使用すると1回のSQLで複数のSELECT文からデータが取得できるようです。

UNIONは複数のSELECT文によってデータをそれぞれ取得し、その結果を結合した上で1つのデータとして取得する場合に使います。書式は次の通りです。
SELECT col_name1, ... FROM tbl_name1
 UNION [ALL | DISTINCT] SELECT col_name2, ... FROM tbl_name2
 UNION [ALL | DISTINCT] SELECT col_name3, ... FROM tbl_name3;
出典: 取得データの結合(UNION句) - データの取得 - MySQLの使い方

また、UNIONを使って結合する、それぞれのSELECT文で並び替えを行いたい場合は、SELECT文にLIMITが含まれる必要がありました。

ex1. pre_get_posts() のメインクエリ書き換え内で別の

管理画面の一覧をカスタムフィールド_story_noで並び替えるようにしようとした場合、
_story_noフィールドが登録されていない状態で投稿(story)が作成されてしまうと、管理画面の一覧に表示されなくなってしまい編集すら行うことができなくなってしまう問題がありました。

WP_Queryを使える箇所であれば2回WP_Query()を使えば問題がないのですが、管理画面の一覧などpre_get_postsでメインクエリを書き換える必要がある箇所はpre_get_postsのアクション内でSQLを書き換えざるを得ません。 posts_requestフィルターを使えば実行されるSQLを直接書き換えることが可能なようなので、これを利用します。
[参考] Plugin API/Filter Reference/posts request « WordPress Codex

<?php // functions.php
add_action( 'pre_get_posts', 'my_posts_per_page' );
function my_posts_per_page( $wp_query ) {
  // 管理画面
  if( is_admin() ) {
    // 投稿タイプ story
    if( $wp_query->is_post_type_archive('story') ) {
      // カスタムフィールド _story_no で検索して表示させる
      $wp_query->set('meta_key', '_story_no');
      $wp_query->set('orderby', array(
        'meta_value_num' => 'DESC',
        'date' => 'DESC'
      ));

      /**
       * WP_QueryのSQLを置き換えるフィルター
       * @param: $request (Strings) ... 元のSQLのテキスト
       */
      add_filter( 'posts_request', function( $request ) {
        global $wpdb;
        // config.phpで設定されているDBのprefixを取得
        $prefix = $wpdb->base_prefix;
        // UNIONで追加するSQL文を作成
        $sql = "SELECT p.* FROM {$prefix}posts AS p
          WHERE p.post_type = 'story'
            AND p.post_status = 'publish'
            AND NOT EXISTS(
              SELECT meta.meta_id FROM {$prefix}postmeta AS meta
              WHERE meta.post_id = p.ID
                AND meta.meta_key = '_story_no'
          )
          GROUP BY p.ID
          ORDER BY p.post_date DESC
          LIMIT 0,18446744073709551615";

        // 実際に実行したいSQL文を返す
        // ※ 管理画面の一覧の$requestはデフォルトでLIMITが設定されれいるので別途LIMIT節を追加する必要なし
        return "({$request}) UNION ({$sql})";
      });
    }
  }
}

これで管理画面一覧でも特定のカスタムフィールドが無い投稿も表示され編集できるようになりました。

ex2. WP_Query() でUNIONを使う方法

残念ながらWP_Query()の引数の渡し方で複数のSELECTをマージできるような機能、UNIONを使ったSQLを作れる機能は無いようです。
[参考] Class Reference/WP Query « WordPress Codex

WP_Queryを2回実行しないのであれば、やはりposts_requestフィルターでSQLを変更するしかなさそうです。

<?php // functions.php
function get_my_story() {
  $arg = [
    'post_type' => 'story',
    // 該当する story を全件取得
    'posts_per_page' => -1,
    'meta_key'       => '_story_no',
     // _story_no が0以下のものは表示しない
    'meta_query'     => [
      [
        'key'     => '_story_no',
        'value'   => 0,
        'compare' => '>',
      ],
    ],
    // _story_no が小さい順に並べる
    'orderby'        => [
      'meta_value_num' => 'ASC',
      'date' => 'ASC',
    ],
  ];
  // SQLを書き換えるためのフィルター
  add_filter( 'posts_request', 'my_posts_request_filter');
  // WP_Query()が実行される際に↑の関数が実行される
  $the_query = new WP_Query($arg);
  // 同じページに他の WP_Query() があるとフィルターが動作してしまうので、フィルターを削除する
  remove_filter( 'posts_request', 'my_posts_request_callback');
  if( $the_query->have_posts() ) {
    while( $the_query->have_posts() ) {
      // 処理
    }
  }
  // 略
};

/**
 * WP_QueryのSQLを置き換えるフィルター
 * @param: $request (Strings) ... 元のSQLのテキスト
 */
function my_posts_request_filter( $request ) {
  global $wpdb;
  $prefix = $wpdb->base_prefix;
  $sql = "SELECT p.* FROM {$prefix}posts AS p
          WHERE p.post_type = 'story'
            AND p.post_status = 'publish'
            AND NOT EXISTS(
              SELECT meta.meta_id FROM {$prefix}postmeta AS meta
              WHERE meta.post_id = p.ID
                AND meta.meta_key = '_story_no'
          )
          GROUP BY p.ID
          ORDER BY p.post_date DESC
          LIMIT 0,18446744073709551615";

  // 実際に実行したいSQL文を返す
  // ※ 全件取得する元のSQLのORDER BYが効くようにするには LIMITが必要なので大きな値でLIMIT節を追加する
  return "({$request} LIMIT 0,18446744073709551615) UNION ALL ({$sql})";
}

表側のページの場合他の箇所でもWP_Queryが使われていたりする場合があるので、必要な箇所でのフィルター処理が完了したらremove_filterでフィルターを削除しておかないと、他のWP_Queryが使われている場所でもフィルターが実行されてしまうので注意が必要です。

 
WordPressの管理画面を見やすくなるようにと特定のカスタムフィールドで並び替えをしていたら、カスタムフィールドが空文字やNULLなら良いのですが、DBに登録されなかった場合、一覧に表示されず編集できなくなってしまう状況になり盛大にハマってしまいました。
いつもカスタムフィールドはCMB2というプラグインを使いコードベースで作成しているのですが、表示に際して必須なカスタムフィールドがある場合は必ず初期値を指定しておくべきだなと痛感しました。(初期値があれば今回のようにSQLを直接書き換える必要もなかったのです...)

MySQLとかWordPressで覚えた程度だったのでUNIONの存在を知らず、UNIONで結合したSELECT文をそれぞれ別にソートする方法にすごくハマりました... おかげで良い勉強になりなったので良かったです。
結局解UNIONの事と、存在しないカスタムフィールドを取得する複合的な問題だったので、それぞれ別の記事を作成するしました。


参考

SQLアンチパターン

SQLアンチパターン

WordPress サブテーマ テーマの絶対パスを取得したい。

WordPressでテーマのディレクトリの絶対パス取得をする方法のメモ
テーマ構成

/themes
   |- /main-theme
   |   |- functions.php
   |- /sub-theme
   |   |- functions.php

テーマディレクトリまでのパスの取得

get_template_directory

テーマのパスを取得するのに get_template_directory() を使っているのをよく見ますが、サブテーマでこの関数を使うと親テーマのディレクトリまでのパスが返されてしまいます。

親テーマ内で使用

<?php // /main-theme/functions.php
echo get_template_directory();
// 出力 => /home/www/wordpress/wp-content/themes/main-theme

子テーマ内で使用

<?php // /sub-theme/functions.php
echo get_template_directory();
// 出力 => /home/www/wordpress/wp-content/themes/main-theme

get_template_directory()は常に親テーマのディレクトリまでのパスが取得される。

get template directory
末尾にスラッシュを付けずに、現在のテーマのディレクトリへの絶対パスを取得します。
子テーマが使用されている場合には、親テーマのディレクトリへの絶対パスが返されます。 子テーマのディレクトリへの絶対パスを取得するには get_stylesheet_directory() をお使いください。
[出典] 関数リファレンス/get template directory - WordPress Codex 日本語版

get_stylesheet_directory

get stylesheet directory
現在のテーマまたは子テーマのスタイルシートディレクトリのパスを取得します。
注: 末尾にスラッシュを含みません。
[出典] 関数リファレンス/get stylesheet directory - WordPress Codex 日本語版

親テーマ内で使用

<?php // /main-theme/functions.php
echo get_stylesheet_directory();
// 出力 => /home/www/wordpress/wp-content/themes/main-theme

子テーマ内で使用

<?php // /sub-theme/functions.php
echo get_stylesheet_directory();
// 出力 => /home/www/wordpress/wp-content/themes/sub-theme

get_stylesheet_directory()を使えばそれぞれのテーマまでのパスが取得することができました!
 

get_stylesheet_directory 親テーマ内のfunctions.phpで実行されても今見ているサイトのテーマまでのパスが取得できるっぽい

WordPressの子テーマは親テーマのfunctions.phpも実行されます。
親テーマのfunctions.phpget_stylesheet_directory()がある時、子テーマから実行されると「現在のテーマ」である子テーマのパスが返されるようです。
これを利用すれば、親テーマに書いておくだけで、子テーマが複数あっても各テーマの設定ファイルを読み込んだりさせることができます。

テーマ構成

/themes
   |- /main-theme
   |   |- functions.php
   |   |- /inc
   |       |- config.php
   |- /sub-theme
   |   |- functions.php
   |   |- /inc
   |       |- config.php

親テーマ(/main-theme/functions.php)

<?php
define('__THEME_DIR__', get_stylesheet_directory());
require_once(__THEME_DIR__ . '/inc/config.php');
  1. 親テーマのサイトを見ている時
    /main-theme/inc/config.php が読み込まれる
  2. 子テーマのサイトを見ている時
    /sub-theme/inc/config.php が読み込まれる

 
WordPressで親テーマ・子テーマを作成する時は、常に親テーマまでの絶対パスを取得したい時はget_template_directory()、動的に現在のテーマまでの絶対パスを取得したい時はget_stylesheet_directory()を使えば良いとお覚えておけば良さそうです。


[参考]

WordPress マルチサイト ネットワーク管理画面のURLが変?

下記のような構成でWordPressでマルチサイトを設置しました。
サイト構成

/site-root <- サイトのルートのディレクトリ
  |- .htaccess
  |- index.php
  |- /wordpress  <- WordPress本体
        |- wp-config.php

メインサイトの管理画面のURLは example.com/wordpress/wp-admin/ です。
管理画面からマルチサイトをコントロールする「サイトネットワークの管理」にアクセスすると...
URLが example.com/wp-admin/network/ になりました。
WordPress本体が /wordpress にあるので、example.com/wordpress/wp-admin/network/ でないのがとても気持ち悪いです...

試しに example.com/wordpress/wp-admin/network/ でアクセスした所問題なくネットワークの管理画面が表示されており、 新規サイト作成のフォームのPOST先もこちらで問題なく動作しているようでした。 (見落としがあるかもしれません)

検索してみると同じような質問(Multisite network admin – URL / redirect error - WordPress Development Stack Exchange)が出ていましたが、解決方法は載っておらず...
WordPressのネットワーク管理画面のコアファイル(/wp-admin/network)でネットワーク管理画面関連のURLを出力している部分を調べてみた所、network_site_urlという関数を使ってネットワーク管理画面のURLを作成しているようでした。
このnetwork_site_url関数が定義されているコードを調べた所 network_site_url フィルターが使用できることが解りました。

network_site_url フィルターを使ってネットワーク管理画面のURLを置き換える

テーマにかかわらず常に実行してほしいのとWordPressのアップデート時に巻き戻ってしまっては困るので wp-config.php にフィルターを設置して、ネットワーク管理画面のURLを変更するようにします。

/wordpress/wp-config.php の最下部WordPressの設定を読み込んでいるrequire_once(ABSPATH . 'wp-settings.php');の下にフィルターを追記します。

<?php //wp-config.php
/** Sets up WordPress vars and included files. */
require_once(ABSPATH . 'wp-settings.php');

// ▼ ネットワーク管理画面のURLを変更するフィルター ▼
// Wordpressのディレクトリ
define('__WORDPRESS_CORE_DIR__', '/wordpress');
// ネットワーク管理画面のURLを上書き
add_filter('network_admin_url', 'rewrite_my_network_admin_url', 10, 2);
function rewrite_my_network_admin_url($url, $path) {
  $networkPath = str_replace( '/wp-admin/', __WORDPRESS_CORE_DIR__ . '/wp-admin/', $url );
  return $networkPath;
}

これでネットワークの管理画面はexample.com/wordpress/wp-admin/network/になり、管理画面内からのリンクやフォームのPOST先のURLもwordpressディレクトリがある状態にすることができました!

WordPressの関数やフィルターを利用するコードを require_once(ABSPATH . 'wp-settings.php'); より上に書いてしまうとエラーになってしまうので、必ずwp-settings.phpの読み込みより下に記述しなければならないようです。

本当は.htaccessのRewriteRuleで書き換えたほうがスマートなのだと思うのですが、マルチサイトにした状態で上手くネットワーク管理画面関連だけのURLを書き換える技術力が今野私に無かったので、フィルターで出力自体を変更してしますというパワープレイに出ているので、とてもバッドノウハウな気がしてます。
現状確認できる範囲では問題なく動作しているようですが、何か不具合がある可能性は否めません。

そして、サブサイトを作成すると管理画面のURLは example.com/<sub-site>/wp-admin/ になってしまうので、WordPressのマルチサイトはそもそもサイト直下に直接WordPressのコアファイルを展開して使用する想定になってるっぽいなと思いました。

良い解決方法があればお待ちしています。

[参考]

<?php // /wp-includes/link-template.php#L3256
function network_site_url( $path = '', $scheme = null ) {
  if ( ! is_multisite() )
    return site_url($path, $scheme);

  $current_network = get_network();

  if ( 'relative' == $scheme )
    $url = $current_network->path;
  else
    $url = set_url_scheme( 'http://' . $current_network->domain . $current_network->path, $scheme );

  if ( $path && is_string( $path ) )
    $url .= ltrim( $path, '/' );

  /**
   * Filters the network site URL.
   *
   * @since 3.0.0
   *
   * @param string      $url    The complete network site URL including scheme and path.
   * @param string      $path   Path relative to the network site URL. Blank string if
   *                            no path is specified.
   * @param string|null $scheme Scheme to give the URL context. Accepts 'http', 'https',
   *                            'relative' or null.
   */
  return apply_filters( 'network_site_url', $url, $path, $scheme );
}

出典: https://core.trac.wordpress.org/browser/tags/4.8/src/wp-includes/link-template.php#L3256


人生にゆとりを生み出す 知の整理術

人生にゆとりを生み出す 知の整理術

WordPress さくらサーバーでマルチサイトにしたらリダイレクトループでサブサイトの管理画面にログインできなかった件

WordPressのマルチサイトを作成する案件があって、さくらのレンタルサーバーWordPressをインストールしてマルチサイトの設定を行いました。
メインサイトのトップを/にして、ディレクトリ型でマルチサイトをインストールしたまでは良かったのですが、サブサイトを作成してサブサイトの管理画面にアクセスしようとするとリダイレクトループが発生し管理画面にアクセスできない状況にハマってしまいました。(サブサイトそのものはリダイレクトループが発生すること無く表示されていました...

サイト構成

/site-root <- サイトのルートのディレクトリ
  |- .htaccess
  |- index.php
  |- /wordpress  <- WordPress本体
        |- wp-config.php

ネットワーク有効化の際に出力される .htpaccess に記入するコードに問題があった

さくらのレンタルサーバーでマルチサイトのネットワークを有効した時に表示されるRewriteRuleに不要なパスが出力されてしまっていてるのがリダイレクトループになってしまう原因のようでした。

ネットワークの有効化の際に表示される.htaccessの設定👇

2.次の内容を /home/user/www/ にある .htaccess ファイルへ追加して、他の WordPress ルールを置き換えてください:

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]

# add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) site-root/wordpress/$2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ site-root/wordpress/$2 [L]
RewriteRule . index.php [L]

よく見ると、/www 直下の.htaccessに記入するようにと書かれています。
ドメイン/site-rootディレクトリに指定している場合はWordPress.htaccess/site-root直下にあるので、出力される設定ではパスが異なってしまうのが原因のようです!

RewriteRule のパスを修正する

下から2つ目と3つ目の設定

RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) site-root/wordpress/$2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ site-root/wordpress/$2 [L]

ここにあるパスを.htaccessのあるドメイン指定してある/site-rootからみてのWordPress本体があるパスに修正します。

このケースではWordPress本体は/site-rootからみるとwordpressディレクトリになるので次のように修正します。

RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) wordpress/$2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ wordpress/$2 [L]

 
仮に/site-rootに直接WordPressのコアファイルが置かれている場合は次のような感じです。

RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]

 

マルチサイトを作ったのは初めてで、注意書きを見をとしてしまっていたためにパスの違いに気づかずハマってしまいました。
サブサイトのそのものが閲覧できていたので余計に、ナゼ管理画面だけリダイレクトループになるんだ!? と盛大にハマりました。

今日のアイカツ格言
「注意書きはちゃんと読もう!」
f:id:kikiki-kiki:20180201075055p:plain


ローリーとふしぎな国の物語 ~プログラミングとアルゴリズムにふれる旅~

ローリーとふしぎな国の物語 ~プログラミングとアルゴリズムにふれる旅~

WordPress マルチサイト設置のメモ

WordPressでマルチサイトを設置した時のメモ

サイト構成
次のような構成で、WordPressの管理画面の設定からサイトが/webrootで表示されるように変更しました。

/webroot
  |- .htaccess
  |- index.php
  |- /wordpress
         |- wp-config.php

マルチサイトの設定

1. wp-config.php にマルチサイトを使用可能にするコードを追加

/wordpress/wp-config.php にマルチサイトを可能にする設定を/* 編集が必要なのはここまでです ! WordPress でブログをお楽しみください。 */コメントの上に追加

define ('WP_ALLOW_MULTISITE', true);

2. プラグインを全て無効にする

プラグインが有効になっているとネットワークのインストールが出来ないので、プラグインを有効にしている場合は一度全て無効にする必要があります。

3. ネットワークのインストール

WordPress管理画面をリロードすると設定 > ツールに「ネットワーク」が追加されているので「ネットワーク」を選択。

サブサイトの運用を「サブドメイン型」にするか「サブディレクトリ型」にするかを決めます。 レンタルサーバーの場合「サブドメイン型」にするとサブサイトを作る度にドメインの設定を行わなければならない場合もあるので「サブディレクトリ型」にした方がメンドーは無いかもしれません。
既に運用としているWordPressサイトをマルチサイトにする時は固定ページなどでディレクトリの割当がされているせいか「サブドメイン型」しか選択できなくなるようです。

サイトのネットワーク名・マルチサイト全ての管理者(特権管理者権限)アカウントになるサイトネットワーク管理者のメールアドレスを確認します。 ネットワークの設定を1度行うとDBが変更されてしまうので修正するにはDBを初期化してWordPressを再インストールする必要があります。
設定に問題がなければ「インストールボタン」をクリックしてマルチサイトが使用できるネットワークをインストールします。

4. サイトネットワークの設置

ネットワークのインストールが完了するとwp-config.phpとサイトのトップ(index.phpのある階層)にある.htpaccessを変更する指示が出るので、これを各ファイルにコピペしていきます。
f:id:kikiki-kiki:20180201055509p:plain

/wordpress/wp-config.php 1.で追加したコードの下に表示されたコードをコピペします

define ('WP_ALLOW_MULTISITE', true);
define('MULTISITE', true);  // マルチサイトにする: true
define('SUBDOMAIN_INSTALL', false); // サブドメイン型: true サブディレクトリ型: false
define('DOMAIN_CURRENT_SITE', 'example.com'); // サイト全体のトップドメイン
define('PATH_CURRENT_SITE', '/'); // メインサイトのパス
define('SITE_ID_CURRENT_SITE', 1); // メインサイトのID
define('BLOG_ID_CURRENT_SITE', 1); // メインブログのID (将来的に複数のブログが管理できるようになるっぽい?)

wp-config.phpの変更が完了したらパーミッション400に変更しておきます。

次に、サイトトップに当たる階層にある.htpaccessに表示されたコードをコピペします。

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]

# add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) wordpress/$2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ wordpress/$2 [L]
RewriteRule . index.php [L]

この時 BEGIN WordPress で始まるWordPressが出力していた設定があるとリダイレクトループが発生してサイトが表示されなくなります。
注意書きにある通り「他のWordPress ルールを置き換えてください」とあるので、これを削除してしまうかコメントアウトします。(コメントアウトの方が万が一の時に戻しやすいかもです)

# BEGIN WordPress
# <IfModule mod_rewrite.c>
# RewriteEngine On
# RewriteBase /
# RewriteRule ^index\.php$ - [L]
# RewriteCond %{REQUEST_FILENAME} !-f
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteRule . /index.php [L]
# </IfModule>
# END WordPress

5. 管理画面に再ログイン

wp-config.php.htpaccessの変更が完了したら、管理画面をリロードするとログアウトしているので、3. で決めた特権管理者権限のアカウントでログインできればマルチサイトの設定は完了です。
ログインボタンを押した時にログイン画面にリダイレクトされてしまう場合は、一度Cookieを削除するとログインがログインが出来るようになるようです。
 

wp-config.phpに追加した define('MULTISITE', true); の値をfalseにするとDBやマルチサイトのデータを残したまま、マルチサイトでないメインのサイトだけの状態に戻すことが出来るようです。
但し、テーマ内にget_blog_details()の様なマルチサイトの時しか使えない関数があるとエラーになるので注意が必要です。
FYI: https://codex.wordpress.org/Function_Reference#Multisite_functions


[参考]

WordPress 子テーマ「親テーマが見つかりません。」になるとき

WordPressで子テーマを作成してインストールしようとしたら
修正が必要なテーマ: 親テーマが見つかりません。「THEME NAME」の親テーマをインストールしてください。
というエラーが表示される時

親テーマの指定はディレクトリ名でなければならない

テーマの構成

/themes
   |- /my-theme
   |- /my-sub-theme

親テーマ(my-theme)の設定

/* /my-theme/style.css
  Theme Name:  MY THEME
  ...
*/

この様な構成の時、子テーマ(my-sub-theme)での親テーマをTemplate:に指定します

/* /my-sub-theme/style.css
  Theme Name:  MY SUB THEME
   Template:   my-theme
*/

親テーマ名のMY THEMEではなく、親テーマのディレクトリ名のmy-themeで指定しないと親テーマとして認識されない。
テスト環境が大文字小文字を同じものとして扱ってたりして、本番サーバーが大文字小文字を別だと認識してたりするとハマったりする。


SHINY DAYS(TVアニメ「ゆるキャン△」OPテーマ)

SHINY DAYS(TVアニメ「ゆるキャン△」OPテーマ)

Coincheck 5億2300万NEM(620億円相当)流出事件のメモ

個人的な趣味でタイムライン作ってみるのに調べたのでlogとして
f:id:kikiki-kiki:20180129065836p:plain

日時 内容 参照
2018 1/26
02:57~08:26
CoincheckのウォレットからNEMが数回に渡り不正送金される Coincheckサービスにおける一部機能の停止について | コインチェック株式会社
11:25頃 Coincheck 異常を検知 http://corporate.coincheck.com/2018/01/26/29.html
12:07頃 Coincheck NEMの入金一時停止について告知 Coincheckサービスにおける一部機能の停止について | コインチェック株式会社
【重要】NEMを始めとした取扱通貨について | Coincheck(コインチェック)
12:38頃 Coincheck NEMの売買一時停止について告知 Coincheckサービスにおける一部機能の停止について | コインチェック株式会社
【重要】NEMを始めとした取扱通貨について | Coincheck(コインチェック)
12:52頃 Coincheck NEMの出金一時停止について告知 Coincheckサービスにおける一部機能の停止について | コインチェック株式会社
【重要】NEMを始めとした取扱通貨について | Coincheck(コインチェック)
15:35 Yahooニュース ビットコイン取引所「コインチェック」で620億円以上が不正に引き出される被害が発生 ビットコイン取引所「コインチェック」で620億円以上が不正に引き出される被害が発生(追記あり)(山本一郎) - 個人 - Yahoo!ニュース
16:33頃 Coincheck JPYを含め、全ての取扱通貨の出金一時停止について告知 Coincheckサービスにおける一部機能の停止について | コインチェック株式会社
【重要】NEMを始めとした取扱通貨について | Coincheck(コインチェック)
17:23頃 Coincheck BTC以外(オルトコイン)の売買の一時停止について告知 Coincheckサービスにおける一部機能の停止について | コインチェック株式会社
【重要】NEMを始めとした取扱通貨について | Coincheck(コインチェック)
17:31 Rin MIZUNASHI(@minarin_)さんハッカーのアカウント追跡開始をTwitterに投稿 コインチェック犯人追跡するみなりんさんのテレグラム中継 | ビットコインバーゲン
18:50頃 Coincheck クレジットカード、ペイジー、コンビニ入金一時停止について告知 Coincheckサービスにおける一部機能の停止について | コインチェック株式会社
【重要】NEMを始めとした取扱通貨について | Coincheck(コインチェック)
20:27 NEM財団 CEO Lon Wong(@2017Lon)氏
CC社のセキュリティの問題なのでハードフォークを行わない旨の記事をTwitterに投稿
https://twitter.com/2017Lon/status/956851031249272832
Coincheck Hack: "The Biggest Theft in the History of the World" | CryptoNews
23:30頃 Coincheck 記者会見 コインチェック社、記者会見全文3 仮想通貨NEMの不正流出疑惑を受けて - ログミー
2018 1/27
3:57
NEM財団 Alex(@Inside_NEM)さん
ハッキングされたNEMを自動追跡するシステムを開発したことをTwitterで発表
https://twitter.com/Inside_NEM/status/956964401399156736
NEMがコインチェックでハッキングを受けた通貨を追跡アカウントにタグ付け
17:00頃 Coincheck Coincheck payment 日本円出金、及び新規支払いの受付停止を発表 Coincheckサービスにおける一部機能の停止について | コインチェック株式会社
Coincheckサービスにおける一部機能の停止について | コインチェック株式会社
2018 1/28
00:46
Coincheck 不正送金されたNEMの460億円相当の補償を発表
NEM保有者に88.549円×保有数計算した日本円でコインチェックウォレットに返金
不正に送金された仮想通貨NEMの保有者に対する補償方針について | コインチェック株式会社

[出典]

ハッカーと画家 コンピュータ時代の創造者たち

ハッカーと画家 コンピュータ時代の創造者たち

Apache ローカル環境 400 Bad Request になるとき

ローカル環境を作成してApacheを起動してアクセスしたら次のようなエラーが表示されました。

Bad Request
Your browser sent a request that this server could not understand.
Additionally, a 400 Bad Request error was encountered while trying to use an ErrorDocument to handle the request.

ホスト名に_(アンダースコア)を使うことはできない。

Apache 2.4.25以降では/etc/hostshttpd-vhosts.confで設定したホスト名に_(アンダースコア)が含まれていると、400 Bad Requestになるようです。

解決方法。
そもそもホスト名に_を含むことはできないようなので、素直にローカル環境のホスト名を変更するコト。以上!

ドメイン名と違い、ホスト名のラベルはASCII文字の'a'から'z'まで(大文字小文字は無視される)と、'0'から'9'の数字そしてハイフンだけを使うことが出来る。ラベルの最初と最後の文字にハイフンを使うことは出来ない。ハイフン(そしてラベルの間に打つドット)以外の特殊文字は時に誤って使われるが許容されない。 また、アンダースコアはWindowsで構築されたシステムで一般に使われるが、RFC 952によれば許容されない。

出典: ホスト名 - Wikipedia

ホスト名に_は基本的にNGっぽいのですね。
ホスト名に_を使えるようにする方法もあるようですが、トラブルの元になりそうなので辞めておいたほうが良さげです。


参考

ふゆびより (キャンプ盤DVD付)

ふゆびより (キャンプ盤DVD付)

WordPress MySQL 特定のカスタムフィールドの値が存在しない投稿を取得したい。

WordPressのデータベースでは、カスタムフィールドの値は{$pre_fix}postmetaテーブルに登録されます。

postmeta テーブル
+------------+---------------------+
| Field      | Type                |
+------------+---------------------+
| meta_id    | bigint(20) unsigned |
| post_id    | bigint(20) unsigned |
| meta_key   | varchar(255)        |
| meta_value | longtext            |
+------------+---------------------+

カスタムフィールドが入力されていない投稿を取得したい時、カスタムフィールドの値が空文字かNULLでDBに登録されている場合は、単純に条件を空文字かNULLにしてしまえば問題ありません。
WP_Queryを使用する場合は下記のような感じ

<?php
$args = [
  'post_type'  => 'post_type',
  'meta_key'   => '_custom_field_name',
  'meta_query' => [
    [
      'key'     => '_custom_field_name',
      'value'   => '',
      'compare' => '=',
    ],
  ]
];

$the_query = new WP_Query($args);

DBに値が登録されていない場合

そもそもカスタムフィールドの値がDBに登録されていない(存在しない)場合は、WP_Queryなどでmeta_keyを条件にデータを取得する事ができないので、SQLを自作するしかありません。
WordPressにはDBから自作のSQLを使ってデータを取得することができるwpdbクラスがあるのでこれを利用します。

WordPressをインストールする時に決めたDBのテーブルのプレフィックス名(config.phpに記入されている)$wpdb->base_prefixで取得することが可能です。 カスタムフィールド_custom_field_nameの値がDBに登録されていない公開されている投稿を取得するなら下記のような感じになります。

<?php
global $wpdb;
$prefix = $wpdb->base_prefix;
$sql = "SELECT p.* FROM {$prefix}posts AS p
  WHERE
    p.post_type = 'post_type'
    AND p.post_status = 'publish'
    AND NOT EXISTS(
      SELECT meta.meta_id
      FROM {$prefix}postmeta AS meta
      WHERE
        meta.post_id = p.ID
        AND meta.meta_key = '_custom_field_name'
    )
  GROUP BY p.ID
  ORDER BY p.post_date DESC";

$res = $wpdb->get_results($sql); 
foreach($res as $post) {
  $ID = $post->ID;
  // 処理
}

SQLの書き方は自身がありませんが、これでカスタムフィールドの値がDBに登録されていない場合の投稿を取得することができました。
カスタムフィールドの作成にはCMB2プラグインを使っているのですが、カスタムフィールドが未入力の時、値が空文字でDBに登録されるものと、そもそもDBに登録されないものがあり難儀しました。
なんとなく、グループ化にしているカスタムフィールドは未入力のさいもDBに空文字で登録されているような気がしていますが、ちゃんと調べてないのでナゾです。(統一して欲しい


[参考]

SQLアンチパターン

SQLアンチパターン

PHP if文の条件の判定順

if文の中に&&||で条件を複数入れた時にどちらから処理されるか、PHPは左からであってると思ったけど、何か右から判定するのが合った気がしたので念のため調べてみました。

次のような関数を作成して、この関数を条件にすれば、出力結果から左右どちらから処理をしているかが判ります。

<?php
function f($str) {
  echo $str;
  return true;
}

&& (AND)

<?php
if( f('A') && f('Z') );   // AZ
if( !f('A') && f('Z') );  // A
if( f('A') && !f('Z') );  // AZ
if( !f('A') && !f('Z') ); // A

左側から処理がされています。

|| (OR)

<?php
if( f('A') || f('Z') );   // A
if( !f('A') || f('Z') );  // AZ
if( f('A') || !f('Z') );  // A
if( !f('A') || !f('Z') ); // AZ

||も同様に左側から処理がされています。  
PHPのif文の条件は左から(先に書いた方から)されるで合っていました!
なので、条件が沢山あるif文を作成する時は、先にbool値だけで判定できるとか関数を呼ばない処理の軽いものから書いておけば計算量が減るということですね。

なんとなく記憶にある右から処理する言語って何だったか気になるところですが、
覚えている範囲だとプログラム言語ではない感じですが、
CSSセレクタを右から辿って検索する(ネストの深い所から上階層に向かって捜査)のでセレクタにタグ名を付けない方が高速というのがあります。

h1.logo /* 遅い */
.logo /* CLASSだけの方が速い */
#logo /* IDの方がCLASSより速い */

ef - a tale of memories. OPENING THEME〜euphoric field feat.ELISA

ef - a tale of memories. OPENING THEME〜euphoric field feat.ELISA