月別アーカイブを特定のカテゴリーのみにしたい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,996

tomy

score 102

前提・実現したいこと

WordPressの中でアーカイブを作っています。
アコーディオン形式で年をクリックするとスライドダウンして、その年の投稿のある月と件数が表示される仕様です。
また、その月をクリックするとアーカイブへのリンクとなってます。

元々投稿のカテゴリーが一つでしたので
archive.php、category-a.php、single-a.phpに下記コードを記載していました。

該当のソースコード

<div id="archives_year">
  <ul class="menu_list">
    <li>
    <?php
$year_prev = null;
$months = $wpdb->get_results("SELECT DISTINCT MONTH( post_date ) AS month ,
                                    YEAR( post_date ) AS year,
                                    COUNT( id ) as post_count FROM $wpdb->posts
                                    WHERE post_status = 'publish' and post_date <= now( )
                                    and post_type = 'post'
                                    GROUP BY month , year
                                    ORDER BY post_date DESC");
foreach($months as $month) :
$year_current = $month->year;
if ($year_current != $year_prev){
if ($year_prev != null){?>
            </ul>
        <?php } ?>
<div class="main_menu"><span><?php echo $month->year; ?></span></div>
<ul class="sub_menu">
    <?php } ?>
    <li>
        <a href="<?php bloginfo('url') ?>/date/<?php echo $month->year; ?>/<?php echo date("m", mktime(0, 0, 0, $month->month, 1, $month->year)) ?>">
            <?php echo date("n", mktime(0, 0, 0, $month->month, 1, $month->year)) ?>月
            (<?php echo $month->post_count; ?>)
        </a>
    </li>
    <?php $year_prev = $year_current;
    endforeach; ?>
</ul>

    </li>
  </ul>
  </div>

発生している問題・エラーメッセージ

①追加で別のカテゴリーbを作ることになりました。
現在の記述ではカテゴリーaとb両方のアーカイブになっていますが、これを元のaのみにしたいです。
上記ソースのpost_typeの後に次のように書きましたがうまくいきませんでした。BDのテーブルが違うのかと考えてます。

and post_type = 'post' and slug = 'a'

②それとは別にイベントというカスタム投稿を作りそちらのアーカイブも作成することになりました。
event organizerというプラグインを使用しています。
https://ja.wordpress.org/plugins/event-organiser/
内容は絞れたのですが、投稿日ではなくイベント日時でのアーカイブにしたいです。
こちらはpost_typeを次のように変更しました。

and post_type = 'event'

SQLはそこまで知識がなくこちらのソースも数ヶ月前に調べながらどうにか作りました。
解決方法や別の作り方がありましたら、ご教授のほどよろしくお願いします。

試したこと

下記ソースにしたところカテゴリーの絞りこみはできたのですが、記事数のカウントが増えてしまいました。
現在1記事のみしか公開していないのですが、4になってしまいます。

$year_prev = null;
$months = $wpdb->get_results("SELECT DISTINCT MONTH( post_date ) AS month ,
                                    YEAR( post_date ) AS year,
                                    COUNT( id ) as post_count
                                    FROM $wpdb->posts
                                    LEFT JOIN $wpdb->postmeta
                                    ON $wpdb->posts.ID = $wpdb->postmeta.post_id
                                    LEFT JOIN $wpdb->term_relationships
                                    ON $wpdb->posts.ID = $wpdb->term_relationships.object_id
                                    LEFT JOIN $wpdb->term_taxonomy
                                    ON $wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id
                                    LEFT JOIN $wpdb->terms
                                    ON $wpdb->term_taxonomy.term_taxonomy_id = $wpdb->terms.term_id
                                    WHERE post_status = 'publish' and post_date <= now( )
                                    and post_type = 'post' and slug = 'column'
                                    GROUP BY month , year
                                    ORDER BY post_date DESC");

問題②解決方法

下記のようにソースを変更して解決しました。

$year_prev = null;
$months = $wpdb->get_results("SELECT MONTH( StartDate ) AS month ,
                                    YEAR( StartDate ) AS year,
                                    COUNT( DISTINCT id ) as post_count FROM $wpdb->posts
                                    LEFT JOIN $wpdb->eo_events
                                    ON $wpdb->posts.ID = $wpdb->eo_events.post_id
                                    WHERE post_status = 'publish' and post_date <= now( )
                                    and post_type = 'event'
                                    GROUP BY month , year
                                    ORDER BY StartDate DESC");
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • kei344

    2016/11/22 20:20

    まだ質問が「受付中」になっていますが、いったん「解決済」にされてはいかがでしょうか。また、解決されていないなら状況をお教えください。

    キャンセル

  • tomy

    2016/11/22 20:29

    発生している問題①は解決しましたが、②についてはこれから取り組みます。

    キャンセル

  • kei344

    2016/11/22 20:43

    それは失礼しました、がんばってください。

    キャンセル

回答 2

checkベストアンサー

+1

group by してるから常にレコードはユニークなのでDISTINCTはいらないような気がします

ちなみに「 and slug = 'a'」にあたってslugカラムはどういうデータを保持していますか
aかbか''のどれかでしょうか?aとbの両方にカテゴライズされることはないのですか?

いずれにしろ、テーブルとかんたんなサンプルを表記し、
期待する結果を提示していただいたほうが適切な回答がつきやすいと思います

 sample

create table wp_posts(ID bigint(20) unsigned not null primary key auto_increment,post_date datetime);
insert into wp_posts values(1,'2016-01-01 00:00:00'),(2,'2016-01-01 01:00:00'),(3,'2016-01-02 00:00:00'),(4,'2016-02-01 00:00:00'),(5,'2016-02-01 01:00:00'),(6,'2016-03-01 00:00:00'),(7,'2017-01-01 00:00:00');


だったとして、抽出するSQLは

select year(post_date) as year 
,month(post_date) as month
,count(*) as count
from wp_posts
group by year,month
order by year desc,month desc;


すると結果は

year      month      count
2017     1     1
2016     3     1
2016     2     2
2016     1     3


なり、PHPでいうとこんな感じ

/*抽出部分は省略*/
$months=[
  (object) ["year"=>2017,"month"=>1,"count"=>1],
  (object) ["year"=>2016,"month"=>3,"count"=>1],
  (object) ["year"=>2016,"month"=>2,"count"=>2],
  (object) ["year"=>2016,"month"=>1,"count"=>3],
];
$html="";
$pre_year=0;
foreach($months as $key=>$month){
  $year=$month->year;
  if($pre_year!==$year){
    if($key>0){
      $html.="</ul>\n";
      $html.="</div>\n";
    }
    $html.="<div>\n";
    $html.=sprintf("<h4>%s年</h4>\n",$year);
    $html.="<ul>\n";
  }
  $html.=sprintf("<a href='#'>%s月(%s)</a></li>\n",$month->month,$month->count);
  $pre_year=$year;
}
if(count($months)>0){
      $html.="</ul>\n";
      $html.="</div>\n";
}
print "<pre>\n";
print htmlspecialchars($html);
print "</pre>\n";

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/22 17:51

    inner joinで繋いでいく限り
    n対nでリレーションしなければ合うと思うんですが
    多段でつなぐとそれなりに調整が必要なのかもしれません

    キャンセル

  • 2016/11/22 19:00

    横入りで失礼しますが、一言だけ。
    「試したこと」で記載されたSQLを見る感じ、$wpdb->postmetaのLEFT JOINがいらないんじゃないかと思います。

    キャンセル

  • 2016/11/22 19:40

    LEFT JOIN $wpdb->postmeta
    ON $wpdb->posts.ID = $wpdb->postmeta.post_id
    まで削除したらうまくできました。ありがとうございます。

    キャンセル

+1

「試したこと」にご提示の SQL文の SELECT句を、以下のように変更してみてください。

 Before
SELECT DISTINCT ... , COUNT( id ) as post_count
 After
SELECT ... , COUNT( DISTINCT id ) as post_count


 COUNT( id )では、テーブルを JOIN する過程で 1対n (n > 1) 関係のレコードが存在した場合、
重複を除外せずに id が NULL でないレコードの数を数え上げてしまいます。
https://dev.mysql.com/doc/refman/5.6/ja/group-by-functions.html#function_count

SELECT ステートメントで取得された行に含まれる expr の非 NULL 値の数を返します。

一方、COUNT( DISTINCT id )とすると、重複を除いた上で id が NULL でないレコードの数を取得できます。
https://dev.mysql.com/doc/refman/5.6/ja/group-by-functions.html#function_count-distinct

MySQL では、式のリストを指定することで、NULL が含まれない個別の式の組み合わせ数を取得できます。


また、yambejp様が回答で指摘されている通り、SELECT句自体の DISTINCT は不要です。

SELECT句内で集約関数を使用していないカラムと GROUP BY句に指定しているカラム(つまり、monthyear)が一致しているため、取得するデータ自体に重複はあり得ないからです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/22 19:40

    こちらでもできました!ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.21%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる