プラグインなしで直近のカテゴリーだけから関連商品(記事)を表示させる方法
スポンサーリンク
他にやりたい人がいるかわかりませんが、自分の備忘録としてメモ。
Welcartをベースに、WordPressでECサイトを自分で作ろうとすると、意外と「カテゴリー分け」がネックになってきます。特に、プラグインなしでパンくずリストや関連商品を表示させたい時です。
WordPressのプラグインは便利ですが、裏で様々に複雑な処理を行うため、プラグインの組み合わせによっては不具合を起こしたり、処理が重くなり、サイトの表示速度が遅くなります。SEO対策にも、プラグインは少ないに越したことはありません。
プラグインを使わず、直近のカテゴリーだけで関連記事を表示させる方法は、当然Welcartユーザーでなくても利用できるものになっています。
もくじ
- Welcartのカテゴリー分けについて
- よくある「プラグインなしで関連記事を表示させる方法」とは
- じゃあ、Welcartではどうすれば?
- 直近のカテゴリーだけから関連記事を表示させる方法
- 関連記事を表示させるコード:まとめ(記事タイトルだけの場合)
- Welcartで、商品画像や価格も表示させるには
1. Welcartのカテゴリー分けについて
Welcartでは、全てのアイテムが「商品(スラッグ:item)」というカテゴリーに入ることになります。
- 商品(親カテゴリー)
- パン(子カテゴリー1)
- メロンパン
- クリームパン
- カレーパン
- 飲み物(子カテゴリー2)
- 牛乳
- オレンジジュース
- コーヒー
- パン(子カテゴリー1)
というように、「商品」カテゴリーを最上位の親カテゴリーとしたツリー構造になっています。
ツリーの最下位は商品そのもの(商品詳細ページ)であり、それらには複数のカテゴリーに属させることが可能です。
2. よくある「プラグインなしで関連記事を表示させる方法」
Googleで検索すると、たくさんの先駆者さん達が、「プラグインなしで関連記事を表示させる方法」を紹介してくれています。
カテゴリーベース
同じカテゴリー内から、関連記事を取得する方法
- How to: Related Posts with Thumbnails in WordPress without Plugins
- HOW TO SHOW RELATED ARTICLES WITHOUT ANY WORDPRESS PLUGIN USING CATEGORY
タグベース
同じタグが付いている記事から、関連記事を取得する場合
- WordPressでプラグインなしで関連する記事を一発で呼び出す関数をfunctions.phpに記述してみる
- 関連する記事を表示する – プラグインを使わずWordPressに9の機能をつける
それぞれの方法は素晴らしいのですが、いかんせん、Welcartではこれらでは不十分です。なぜなら。
- カテゴリーベース:全ての投稿が「商品」カテゴリー下にあるため、その全てが関連記事の候補になってしまう。
- タグベース:ブログならともかく、商品一つ一つに細かくタグ付けするのは面倒。
Welcartならではの悩みかもしれません。
3. じゃあ、Welcartではどうすれば?
「メロンパン」のページを見ているときに、関連記事として「クリームパン」「カレーパン」を表示させたい。でも、最上位の「商品」カテゴリーまで取得されちゃうと、「牛乳」や「コーヒー」まで表示されしまう。
→同じカテゴリーに属する記事を取得、でも直近の「パン」カテゴリーだけを取り上げればいい!
そうすれば、「カテゴリーベースの関連記事表示」をアレンジして使えます。
4. 直近のカテゴリーだけから関連記事を表示させる方法
以下、解説です。
4-1.直近のカテゴリーを取得する方法
WordPressのテンプレートタグでは、カテゴリーを取得する関数はいくつかあります。
- get_the_category – 現在の記事属するカテゴリーを取得する
- the_category – 現在の記事が属するカテゴリへのリンクを取得する
- その他いろいろ
しかし、「現在の記事が属する直近のカテゴリーを取得する」関数はありません。そこで、以下の関数を使います。
- get_term_children – 子孫タクソノミーIDを取得する
※「タクソノミー」は、「カテゴリー」と同等と考えてOKです。
子孫カテゴリーを持たないカテゴリーだけをピックアップすることで、「パン」や「飲み物」カテゴリーだけを取得します。
※現在の投稿ページが「パン」「飲み物」カテゴリーに属していて、更に「パン」「飲み物」カテゴリーが子孫を持つと、この方法では対応不可という欠点はありますが・・・(あまりないケースかと・・・もっと出来る方、教えて下さいm(_ _)m)。
- get_the_categoryで、現在の記事が属するカテゴリー情報を全て取得、$categoriesに代入。
- 取得したいカテゴリーIDだけを代入するための箱、$category_idsを用意。
- $categoriesから、カテゴリーを一つずつ$categoryとして取得する繰り返し命令(foreach)。
- $categoryから、カテゴリーIDを取得。
- カテゴリーIDから、そのカテゴリーの子孫を取得。
- $categoryが子孫を持たない場合(if条件)
- 3で用意した$category_idsの箱の中に、子孫を持たないカテゴリーのIDを代入。
- 6の条件(if)終了。
- 3の繰り返し命令(foreach)終了。
$categories = get_the_category($post->ID); $category_ids = array(); foreach( $categories as $category){ $category_id = $category->term_id; $category_child = get_term_children($category_id, 'category'); if($category_child != true){ $category_ids[] = $category->term_id ; } }
ここまでやると、最終的に$category_idsという箱の中に、「子孫カテゴリーを持たないカテゴリーのID」が入れられることになります。
冒頭のツリーの例で言うと、「メロンパン」の投稿ページが表示されている時に、
- 属しているカテゴリーを全て取得 → 「商品」「パン」カテゴリーを取得。
- 「商品」カテゴリーが子孫を持つか確認。持っているので$category_idsの箱には入れない。
- 「パン」カテゴリーが子孫を持つか確認。持っていないので$category_idsの箱に入れる。
という感じで、$category_idsには「パン」カテゴリーのIDだけがセットされます。
もちろん、「メロンパン」が例えば更に「人気商品」というカテゴリー(子カテゴリーなし)というカテゴリーに属していれば、そのIDも$category_idsの箱の中にセットされます。
4-2. 表示させたい投稿ページの情報を整理
他のページの情報を取得して表示する場合には、WordPressのクエリを使いますが、そのクエリで表示する投稿ページを制御するために、条件をつけてフィルタリングします。
今回は以下のように設定してみました。
- カテゴリー:直近のカテゴリー(先ほど取得)に属する
- 投稿ページ:現在の投稿以外
- 表示する件数:4件
- 「先頭に固定表示」されている投稿:無効にする
- 表示順:ランダム
以下のコードの1行目で$argsという変数(箱)を用意し、その中に表示させる投稿の引数(条件)を代入します。
$args=array( 'category__in' => $category_ids, 'post__not_in' => array($post->ID), 'posts_per_page'=> 4, 'ignore_sticky_posts'=> 1, 'orderby' => 'rand', );
引数は他にも色々あり、様々に設定できますので、詳しくは以下WordPressリファレンスをご覧下さい。
テンプレートタグ:query_posts - WordPress Codex 日本語
4-3. クエリを使って表示
WordPressには3種類ほどクエリを生成するテンプレートタグがありますが、ここではWP_Queryを使用。WP_Queryを使う理由は、以下のサイトにめちゃめちゃ詳しく解説されていますので、興味のある方はどうぞ。
カスタム投稿タイプのFAQページを作りながら、WP_Query、rewind_posts、query_posts、pre_get_postsの表示パフォーマンスを比較してみた
先ほど、表示させる投稿の条件を入れた$argsを、WP_Queryに代入します。
$my_query = new WP_Query($args); if( $my_query->have_posts() ) { while ($my_query->have_posts()) { $my_query->the_post(); //ここに表示させたい内容を記載 } wp_reset_query(); }
「//ここに表示させたい内容を記載」と記したところに、例えば、
echo '<a href="' . get_post_permalink() . '">' . get_the_title() . '</a>';
と入れると、関連記事のページへのリンクが表示出来ます。echoは、ブラウザ上に表示を行うためのPHPの命令です。
出力されるリンクは、<ul>要素で囲ってリスト形式にしたほうがSEO的にも好ましいと思われるので、この場合なら以下のようにするのがいいでしょう。
$my_query = new WP_Query($args); if( $my_query->have_posts() ) { $html =''; $html .= '<ul>'; while ($my_query->have_posts()) { $my_query->the_post(); $html .= '<li><a href="' . get_post_permalink() . '">' . get_the_title() . '</a></li>'; } echo $html; wp_reset_query(); } }
上記のコードでは、echoで直接htmlタグを出力させるのではなく、一旦$htmlという変数に出力させたい内容を代入していき、最後にecho $html;として出力させています。
5. 関連記事を表示させるコード:まとめ(記事タイトルだけの場合)
ここまでのコードをまとめて、関連記事を表示させるための新たな関数related_posts_listとして、functions.phpに記入。
function related_posts_list(){ global $post; $categories = get_the_category($post->ID); if( $categories ){ $category_ids = array(); foreach( $categories as $category){ $category_id = $category->term_id; $category_child = get_term_children($category_id, 'category'); if($category_child != true){ $category_ids[] = $category->term_id ; } } $args=array( 'category__in' => $category_ids, 'post__not_in' => array($post->ID), 'posts_per_page'=> 4, 'ignore_sticky_posts'=> 1, 'orderby' => 'rand', ); $my_query = new WP_Query($args); if( $my_query->have_posts() ) { $html =''; $html .= '<ul>'; while ($my_query->have_posts()) { $my_query->the_post(); $html .= '<li><a href="' . get_post_permalink() . '">' . get_the_title() . '</a></li>'; } echo $html; wp_reset_query(); } } }
あとは、関連記事を表示させたいテンプレート(例えばsingle.php)の好きな場所に、
<?php related_posts_list(); ?>
と記述すれば、関連記事が表示されます。
6. Welcartで、商品画像や価格も表示させるには
4-3.で「//ここに表示させたい内容を記載」と記したところに、welcart専用のテンプレートタグを入れれば、表示が可能です。
- usces_the_itemImage($number, $width, $height) … 商品画像の出力
- usces_the_firstPrice … 先頭の売価
- usces_guid_tax() … 消費税ガイド(税込か税別か)
Welcartテンプレートタグ一覧
http://www.welcart.com/community/archives/799
functions.phpにこれらを直接記載してもいいのですが、これまたかなり複雑になりそうなので、私は別にthumbnail-box.phpというテンプレートを用意し、使用中テーマ内に設置しました。
thumbnail-box.phpには、以下のように記述します。4行目のwidthとheightが、画像の大きさを指定している部分です。ここでは両方とも200pxとしています。また、商品名にはh2タグを適用させていますが、これも自由に変えて頂いて結構です。
<li> <?php usces_the_item(); usces_have_skus(); ?> <a href="<?php the_permalink(); ?>"> <?php usces_the_itemImage($number=0, $width=200, $height=200); ?> <h2><?php usces_the_itemName(); ?></h2> ¥<?php usces_the_firstPrice(); ?><?php usces_guid_tax(); ?> </a> </li>
これを、4-3で「//ここに表示させたい内容を記載」とした所で、thumbnail-box.phpを呼び出すコード(下記)を代入すればOKです。
echo get_template_part('thumbnail-box');
まとめると、コードは以下のようになります。
function related_items_list(){ global $post; $categories = get_the_category($post->ID); if( $categories ){ $category_ids = array(); foreach( $categories as $category){ $category_id = $category->term_id; $category_child = get_term_children($category_id, 'category'); if($category_child != true){ $category_ids[] = $category->term_id ; } } $args=array( 'category__in' => $category_ids, 'post__not_in' => array($post->ID), 'posts_per_page'=> 4, 'ignore_sticky_posts'=> 1, 'orderby' => 'rand', ); $my_query = new WP_Query($args); if( $my_query->have_posts() ) { while ($my_query->have_posts()) { $my_query->the_post(); echo get_template_part('thumbnail-box'); } wp_reset_query(); } } }
スタイルシートはここでは設定していないので、いい具合に指定させて下さい。display:inlineなどを加えて横並びになるようにすれば、ばっちりでしょう!
ちょくちょく参考にさせていただいています。
>※現在の投稿ページが「パン」「飲み物」カテゴリーに属していて、更に「パン」「飲み物」カテゴリーが子孫を持つと、この方法では対応不可…
カテゴリの最下層のみ欲しい、というシチュエーションはままありますよね。子カテゴリから探すのではなく、先祖カテゴリを消していく方法で作った関数です。それほど重たくはない・・・はずです。よろしければどうぞ。
コメント有難うございます。早速試させて頂きますね!学生時代に授業のFortranでなんとか可を貰ったものの、実際は全くわからなかったトラウマからどうもプログラミングは苦手で眠くなってしまうので、、、さくっとこんな風にコードが書ける方が羨ましいです。
いえ。私もプログラムは必要に迫られて見よう見まねでやるだけなんで。これを機に、関連する関数をまとめることにしました。以下、ちょっと修正を。
最上段のget_the_terms( $post_id, ‘category’ )をget_the_terms( $post->ID, ‘category’ )に、重複している $sbjcats = $candidate; は二つ目のループ内のを削除、で。
また寄らせていただきますね。いつも良い情報をありがとうございます^^
修正有難うございました。半分自分の勉強の為に書いているようなブログなので、私もまだまだ色々な方の記事から勉強させて頂いてばかりです^^; fp様のブログも参考に、また試してみますね!
[…] だが、先日welcart関連のカスタマイズを調べていると【プラグインなしで直近のカテゴリーだけから関連商品(記事)を表示させる方法】という記事に行き当たり、最下層カテゴリを取得 […]
初めまして。当サイトを参考に「 同階層カテゴリーからの関連商品表示 」
表示させてみました。デバックモードをTRUEに設定して確認したところ。
エラーが出力されました。
下記の内容はエラーコードです。
Notice: Use of undefined constant rand – assumed ‘rand’ in functions.php on line 148
ご指摘ありがとうございます。まさにその英文の通りです。早速、記事内のコードを修正しておきますね。