Edited at

WordPressでシステム開発をする時に必要なクエリ操作について

More than 3 years have passed since last update.

WordPressでガッツリしたシステム開発をする時によく使うクエリ操作について纏めてみた。

WordPress楽しいので他のフレームワークを使わないよ、でもゴリゴリSQL書かなきゃねっていう案件の時に。


前置き

使う前には

global $wpdb;

を書いてやる

PHPStormとかPHPDocが使える環境であれば

global $wpdb; /** @var wpdb $wpdb */

と書いておくとメソッドが補完できる。


テーブル一覧

テーブル一覧については把握が必要。

特にwp_postswp_postmetawp_optionsはよく使う。

あまり使わないけど混乱しがちなのがwp_terms, wp_term_taxonomies,wp_term_relationsps

テーブル名
備考

wp_commentmeta
コメントメタ。あまり使われない。

wp_comments
コメント。コメントの本文やスパム判定などが入ってる。

wp_links
リンク。もう(ほとんど)使われてない。

wp_options
オプション。サイト本体のオプションはもちろん、プラグインの設定やウィジェットなども入っている。

wp_postmeta
ポストメタ。カスタムフィールドの内容が入っている。

wp_posts
ポスツ。記事自体が入っている。post_statusやpost_typeの取り扱いに注意。

wp_terms
タームス。タグやカテゴリの表示名が入っている。

wp_term_relationships
タームリレーションシップス。記事とタームの関連付けが入っている。

wp_term_taxonomy
タームタクソノミー。 タームとタクソノミーのリレーションテーブル。

wp_usermeta
ユーザーメタ。なかなか活躍しない。

wp_users
ユーザーズ。ユーザとかはここ。スーパーパスワードリセットもここ。


http://wpdocs.osdn.jp/%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E6%A6%82%E8%A6%81

https://codex.wordpress.org/Database_Description



テーブル名

$wpdb->postsのように

テーブルプリフィックス+固有テーブル名を

$wpdb->テーブル名

と書いてやる。

テーブル名
PHP表記

wp_commentmeta
$wpdb->commentmeta

wp_comments
$wpdb->comments

wp_links
$wpdb->links

wp_options
$wpdb->options

wp_postmeta
$wpdb->postmeta

wp_posts
$wpdb->posts

wp_terms
$wpdb->terms

wp_term_relationships
$wpdb->term_relationships

wp_term_taxonomy
$wpdb->term_taxonomy

wp_usermeta
$wpdb->usermeta

wp_users
$wpdb->users


SELECT


よくある複数個のSELECT

$query = "SELECT * FROM $wpdb->posts ORDER BY ID LIMIT 20;";

$rows = $wpdb->get_results($query);
foreach($rows as $row) {
$id = $row->ID; // オブジェクトで返ってくる
}

$rows = $wpdb->get_results($query, OBJECT);と同義。

これとprepareを組み合わせたのが一番よく使う基本形。


連想配列で

global $wpdb;

$query = "SELECT * FROM $wpdb->posts ORDER BY ID LIMIT 20;";
$rows = $wpdb->get_results($query, ARRAY_A); // ココに指定
foreach($rows as $row) {
$id = $row['ID']; // 連想配列で返る
}


数字添え字で

$query = "SELECT ID, post_title FROM $wpdb->posts ORDER BY ID LIMIT 20;";

$rows = $wpdb->get_results($query, ARRAY_N); // ココに指定
foreach($rows as $row) {
$id = $row[0]; // 0: ID
$id = $row[1]; // 1: post_title
}


値一つだけ

$query = "SELECT count(*) FROM $wpdb->posts ORDER BY ID LIMIT 20;";

$count = $wpdb->get_var($query);

値一つだけならget_var()でも可。


Prepare -> Excecute

クエリを使い回すという感じではなくて単なるエスケープ(適当)。


標準的な

$query = "SELECT count(*) FROM $wpdb->posts WHERE post_type = %s ORDER BY ID LIMIT 20;";

$prepared = $wpdb->prepare($query, 'product');
$count = $wpdb->get_var($prepared);

$count = $wpdb->get_var($wpdb->prepare($query, 'product'));と一行で書いてある事が多い。


値を取得しない場合

$query = "INSERT INTO $wpdb->posts (post_content) VALUES (%s);";

$wpdb->query( $wpdb->prepare($query, 'yaaaHoooo!') );


sqlのワイルドカード

タイトルに'今日の'が含まれる物を取得

$query = "SELECT * FROM $wpdb->posts 

WHERE post_title LIKE %s
ORDER BY ID LIMIT 20;"
;
$rows = $wpdb->get_results($wpdb->prepare($query, '%今日の%'));
foreach($rows as $row) {
$id = $row->ID; // オブジェクトで返ってくる
}

%や_はprepareの後ろにそのまま入れると適当に展開してくれる。


updateとinsert


事例


こんな長いのでもOK

$query = "SELECT

year(pm1.meta_value) as year,
count(*) as count
FROM
$wpdb->posts
INNER JOIN wp_otpostmeta pm1 ON (wp_otposts.ID = pm1.post_id)
INNER JOIN wp_otpostmeta pm2 ON (wp_otposts.ID = pm2.post_id)
INNER JOIN wp_otpostmeta pm3 ON (wp_otposts.ID = pm3.post_id)
WHERE
$wpdb->posts.post_type = 'member'
AND
$wpdb->posts.post_status = 'publish'
AND pm1.meta_key = 'join_date'
AND pm1.meta_value >= %s
AND pm1.meta_value <= %s
AND pm2.meta_key = %s
AND pm2.meta_value NOT LIKE %s
AND pm3.meta_key = %s
AND pm3.meta_value NOT LIKE %s
GROUP BY year(pm1.meta_value)
ORDER BY pm1.meta_value ASC;"
;
$years = $wpdb->get_results(
$wpdb->prepare($query,
EPOCH_YEAR, get_year(),
get_year().'_status', '%quit%',
get_year().'_status', '%off%'), OBJECT);

混乱するけど。


カスタムフィールドに作った独自のIDで最大値取得

$query = "SELECT MAX(CAST(meta_value AS UNSIGNED)) FROM $wpdb->posts p

LEFT OUTER JOIN
$wpdb->postmeta pm on p.ID = pm.post_id
WHERE meta_key = %s;"
;
$max_id = $wpdb->get_var($wpdb->prepare($query, 'my_id'));


WordPressで使う全てのテーブルでロック取得

トランザクション処理する場合にはテーブルロックする必要がある。

更新しようとしているテーブル(例えばwp_posts)とかだけをロックしようとしても失敗するので、関連テーブルを全てロックするのが無難。

$locking = array();

foreach ($wpdb->tables('blog') as $table) {
$locking[]= $table . ' WRITE';
}
$query = 'LOCK TABLES '.implode(', ', $locking).';';
$wpdb->query($query);


ロックの時エイリアスを使うのであれば面倒っぽい

$locking = array();

foreach ($wpdb->tables('blog') as $table) {
$locking[]= $table . ' WRITE';
}
// http://mysql.stu.edu.tw/doc/refman/5.1/en/lock-tables.html
$aliases = array(
$wpdb->posts => 'p',
$wpdb->postmeta => 'pm',
);
foreach ($aliases as $table => $alias) {
$locking[]= $table . ' AS ' . $alias. ' WRITE';
}
$query = 'LOCK TABLES '.implode(', ', $locking).';';
$wpdb->query($query);

SELECT MAX(CAST(meta_value AS UNSIGNED)) FROM $wpdb->posts p

LEFT OUTER JOIN $wpdb->postmeta pm on p.ID = pm.post_id

こういうクエリ用


まとめ

超小規模な案件ならまだしも、小規模なシステム開発だとデータモデリングからテーブル設計辺りで「こ、これをWordPressで実装するには無理があるんじゃ…」と気づくでしょう。無理ではないですが適してないです。

他のフレームワークも検討しましょう。

という自戒。

WordPress愛がある人は是非WordPressで!