logo

Now Loading...

WEBサイト作りノウハウ WEBノウハウメモ

  • HOME
  • 記事一覧
  • カスタム投稿をカスタムタクソノミーのslugを条件として投稿を絞り込む

カスタム投稿をカスタムタクソノミーのslugを条件として投稿を絞り込む

cover image

カテゴリー:wordpress自作テーマ作成メモ

作成日付:2020年2月24日

更新日付:2020年2月24日

Wordpressで、$wpdbを使用してカスタムタクソノミーを条件としたSQL文を作成します。

目的

WordPressで同じカスタム投稿の中からカスタムタクソノミーの値をキーに投稿記事を抽出したい、
例えば、Wordpressでサイトを運用していて、サイト内で日本語記事ページと英語記事ページがあった場合、
カスタムタクソノミーで英語記事である目印’en’をつけておく。 日本語記事にはタクソノミーを何もつけない

最初1ページに記事を数件表示し、もっと記事を表示したい時はAJAXを使用してさらに記事を読み込む。その場合に $wpdbを使用して自分でSQL文を作成する。

同じカスタム投稿の中から以下のように分けて記事を抽出するのが期待値

  • 日本語投稿記事の場合は記事の中からカスタムタクソノミーに’en’がついていない記事を読み込む
  • 英語投稿記事の場合は記事の中からカスタムタクソノミーに‘en’がついてる記事を読み込む

結果

カスタムタクソノミーに’en’がついていない記事の場合

$now_post_num, $get_post_numはオフセット、何個目から何個目までというのを決定するために使っている
カスタムタクソノミーが‘en’じゃないことと、slugがNULLの場合を取得している


$sql = "SELECT
    p.ID,p.post_title,p.post_content,p.post_type,p.post_status,tte.slug
  FROM
    $wpdb->posts AS p
    LEFT OUTER JOIN $wpdb->term_relationships AS tr ON tr.object_id = p.ID
    LEFT OUTER JOIN $wpdb->term_taxonomy AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
    LEFT OUTER JOIN $wpdb->terms AS tte ON tte.term_id = tt.term_id
  GROUP BY
    p.ID
  HAVING
    p.post_type = 'articles' AND p.post_status = 'publish' AND (tte.slug != 'en' OR tte.slug IS NULL)
  ORDER BY
    p.post_date DESC
  LIMIT 
    $now_post_num, $get_post_num;";

$pre = $wpdb->prepare($sql,$now_post_num,$get_post_num);
$results = $wpdb->get_results($pre);

カスタムタクソノミーに’en’がついている記事の場合

基本的には上記と同じだが
カスタムタクソノミーが‘en’のみの場合を取得している。違いはHAVING句の箇所のみ。


$sql = "SELECT
    p.ID,p.post_title,p.post_content,p.post_type,p.post_status,tte.slug
  FROM
    $wpdb->posts AS p
    LEFT OUTER JOIN $wpdb->term_relationships AS tr ON tr.object_id = p.ID
    LEFT OUTER JOIN $wpdb->term_taxonomy AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
    LEFT OUTER JOIN $wpdb->terms AS tte ON tte.term_id = tt.term_id
  GROUP BY
    p.ID
  HAVING
    p.post_type = 'articles' AND p.post_status = 'publish' AND tte.slug = 'en'
  ORDER BY
    p.post_date DESC
  LIMIT 
    $now_post_num, $get_post_num;";

$pre = $wpdb->prepare($sql,$now_post_num,$get_post_num);
$results = $wpdb->get_results($pre);

方法

フロー

1SQLを検討する
2$wpdbに置き換える

SQLを検討する

投稿記事(wp_XXXXposts)とカスタムタクソノミーのSLUG(wp_XXXXterms)を結び付けたい、 結びつけるために以下のテーブルを使用する。意外と関連付けるのにたくさんテーブルがある
XXXXはWordpressをインストールした時の接頭語

参考は データベース構造 – WordPress Codex 日本語版

テーブル名説明
wp_XXXXpostsWordPress データの核である投稿記事のほか、ページ、ナビゲーションメニューのデータを格納
wp_XXXXterm_relationshipsオブジェクト(wp_posts テーブルの各投稿記事、wp_links テーブル内の各リンク)と wp_term_taxonomy のカテゴリ・タグとの関連付け情報を格納
wp_XXXXterm_taxonomy投稿およびリンクの分類上の語句(カテゴリ・タグ)データを格納
wp_XXXXterms投稿およびリンクの分類(カテゴリ・タグ)に使われる語句の基本情報を格納

結論からいうとこうなった。カスタムタクソノミーのslugで記事をわける。
例ではslugがenだったら英語記事、slugにenがついていなかったら日本語記事を想定している。

英語記事と日本語記事で異なる点はhaving句の箇所のみ


//英語記事の場合
select p.ID, p.post_type,p.post_status,p.post_title,tte.slug from wp_XXXXposts as p 
LEFT OUTER JOIN  wp_XXXXterm_relationships AS tr ON tr.object_id = p.ID
LEFT OUTER JOIN  wp_XXXXterm_taxonomy AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
LEFT OUTER JOIN  wp_XXXXterms AS tte ON tte.term_id = tt.term_id
group by p.ID 
having p.post_type = 'articles' AND p.post_status = 'publish' AND tte.slug = 'en';

//日本語記事の場合
select p.ID, p.post_type,p.post_status,p.post_title,tte.slug from wp_XXXXposts as p 
LEFT OUTER JOIN  wp_XXXXterm_relationships AS tr ON tr.object_id = p.ID
LEFT OUTER JOIN  wp_XXXXterm_taxonomy AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
LEFT OUTER JOIN  wp_XXXXterms AS tte ON tte.term_id = tt.term_id
group by p.ID 
having p.post_type = 'articles' AND p.post_status = 'publish' AND (tte.slug != 'en' OR tte.slug IS NULL);

テーブル結合箇所、値が入っていないことで結果の行が落ちるのを防ぐためにOUTER JOINで結合する。
結合するキーはそれぞれ下の感じ。


LEFT OUTER JOIN  wp_XXXXterm_relationships AS tr ON tr.object_id = p.ID
LEFT OUTER JOIN  wp_XXXXterm_taxonomy AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
LEFT OUTER JOIN  wp_XXXXterms AS tte ON tte.term_id = tt.term_id

このままだとen以外の場合に他のタームが入ってきた場合に、同じ投稿IDが複数個出力されてしまう。 そのためにwp_XXXXpostsテーブルのIDでグループ化する。


group by p.ID 

enがカスタムタクソノミーに入っている場合は、enを指定するだけだが、enがはいっていない場合、 値がNULLだと取得できない、対策としてIS NULLを指定する。 また、post_statusがpublishのものをとらないと、ごみ箱などの値を拾ってきてしまう。


having p.post_type = 'articles' AND p.post_status = 'publish' AND (tte.slug != 'en' OR tte.slug IS NULL);

$wpdbに置き換える

作成したクエリをWordpress用に置き換える、$wpdbだと各テーブルが以下のように取得することがきる。

オブジェクトの値対応するテーブル
$wpdb->postswp_XXXXposts
$wpdb->term_relationshipwp_XXXXterm_relationships
$wpdb->term_taxonomywp_XXXXterm_taxonomy
$wpdb->termswp_XXXXterms

置き換えるとこのような感じ、テーブル名がオブジェクト内の値に置き換わっているだけ


$sql = "SELECT
    p.ID,p.post_title,p.post_content,p.post_type,p.post_status,tte.slug
  FROM
    $wpdb->posts AS p
    LEFT OUTER JOIN $wpdb->term_relationships AS tr ON tr.object_id = p.ID
    LEFT OUTER JOIN $wpdb->term_taxonomy AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
    LEFT OUTER JOIN $wpdb->terms AS tte ON tte.term_id = tt.term_id
  GROUP BY
    p.ID
  HAVING
    p.post_type = 'articles' AND p.post_status = 'publish' AND tte.slug = 'en'
  ORDER BY
    p.post_date DESC
  LIMIT 
    $now_post_num, $get_post_num;";

$pre = $wpdb->prepare($sql,$now_post_num,$get_post_num);
$results = $wpdb->get_results($pre);

prepareはSQLエスケープをする目的で使用される。


$pre = $wpdb->prepare($sql,$now_post_num,$get_post_num);
引数説明
query(文字列) 実行したい SQL クエリ。%s および %d がプレースホルダーになる。
value_parameter (整数|文字列|配列) プレースホルダーへ代入する値。sprintf() 風の書き方でたくさんの値を渡すことができます。別の方法として、PHP の関数 vsprintf() 風に値を並べた配列を第2引数にすることもできます。

get_resultsは結果取得部分


$results = $wpdb->get_results($pre);
引数説明
query(文字列) 実行したい SQL クエリ。
output_type(定数)4つの定数のいずれか。初期値は OBJECT。詳細は省略。

参考

その他詳細

wp_XXXXpostsの中身

WordPressのPostsテーブル
表示する記事の本体


mysql> DESCRIBE wp_XXXXposts;
+-----------------------+---------------------+------+-----+---------------------+----------------+
| Field                 | Type                | Null | Key | Default             | Extra          |
+-----------------------+---------------------+------+-----+---------------------+----------------+
| ID                    | bigint(20) unsigned | NO   | PRI | NULL                | auto_increment |
| post_author           | bigint(20) unsigned | NO   | MUL | 0                   |                |
| post_date             | datetime            | NO   |     | 0000-00-00 00:00:00 |                |
| post_date_gmt         | datetime            | NO   |     | 0000-00-00 00:00:00 |                |
| post_content          | longtext            | NO   |     | NULL                |                |
| post_title            | text                | NO   |     | NULL                |                |
| post_excerpt          | text                | NO   |     | NULL                |                |
| post_status           | varchar(20)         | NO   |     | publish             |                |
| comment_status        | varchar(20)         | NO   |     | open                |                |
| ping_status           | varchar(20)         | NO   |     | open                |                |
| post_password         | varchar(255)        | NO   |     |                     |                |
| post_name             | varchar(200)        | NO   | MUL |                     |                |
| to_ping               | text                | NO   |     | NULL                |                |
| pinged                | text                | NO   |     | NULL                |                |
| post_modified         | datetime            | NO   |     | 0000-00-00 00:00:00 |                |
| post_modified_gmt     | datetime            | NO   |     | 0000-00-00 00:00:00 |                |
| post_content_filtered | longtext            | NO   |     | NULL                |                |
| post_parent           | bigint(20) unsigned | NO   | MUL | 0                   |                |
| guid                  | varchar(255)        | NO   |     |                     |                |
| menu_order            | int(11)             | NO   |     | 0                   |                |
| post_type             | varchar(20)         | NO   | MUL | post                |                |
| post_mime_type        | varchar(100)        | NO   |     |                     |                |
| comment_count         | bigint(20)          | NO   |     | 0                   |                |
+-----------------------+---------------------+------+-----+---------------------+----------------+
23 rows in set (0.00 sec)

wp_XXXXterm_relationshipsの中身

WordPressのterm_relationshipsテーブル
ここのobject_idとPostidが紐づく


mysql> DESCRIBE wp_XXXXterm_relationships;
+------------------+---------------------+------+-----+---------+-------+
| Field            | Type                | Null | Key | Default | Extra |
+------------------+---------------------+------+-----+---------+-------+
| object_id        | bigint(20) unsigned | NO   | PRI | 0       |       |
| term_taxonomy_id | bigint(20) unsigned | NO   | PRI | 0       |       |
| term_order       | int(11)             | NO   |     | 0       |       |
+------------------+---------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

wp_XXXXterm_taxonomyの中身

WordPressのterm_taxonomyテーブル
term_taxonomy_idでwp_XXXXterm_relationshipsテーブルとつなぎ、term_idでwp_XXXXtermsとつなげる


mysql> DESCRIBE wp_XXXXterm_taxonomy;
+------------------+---------------------+------+-----+---------+----------------+
| Field            | Type                | Null | Key | Default | Extra          |
+------------------+---------------------+------+-----+---------+----------------+
| term_taxonomy_id | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| term_id          | bigint(20) unsigned | NO   | MUL | 0       |                |
| taxonomy         | varchar(32)         | NO   | MUL |         |                |
| description      | longtext            | NO   |     | NULL    |                |
| parent           | bigint(20) unsigned | NO   |     | 0       |                |
| count            | bigint(20)          | NO   |     | 0       |                |
+------------------+---------------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)

wp_XXXXtermsの中身

WordPressのtermsテーブル
ここのslugの値をキーにして判断する


mysql> DESCRIBE wp_XXXXterms;
+------------+---------------------+------+-----+---------+----------------+
| Field      | Type                | Null | Key | Default | Extra          |
+------------+---------------------+------+-----+---------+----------------+
| term_id    | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| name       | varchar(200)        | NO   | MUL |         |                |
| slug       | varchar(200)        | NO   | MUL |         |                |
| term_group | bigint(10)          | NO   |     | 0       |                |
+------------+---------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

get_postsで取得する場合

get_postsで取得する場合は以下のような形になる。なぜ$wpdbを使わなければならなかったのか、 今となっては忘れてしまう。ちゃんと設計時の思想を残すべきだなと。


  $lang = getLang();
  if ( $lang == "jp" ) {
    $args = array(
      'post_type'   =>  'articles',
      'tax_query' => array (
        array(
          'taxonomy' => 'articles-category',
          'field' => 'slug',
          'terms'=> 'en',
          'operator' => 'NOT IN',
        ),
      ),
      'posts_per_page'   =>  3,
      'paged' => $paged ,
    );
  } else if ( $lang == "en" ) {
    $args = array(
      'post_type'   =>  'articles',
      'tax_query' => array (
        array(
          'taxonomy' => 'articles-category',
          'field' => 'slug',
          'terms'=> 'en',
        ),
      ),
      'posts_per_page'   =>  3,
      'paged' => $paged ,
    );
  }