[WordPress] GraphQLフィールドの追加とGatsbyでの利用

最近Wordpress+Gatsbyでサイト作るのが多い。
WordPressではないHeadressCMSを使うか、WordpressをHeadressCMS代わりにするかという二択になったとき、コストの低い方や運用の手間の少ない方が当然選ばれやすいのだが、「編集者が使いやすいもの」という条件を増やすと後者になりやすいし、長年運用されてたWordpressのリニューアルだと継続してWorpdressになりがちである。

投稿された西暦一覧と西暦ごとの投稿数を返したい

そんなWordpress+Gatsbyサイトを作ってる時に西暦だけのナビゲーションが必要になった。

post_type=postにおける年月日ごとのアーカイブページとそのナビゲーションはwp_get_archives関数1つで簡単に作れるんだけど、これをGraphQLでやろうとすると…???🤔
カテゴリーやタグは簡単に取れるんだけど、年月日的なのは存在していないようす。

テンプレートタグが返すのはHTMLテキストなのでそのままではparseして出力するしかない。それはそれで手っ取り早いのだが、wp_get_archives 関数の中身だけピンポイントで返すフィールドなんてのは存在してないし、JSXでごにょごにょできるように配列で西暦とカウント数だけ欲しいのだ。

投稿全件取得してgatsby-nodeでページ生成してるから、そのついでに西暦ナビゲーションも作ればいいのでは、と思ったんだけども、ナビゲーションコンポーネントの中でStaticQuery叩いて生成したいなぁという気持ちになったのでそのように作ってみることにした。

西暦一覧と西暦ごとの投稿数を取得

これはテンプレートタグである wp_get_archives の中身を拝借して作った。
文字列ではなくて配列を返すようにしておく。

// functions.php
function get_year_archives() {
  global $wpdb, $wp_locale;
  $defaults = array(
    'type'            => 'yearly',
    'limit'           => '',
    'order'           => 'DESC',
    'post_type'       => 'post',
    'year'            => get_query_var( 'year' ),
    'monthnum'        => get_query_var( 'monthnum' ),
    'day'             => get_query_var( 'day' ),
    'w'               => get_query_var( 'w' ),
  );

  $parsed_args = wp_parse_args( $defaults );

  $post_type_object = get_post_type_object( $parsed_args['post_type'] );

  if ( ! is_post_type_viewable( $post_type_object ) ) {
    return;
  }

  $parsed_args['post_type'] = $post_type_object->name;

  $join = apply_filters( 'getarchives_join', '', $parsed_args );
  $sql_where = $wpdb->prepare( "WHERE post_type = %s AND post_status = 'publish'", $parsed_args['post_type'] );
  $where = apply_filters( 'getarchives_where', $sql_where, $parsed_args );
  $order = strtoupper( $parsed_args['order'] );
  $limit = $parsed_args['limit'];

  $query   = "SELECT YEAR(post_date) AS `year`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date) ORDER BY post_date $order $limit";

  $last_changed = wp_cache_get_last_changed( 'posts' );
  $key     = md5( $query );
  $key     = "get_year_archives:$key:$last_changed";

  $results = wp_cache_get( $key, 'posts' );

  $output = array();

  if ( ! $results ) {
    $results = $wpdb->get_results( $query );
    wp_cache_set( $key, $results, 'posts' );
  }

  if ( $results ) {
    foreach ( (array) $results as $result ) {
      array_push($output, array(
        'year' => (int) $result->year,
        'count' => (int) $result->posts
      ));
    }
  }

  return $output;
}

WpGraphQLのフィールド追加

WordPressでGraphQLを使えるようにするプラグインであるWpGraphQLにはフィールドを追加する関数が備わっているので、それを利用する。

オブジェクトのキーと値の型定義(①、②)と、フィールドの設定(③)が必要。

add_action('graphql_register_types', function ()  {
  $itemName = 'postYearCount';
  $responseName = 'postYearCounts';

  $itemProps = [
    'year' => [ 'type' => 'Integer' ],
    'count' => [ 'type' => 'Integer' ]
  ];

  // ①オブジェクト単体のキーと値の型
  register_graphql_object_type($itemName, [
    'fields' => [
    'year' => [ 'type' => 'Integer' ],
    'count' => [ 'type' => 'Integer' ]
    ]
  ]);

  // ②フィールドが返すキーと値の型
  register_graphql_object_type($responseName, [
    'fields' => [
      'count' => [ 'type' => 'Integer' ],
      'items' => [ 'type' => [ 'list_of' => $itemName ]],
      'errors' => [ 'type' => [ 'list_of' => 'String' ]]
    ]
  ]);

  // ③新しいフィールドの設定
  register_graphql_field('RootQuery', $responseName, [
    'type' => $responseName,
    'args' => null,
    'description' => __( '投稿のナビゲーション用', 'sample' ),

    'resolve' => function($root, $args, $context, $info) {
      $archives = get_year_archives();

      return [                                
        'count' => count($archives),
        'items' => $archives
      ];
    }
  ]);
});

GraphiQL IDEで表示できたらおk。

Gatsbyでの利用

register_graphql_field によって追加したフィールドは、RootQueryを指定しているのでGraphiQL IDEではルートに並んでいたが、Gatsby側ではwp内に含まれる。

この場所の違い知ってないと、散々探した挙句追加されてないと勘違いしかねない…😂

あとはuseStaticQueryなどでよしなに使える。

const { wp: { newsYearCounts }, } = useStaticQuery(query)

const query = graphql`
  query PostYearCounts {
    wp {
      postYearCounts {
        items {
          count
          year
        }
      }
    }
  }
`

gatsby-source-wordpress 側にある説明では、以下のPostフィールドに追加する例がある。

add_action('graphql_register_types', function() {
  register_graphql_field( 'Post', 'testGatsbyField', [
    'type' => 'String',
    'description' => __( 'Test field for demonstration', 'your-textdomain' ),
    'resolve' => function() {
      return 'gatsby test!';
    }
  ]);
});

第一引数であるObject Typesの指定を変えれば、任意のフィールドに思うまま追加し放題なのだ。

Nuxtを3年使ってたから同じものを作るならNuxtの方が早いんだけど、Vue界隈は2と3が入り乱れてる微妙な時期なので、Vue3が浸透するまではTypeScript復習がてらReact触っとこうかなという気持ちでいる。

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください