Web制作・開発会社 プレスマンのスタッフブログ

PRESSMAN*Tech

WPDBでSQLを扱う

WordPressのwpdbクラスには、SQLを扱うための豊富なメソッドが用意されています。そこで今回は、その中でも特に使用頻度が多いと思われるメソッドについて紹介していきたいと思います。(ほぼSELECT文で使うメソッドです)

wpdbクラスとは?

wpdbクラスとは、WordPressのデータベースとやりとりするための一連のメソッドが含まれているクラスです。 実際に使用する時は、グローバル変数である$wpdbオブジェクトを使うことで、データベースに対して様々な操作をすることができます。

使い方

$wpdbオブジェクトはWordPressで用意されているグローバル変数なので、以下の1文を記載すれば使う準備は全て完了です。
global $wpdb;

よく使われる関数

wpdbクラスでは多くのDB通信用のメソッドが用意されていますが、その中でも特によく使われるメソッドをピックアップしてご紹介します。

get_results

wpdbメソッドの中でも最もよく使われるであろうメソッドです。
SQL文の結果全体を配列として返し、下記の第2引数でどのような配列形式で返すかを指定することもできます。また、SQLの結果に合致しない場合には、NULLが返ります。
複数のカラムの値を取得したい時には最適なメソッドです。

ソースコード

/**
 * Retrieve an entire SQL result set from the database (i.e., many rows)
 *
 * @return array|object|null Database query results
 */
 public function get_results( $query = null, $output = OBJECT ) {
    $this->func_call = "\$db->get_results(\"$query\", $output)";

    if ( $this->check_current_query && $this->check_safe_collation( $query ) ) {
        $this->check_current_query = false;
    }

    if ( $query ) {
        $this->query( $query );
    } else {
        return null;
    }

    $new_array = array();
    if ( $output == OBJECT ) {
        // Return an integer-keyed array of row objects
        return $this->last_result;
    } elseif ( $output == OBJECT_K ) {
        // Return an array of row objects with keys from column 1
        // (Duplicates are discarded)
        foreach ( $this->last_result as $row ) {
            $var_by_ref = get_object_vars( $row );
            $key = array_shift( $var_by_ref );
            if ( ! isset( $new_array[ $key ] ) )
                $new_array[ $key ] = $row;
        }
        return $new_array;
    } elseif ( $output == ARRAY_A || $output == ARRAY_N ) {
        // Return an integer-keyed array of...
        if ( $this->last_result ) {
            foreach ( (array) $this->last_result as $row ) {
                if ( $output == ARRAY_N ) {
                    // ...integer-keyed row arrays
                    $new_array[] = array_values( get_object_vars( $row ) );
                } else {
                    // ...column name-keyed row arrays
                    $new_array[] = get_object_vars( $row );
                }
            }
        }
        return $new_array;
    } elseif ( strtoupper( $output ) === OBJECT ) {
        // Back compat for OBJECT being previously case insensitive.
        return $this->last_result;
    }
    return null;
}

使用例

$result = $wpdb->get_results( $query, $output );
$query:実行したいSQL文
$output:以下の4つの定数のうちいずれかを指定する。初期値はOBJECT。
以下で$outputで使用できる定数を紹介しています。(紹介しているコードは、全て1記事目のpost_titleが'Hello world' である前提です。)
こちらが以下の例で使用している、実際の投稿です。投稿のタイトルが「Hello world!」となっています。
・OBJECT(初期値):結果をインデックス配列として出力し、要素はオブジェクトとして返す。
$sql = 'SELECT post_title, post_content FROM wp_posts';
$object = $wpdb->get_results( $sql );
echo $object[0]->post_title; // 'Hello world!'を出力
・OBJECT_K:結果を連想配列として出力し、その配列のキーはSQLで選択した第1カラムの値となり、その値は1行分のオブジェクトを返す。第1カラムの値に重複がある場合は無視される。
以下の例では第1カラムにpost_titleを選択しているので、post_titleの値が連想配列のキーになる。
$sql = 'SELECT post_title, post_content FROM wp_posts';
$object_k = $wpdb->get_results( $sql, OBJECT_K );
echo $object_k['Hello World!']->post_title; // 'Hello world!'を出力
・ARRAY_A:結果をインデックス配列として出力し、要素は1行分の連想配列でそのキーはカラム名として返す。
$sql = 'SELECT post_title, post_content FROM wp_posts';
$array_a = $wpdb->get_results( $sql, ARRAY_A );
echo $array_a[0]['post_title']; // 'Hello world!'を出力
・ARRAY_N:結果をインデックス配列として出力し、要素は1行分のインデックス配列として返す。
$sql = 'SELECT post_title, post_content FROM wp_posts';
$array_n = $wpdb->get_results( $sql, ARRAY_N );
echo $array_n[0][0]; // 'Hello world!'を出力

get_col

指定した列のみを返すメソッドです。列のみなので、結果としては一次元配列を返しますが、SQLクエリに合致する結果がない場合にはNULLを返します。
各レコードから1つのカラムの値を取得したい場合には、get_resultsとは違って一次元配列で取得することができるため、使い勝手が良いと思います。

ソースコード

/**
 * Retrieve one column from the database.
 *
 * @return array Database query result. Array indexed from 0 by SQL result row number.
 */
 public function get_col( $query = null , $x = 0 ) {
    if ( $this->check_current_query && $this->check_safe_collation( $query ) ) {
        $this->check_current_query = false;
    }

    if ( $query ) {
        $this->query( $query );
    }

    $new_array = array();
    // Extract the column values
    for ( $i = 0, $j = count( $this->last_result ); $i < $j; $i++ ) {
        $new_array[$i] = $this->get_var( null, $x, $i );
    }
    return $new_array;
}

使用例

$wpdb->get_col( $query, $offset );
$query:実行したいSQL文
$offset:必要としている列(カラム)のオフセット。テーブルのカラムを左から数えた時のカラムの順番で、1列目は0で、初期値は0。
こちらが以下の例で使用する、実際の投稿です。本文の内容を段落ブロックで、「はじめまして!ブログを始めました」としています。
例:全記事のpost_titleを取得する(1記事目のpost_contentが、'<p>はじめまして!ブログを始めました</p>'である場合)
// カラム指定で取得した場合
$query = 'SELECT post_content FROM wp_posts';
$result = $wpdb->get_col( $query );
echo $result[0]; // '<p>はじめまして!ブログを始めました</p>'

// オフセット指定で取得した場合(post_contentがwp_postsテーブルでカラムを左から0ベースで数えて4番目の場合)
$query = 'SELECT * FROM wp_posts';
$result = $wpdb->get_col( $query, 4 );
echo $result[0]; // '<p>はじめまして!ブログを始めました</p>'

get_var

指定した変数を1つのみ返すメソッドです。結果にマッチするものがない場合はNULLが返ります。
複数ではなく、単一のレコードから単一のカラムの値を取得する(=取得したい値が1つ)場合には、配列に格納されずそのままの値を取得できるため、get_varを使用した方が良いでしょう。

ソースコード

/**
 * Retrieve one variable from the database.
 *
 * @return string|null Database query result (as string), or null on failure
 */
public function get_var( $query = null, $x = 0, $y = 0 ) {
    $this->func_call = "\$db->get_var(\"$query\", $x, $y)";

    if ( $this->check_current_query && $this->check_safe_collation( $query ) ) {
        $this->check_current_query = false;
    }

    if ( $query ) {
        $this->query( $query );
    }

    // Extract var out of cached results based x,y vals
    if ( !empty( $this->last_result[$y] ) ) {
        $values = array_values( get_object_vars( $this->last_result[$y] ) );
    }

    // If there is a value return it else return null
    return ( isset( $values[$x] ) && $values[$x] !== '' ) ? $values[$x] : null;
}

使用例

$wpdb->get_var( $query, $column_offset, $row_offset );
$query:実行したいSQL文
$column_offset:必要としている「」のオフセット。テーブルのカラムを左から数えた時のカラムの順番で、1列目は0で、初期値は0。
$row_offset:必要としている「」のオフセット。SQLのOFFSETやLIMITのように行をどこから取得するかを指定する数字で、1行目は0で、初期値は0。
以下の例で使用している実際のデータです。固定ページの2番目の投稿のタイトルが「Privacy Policy」となっています。
例: post_typeがpageの投稿をID順に並べた時に、2番目になる記事のpost_titleを取得
$query = 'SELECT post_title FROM wp_posts WHERE post_type = "page" ORDER BY ID LIMIT 1, 1';
$result = $wpdb->get_var( $query );
echo $result; // 'Privacy Policy'

// 以下は$row_offsetを使い、同じ結果を取得している
$query = 'SELECT post_title FROM wp_posts WHERE post_type = "page" ORDER BY ID';
$result = $wpdb->get_var( $query, 0, 1 );
echo $result; // 'Privacy Policy'

prepare

プレースホルダーを含むSQLを指定することができ、SQL文を安全なものにエスケープします。また、$wpdb->queryとよく一緒に使われています。
特に外部からの値をSQLの条件に使う際に、SQLインジェクション対策のために使われます。

ソースコード

/**
 * Prepares a SQL query for safe execution. Uses sprintf()-like syntax.
 *
 * @return string|void Sanitized query string, if there is a query to prepare.
 */
public function prepare( $query, $args ) {
    if ( is_null( $query ) )
        return;

    // This is not meant to be foolproof -- but it will catch obviously incorrect usage.
    if ( strpos( $query, '%' ) === false ) {
        _doing_it_wrong( 'wpdb::prepare', sprintf( __( 'The query argument of %s must have a placeholder.' ), 'wpdb::prepare()' ), '3.9' );
    }

    $args = func_get_args();
    array_shift( $args );
    // If args were passed as an array (as in vsprintf), move them up
    if ( isset( $args[0] ) && is_array($args[0]) )
        $args = $args[0];
    $query = str_replace( "'%s'", '%s', $query ); // in case someone mistakenly already singlequoted it
    $query = str_replace( '"%s"', '%s', $query ); // doublequote unquoting
    $query = preg_replace( '|(?<!%)%f|' , '%F', $query ); // Force floats to be locale unaware
    $query = preg_replace( '|(?<!%)%s|', "'%s'", $query ); // quote the strings, avoiding escaped strings like %%s
    array_walk( $args, array( $this, 'escape_by_ref' ) );
    return @vsprintf( $query, $args );
}

使用例

$query = 'UPDATE wp_posts SET post_title = %s WHERE ID = 1';
$wpdb->query( $wpdb->prepare( $query, $value ) );
$query: 実行したいSQL文
$value: プレースホルダーに入れる値
以下の例で使用している実際のデータです。この中で「story」または「stories」という文字が含まれるものは「The Wonderful Story of Henry Sugar」、「The Story of My Life」、「365 Read-Aloud Bedtime Bible Stories」の3つです。
例:投稿タイプがbookで、かつpost_titleにstoryまたはstoriesが含まれている投稿のpost_metaテーブルに「book_type」というキーで「story」という値を登録する
$post_ids = $wpdb->get_col( 'SELECT ID FROM wp_posts WHERE post_type = "book" AND post_title LIKE "%story%" OR post_title LIKE "%stories%"' );

foreach( $post_ids as $post_id ) {
    if ( get_post_meta( $post_id, 'book_type', true ) === '' ) {
        $wpdb->query( $wpdb->prepare( 'INSERT INTO wp_postmeta( post_id, meta_key, meta_value ) VALUES( %d, %s, %s )', (int) $post_id, 'book_type', 'story' ) );
    }
}
上記コードの結果は、以下のようになります。post_titleに「story」または「stories」を含む投稿のpost_idに、「book_type」というキーで「story」という値が保存されているのが分かります。

さいごに

日々WordPressでシステム開発をしていて、よく使用されている$wpdbのメソッドについて復習をかねてまとめてみました。
WordPressのデータベース操作をするときの一助になれば幸いです。