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

PRESSMAN*Tech

[WordPress]WP_Queryのクエリ数を削減する[WP10]

WP10は弊社が掲げる「WordPressを使った10倍速開発」です。
10倍とは、フルスクラッチに比べて10倍ということです。

この連載ではWP開発に長く携わっている弊社のWordPress開発ベストプラクティスを発信していきます。

第四回はWP_Queryで取得した値をプロパティに格納し、第三回のコードのクエリ数を削減します。

前回のコード

<?php
add_action( 'manage_users_columns',  'add_column_headers' , 10, 3 );
add_action( 'manage_users_custom_column', 'add_column_body', 10, 3 );

/**
  * 「投稿タイトル」カラムを追加します
  *
  * @param $column_headers
  *
  * @return array
  */function add_column_headers( $column_headers ) {
    $column_headers['post_title'] = '投稿タイトル';

    return $column_headers;
}

/**
 *  ユーザーごとの投稿タイトルを表示します
 *
 * @param $custom_column
 * @param $column_name
 * @param $user_id
 *
 * @return string
 */function add_column_body( $custom_column, $column_name, $user_id ) {

    // カラムが「入稿タイトル」でない場合は処理を終了する
    if ( $column_name !=='post_title' ) {
        return;
    }

    // ユーザーごとの投稿を取得
    $query = new WP_Query(
        [
            'author'         => $user_id,
            'posts_per_page' => - 1,
            'post_type'      => 'post',
            'post_status'    => [
                'publish'
            ]
        ]
    );

    // 投稿のタイトルを配列に格納
    $post_titles = [];
    foreach ( $query->posts as $post ) {
        $post_titles[] = $post->post_title;
    }

    // 配列を文字列に変換
    $custom_column = implode( '<br>', $post_titles );

    return $custom_column;
}

コードの問題点

フックmanage_users_custom_columnで関数add_column_bodyを動かしています。このフックはユーザーの行ごとに通ります。上記画像ではユーザーは3名なので、このフックには3回通ります。つまり、関数add_column_bodyが3回呼ばれるため、WP_Queryが3回実行されるということです。

ユーザー一覧画面では1ページに表示される最大ユーザー数は20なので、その場合はWP_Queryが20回も動くことになります。クラス化してクエリ数が無駄に増えるのを避けましょう。

クラス化してWP_Queryの結果をプロパティに格納する

<?php

// 上記コードから変更を加えた箇所しか記載していないため、
// このままでは動かないのでご注意ください
class User_List_Extra_Column {

    private $query;

    public function __construct() {
        add_action('manage_users_custom_column', [$this,'add_column_body'], 10, 3);
        $this->fetch_all_posts();
    }

    public function add_column_body($custom_column, $column_name, $user_id) {

        // カラムが「入稿タイトル」でない場合は処理を終了する
        if ($column_name !== 'post_title') {
            return;
        }

        // 投稿タイトルを配列に格納
        $post_titles = [];
        foreach ($this->query->posts as $post) {
            if ((int)$post->post_author !== $user_id) {
                continue;
            }

            $post_titles[] = $post->post_title;
        }

        // 配列を文字列に変換
        $custom_column = implode('<br>', $post_titles);

        return $custom_column;
    }

    private function fetch_all_posts() {
        $query = new WP_Query(
            [
                'posts_per_page' => - 1,
                'post_type'      => 'post',
                'post_status'    => [
                    'publish'
                ]
            ]
        );

        $this->query = $query;
    }
}

fetch_all_postsというメソッドにクエリを全部任せてしまいます。返ってきた結果をprivateプロパティ$queryに格納します。

次に、add_column_bodyメソッドでプロパティを参照し、その投稿がユーザーのものであれば入稿タイトルを配列に格納します。

これでWP_Queryを一度しか呼び出さずに済みました。

効果の検証

クエリ数をデフォルトの状態・前回のコード・今回のコードで比べてみましょう。今回はQuery Monitorというプラグインでクエリ数を測ります。

https://www.pressmantech.com/tech/cording/4987

ユーザーを新しく20名作成します。

デフォルトのクエリ数

Query Monitorの結果は左上の茶色い部分に表示されています。

デフォルトでは29クエリ走っているのがわかります。

前回のコードのクエリ数

前回のコードではなんと89クエリも走っています。

デフォルトでは29クエリなので、89 - 29 = 60。表示されているユーザー数は20なので、1ユーザーに対して3クエリ走っていることになります。

では、今回のコードのクエリ数はいくつでしょうか。

今回のコード

なんと32クエリです。

WP_Queryはコンストラクタで1回呼ばれるだけなので、29 + 3 = 32で3クエリだけ増えているのも納得です。

しっかりクエリ数を削減することができました!

全体のコード

シングルトンパターンにしています。

<?php

User_List_Extra_Column::get_instance();

class User_List_Extra_Column {

private static $instance;
private $query;

public function __construct() {
add_action( 'manage_users_columns', [ $this, 'add_column_headers' ], 10, 3 );
add_action( 'manage_users_custom_column', [ $this, 'add_column_body' ], 10, 3 );
$this->fetch_all_posts();
}

/**
 * インスタンス返却します
 *
 * @return User_List_Extra_Column
 */public static function get_instance() {
if ( !isset( static::$instance ) ) {
static::$instance = new static;
}
return static::$instance;
}

/**
 * 投稿タイトル」カラムを追加します
 *
 * @param $column_headers
 *
 * @return array
 */public function add_column_headers( $column_headers ) {
$column_headers['post_title'] = '投稿タイトル';

return $column_headers;
}

/**
 *  ユーザーごとの投稿タイトルを表示します
 *
 * @param $custom_column
 * @param $column_name
 * @param $user_id
 *
 * @return string
 */public function add_column_body( $custom_column, $column_name, $user_id ) {
// カラムが「入稿タイトル」でない場合は処理を終了する
if ( $column_name !== 'post_title' ) {
return;
}

// 投稿タイトルを配列に格納
$post_titles = [];
foreach ( $this->query->posts as $post ) {
if ( (int) $post->post_author !== $user_id ) {
continue;
}

$post_titles[] = $post->post_title;
}

// 配列を文字列に変換
$custom_column = implode( '
', $post_titles ); return $custom_column; } /** * WP_Queryで取得したデータをプロパティに格納します */private function fetch_all_posts() { $query = new WP_Query( [ 'posts_per_page' => - 1, 'post_type' => 'post', 'post_status' => [ 'publish' ] ] ); $this->query = $query; } }

WP_Queryが何回も走ってWPが重くなっている場合は、結果をクラスのプロパティに格納しちゃいましょう!

以上、クエリ数の削減についてでした。