今回の投稿は、松戸WordPress部の勉強会「クエリ-とりあえず最終回」のフォローアップです。
WordPress開発勉強中の方が、思い通りのクエリを実装するためのプロセスを示すことに重きをおいた初級者用の記事です。調べ方のコツをつかんでいただければよいのですが・・・。
記事の内容:
この記事の経緯
WordPress部@松戸Banana ClusterでWP勉強会を定期開催してきましたが、この投稿はクエリシリーズの3回目の解説ということになります。
過去2回のクエリシリーズ勉強会のスライド:
そして第三回目は、第二回のスライドの最後に列挙した演習問題のライブ実装でした。
勉強会でもそうでしたが、回答を示したというよりも、WordPress Codexをちゃんと参照して、Codex に掲載されているサンプルコードをどのように活用できるか、実装中の障害をどのように解決するか、そのプロセスをぜひ習得していただきたいと思っています。
wp_query(), get_posts(), pre_get_posts()などのクエリ関係の関数を始めとして、なんでも自力で実装できるようになることを目指したいものです。
まず演習環境を作ろう
大雑把ですが:
- WordPressテスト環境:XAMPP/MAMP などのローカルでもレンタルサーバー上でも構わないので、自由にテストできるWordPressの環境
- テーマ:TwentyThirteen
- テストデータのインポート:theme-test-data-jaを利用
- 上記のリンク先ページの右下 [Download ZIP] をクリックして適当な場所にダウンロード
- ダウンロードしたZIPを解凍しておく
- [WP管理画面/ツール/インポート] から 「WordPress」 を選択
- 初めて利用する場合は、プラグイン WordPress Importer をインストール
- WordPress Importer で先ほど解凍したファイルから WordPress-theme-test-data-ja.xml を選択してアップロード
- すると、いろいろ英語で表示されるが、一番下の [submit] をクリック
- [管理画面/投稿/投稿一覧] から 「固定表示」投稿を削除しておいてください
(上部のリンク「固定表示」で簡単に見つけることができます)
この theme-test-data-ja は「WordPressテーマユニットテストデータ」といって、テーマを開発する際に、投稿や固定ページやコメントなどが正しく表示されるかを確認するためのダミーデータです。
このようなデータをメンテナンスしてくださる方に感謝しましょう!詳しくはこちら
クエリ演習課題
WordPressのためのPHP-基礎の基礎の最後に掲載していましたが、若干調整しました。
- 演習1:トップページに新着情報を3件表示。
- 演習2:トップページの新着情報で「マークアップ」は除外。
- 演習3:トップページの新着情報でタグ「テンプレート」は投稿内容も 表示
- 演習4:1ページ10件表示。でも「テンプレート」だけ5件表示で。
- 演習5:検索結果から固定ページは除外
今回の演習では体裁等は無視しています。
演習課題の実装例
演習1:トップページに新着情報を3件表示。
wp_query() で実装してみます。(get_posts()でも可)
ステップ1:編集するファイルを判断。TwentyThirteenでトップページを表示しているメインのファイルはindex.php
ステップ2:Codex の関数リファレンス/WP_Query から適当なサンプルをindex.php、とりあえず get_header(); ?> の直下にコピペ
<h1>演習1</h1> <?php // クエリ $the_query = new WP_Query( $args ); // ループ while ( $the_query->have_posts() ) : $the_query->the_post(); echo '<li>'; the_title(); echo '</li>'; endwhile; // 投稿データをリセット wp_reset_postdata(); ?>
ステップ3:とにかく表示を確認。トップページを表示してみるとエラー
Notice: Undefined variable: args in D:\xampp\htdocs\wp_study\wp-content\themes\twentythirteen\index.php on line 25
ステップ4:index.phpの25行目(上述では5行目)が問題らしい。「args が定義されていない」らしいことが分かる。(実装により行数は異なります)
ステップ5:Codexから $args の例を参照していくと次のような表示数に言及しているっぽいコードを発見
$args = array( 'posts_per_page' => 1, 'post__in' => get_option( 'sticky_posts' ), 'ignore_sticky_posts' => 1 );
これを、$the_query = new WP_Query($args); の前に挿入してみる
ステップ6:表示を確認すると、エラーは解消されて「Hello world!」と最新記事のタイトルが表示された
ステップ7:件数を調整して、いらいない行を削除して、また表示を確認
<h1>演習1</h1> <?php // クエリ $args = array( 'posts_per_page' => 3 ); $the_query = new WP_Query($args); // ループ while ( $the_query->have_posts() ) : $the_query->the_post(); echo '<li>'; the_title(); echo '</li>'; endwhile; // 投稿データをリセット wp_reset_postdata(); ?>
ステップ8:目的とする「新着記事を3つ表示」が実現されたら、体裁や構造を調整して完成
<h1>演習1</h1> <?php // クエリ $args = array( 'posts_per_page' => 3 ); $the_query = new WP_Query($args); // ループ if ( $the_query->have_posts() ) : ?> <dl> <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?> <dt><?php the_date(); ?></dt> <dd><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></dd> <?php endwhile; ?> </dl> <?php endif; // 投稿データをリセット wp_reset_postdata(); ?>
ステップ9:最終的に表示を確認して問題なければ完了。エラーが表示されたら、大丈夫だったポイント(ステップ8)に戻ってみる。
演習2:トップページの新着情報で「マークアップ」は除外。
演習1を終了すると、表示されたその3件目が 「マークアップ: HTML タグとフォーマット」 という カテゴリ「マークアップ」の記事 になっているはずです。
この「マークアップ」のカテゴリを除外しようというケースです。(実践では「ブログ」のカテゴリだけを除外したいというような場合に応用できます。)
ステップ1:カテゴリをどうにかしようということなので、参照している前述の Codex のページ先頭にある 「目次」 を参照してみると、「5.2 カテゴリーパラメータ」 とある。ここを見て見る。
ステップ2:『特定のカテゴリを除外』ということだったので次のパラメータが使えそう?
category__not_in (array) - use category id.
使い方は、配列(array)で、カテゴリID(use category id)を指定するということが分かる。
「マークアップ」のカテゴリIDを調べるには、[管理画面/投稿/カテゴリ] で 「マークアップ」を検索して、「カテゴリの編集」ページにアクセスすると、そのURLに tag_ID=37 というような記述があるので、この環境の場合は カテゴリID が 37 ということが分かる。(IDは環境によって異なります。)
ステップ3:上記の設定を「演習1」のコードに追加すると:
<h1>演習2</h1> <?php // クエリ $args = array( 'posts_per_page' => 3, 'category__not_in' => array('37') ); $the_query = new WP_Query($args); // ループ if ( $the_query->have_posts() ) : ?> <dl> <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?> <dt><?php the_date(); ?></dt> <dd><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></dd> <?php endwhile; ?> </dl> <?php endif; // 投稿データをリセット wp_reset_postdata(); ?>
ステップ4:表示を確認してOKなら、完了!何か問題があるなら トライアンドエラー を繰り返す。
演習3:トップページの新着情報でタグ「テンプレート」は投稿内容も 表示
「演習2」を終えたら、その3件目に 「テンプレート: アイキャッチ画像 (縦)」 という記事タイトルが表示されているはずです。この記事の場合だけ、本文も表示するというのが「演習3」です。(実践では、トップページの「新着情報」欄に投稿を自動表示させ、特定のカテゴリ(例えば「お知らせ」)に投稿されたものだけは本文も表示して、年末年始のお知らせを表示させたいというようなことがあります。)
ステップ1:「これはクエリは関係するの?」と考えてみると、「特定のカテゴリだけその表示を変える」ということ。ということはクエリについては「演習2」のコードのままで、その投稿ごとの表示を変えればOK!WordPressからの観点で表現してみると、「表示された投稿が特定のカテゴリだったという条件分岐」なので・・・
ステップ2:「WordPress 条件分岐」 でググってみると、WordPress Codex の「条件分岐」 がヒットする。
ステップ3:「目次」の「2.12 カテゴリーページ」が関係しそうなので見てみる。
ステップ4:Codex をよく見てみる。
is_category() あるカテゴリーのアーカイブページが表示されている場合。 ・・・中略・・・ in_category( '5' ) (注: in_category です。) 現在の投稿がカテゴリーID 5に属する場合にtrueを返します。 詳細
is_category の説明は「アーカイブページが表示されている場合」
in_category の説明は「投稿が・・・属する場合」
ということなので、「今回のケースはどっち」 と考えると、アーカイブページではなく、表示されている投稿を評価するので、in_category ということが分かる。
ステップ5:とにかく実装してまず試してみる。「テンプレート」のカテゴリIDは35。(環境によって異なります。)
<h1>演習3</h1> <?php // クエリ $args = array( 'posts_per_page' => 3, 'category__not_in' => array('37') ); $the_query = new WP_Query($args); // ループ if ( $the_query->have_posts() ) : ?> <dl> <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?> <dt><?php the_date(); ?></dt> <?php if ( in_category('35') ) : ?> <dd><p><strong><?php the_title(); ?></strong></p> <?php the_content(); ?></dd> <?php else: ?> <dd><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></dd> <?php endif; ?> <?php endwhile; ?> </dl> <?php endif; // 投稿データをリセット wp_reset_postdata(); ?>
ステップ6:表示を確認してOKなら、完了!何か問題があるなら トライアンドエラー を繰り返す。パラメータの記述を変えてみたり、関数を変えてみたり。都度、自分が何を試しているのかを明確に理解するように努める。
演習4:1ページ10件表示。でも「テンプレート」だけ5件表示で。
ここからは、トップページのサブクエリの話ではなく、アーカイブページのメインクエリの話であることを明記することが、まず重要!
ステップ1:まず標準は「1ページ10件表示」なので、これは[管理画面/設定/表示設定]の「1ページに表示する最大件数」で設定。
ステップ2:まず標準設定の状態で、「テンプレート」アーカイブページの表示を確認する。設定のとおり10件表示されている。
ステップ3:「WordPress アーカイブ メインクエリ 変更」などでググってみて、Codex の記述を探してみると、「プラグイン API/アクションフック一覧/pre get posts」という記事がヒットする。
ステップ4:「『メインクエリから特定のカテゴリだけ表示件数を・・・』というケースにこの関数は使えるのかな?」と説明を見ていくと、「使用例」に「メインページから特定のカテゴリーを除外する」と酷似した内容のものがあります。その部分の説明に「プラグイン内(訳注:テーマのfunctions.phpでもいい?)で」とあるので、functions.php に記述してみる。
function exclude_category( $query ) { if ( $query->is_home() && $query->is_main_query() ) { $query->set( 'posts_per_page', '5' ); } } add_action( 'pre_get_posts', 'exclude_category' );
3行目:「使用例」では、特定のカテゴリ除外だったので、ここを表示件数に変更。
ステップ5:「テンプレート」アーカイブページの表示を確認してみると何も変わっていないので、よくよくコードを見てみる。
2行目の if文 に is_home() があるので、この条件ではトップページにのみの適用となるので、その条件を外してみる。
ステップ6:「テンプレート」アーカイブページの表示を確認してみると5件表示になっている。これで、pre_get_posts が機能することが分かった。いきなり複雑なコードを実装しようとするのではなく、「正常に動作する」ポイントを探ってまずは起点を明確にする。
ステップ7:でも当然、「テンプレート」に限定する条件を記述していないのでその条件を付記。
function exclude_category( $query ) { if ( is_category('35') && $query->is_main_query() ) { $query->set( 'posts_per_page', '5' ); } } add_action( 'pre_get_posts', 'exclude_category' );
ステップ8:表示を確認してみてOKなら機能的にはOK。ファンクション名を変更してコメントも付記、その他ちょっと整理して・・・。後から参照した時に何のためのコードか分かるようにしておくことも大事。
/** * カテゴリ「テンプレート」アーカイブページの表示件数 */ function posts_of_template( $query ) { if ( !is_admin() && is_category('35') && $query->is_main_query() ) { $query->set( 'posts_per_page', '5' ); } } add_action( 'pre_get_posts', 'posts_of_template' );
演習5:検索結果から固定ページは除外
これは、同じpre_get_postsのページを見ると、「検索結果から固定ページを除外」を参照して、そのままのコードで実装できるのでステップ解説は省略いたします。
実装調査のコツ
■ WordPress Codex をしっかり活用する
当ブログも含めてWPについて解説したりTIPSを紹介している記事は数多あります。それらも役にたちますが、調査は WordPress Codex の記事から始めてください。ほとんどことはちゃんとそこに書いてありますし、その情報はほぼ最新です。
■ 起点を明確に定める
最初から複数の要素を盛り込んだ複雑なコードを書くのではなく、目的の第一歩を確実に定めるのがコツです。方向性は合っているのか、ステップを進めるごとに確認して、進めなくなったら、起点に戻って考え直します。どんなに複雑なロジックを組むプログラマでも、結局はこのプロセスを踏んでいるのです。
■ 絶えず確認してトライアンドエラーを繰り返す
起点を定めたら次のステップに順番に進んでいくのですが、必ず躓きます。要素を一つ一つ実装しては確認というプロセスを追っているなら、どのコードが問題なのかをすぐに判別できます。どうしようもなくなることもしばしばありますので、その状態に陥ったら必ず起点に戻ることができるようにしておきます。
■ 検索ワードはWordPressの観点で
調査のためにググる時は、やりたいことを整理して、WordPressを主語にしたらどういうことなのか考えてみて、検索ワードを決める。(演習3のステップ1-2を参照)
まず手を動かしてみるのが一番ですね。