かもメモ

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

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を自作するしかありません。
追記: meta_keyで指定せずにmeta_query の条件をNOT EXISTSにすることでWP_Queryで実現可能でした。下部に方法を書きます。
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に空文字で登録されているような気がしていますが、ちゃんと調べてないのでナゾです。(統一して欲しい
 

追記 2018-03-10 WP_Queryでカスタムフィールドが存在しない場合を条件に投稿を取得することができました。

WordPress3.5以上だとWP_Queryのmeta_queryNOT EXISTSを使えることを見落としていました。
そしてmeta_keyにカスタムフィールドの値を設定していたためにうまく取得できずWP_Queryでは取得できないのだと思ってしまっていたのでした...

<?php
$args = [
  'post_type'  => 'post_type',
  // ↓ meta_keyに指定しているとうまく取得できない
  // 'meta_key'   => '_custom_field_name',
  'meta_query' => [
    [
      'key'     => '_custom_field_name',
      'compare' => 'NOT EXISTS',
    ],
  ]
];

$the_query = new WP_Query($args);

参照: 関数リファレンス/WP Query - カスタムフィールドのパラメータ


[参考]

SQLアンチパターン

SQLアンチパターン