Shopifyで特定のタグに属したブログ記事の一覧を表示する

フォーラムで見かけて思ったよりも回答が大変だったので記事にまとめておきたいと思います。

[追記:2022.2.14]
ハンドルではなくブログ記事のオブジェクトをループで用いるサンプルコード2を追加。

実現したいこと

  • Shopifyで特定のタグに属したブログの記事を5つ表示する
  • 対象となるタグは2つ以上(例:タグAとタグBそれぞれ5記事表示する)

タグは複数存在できるためか、以下のwhereフィルターは使えませんでした。一応プロパティにtagsを記載し値を入力しない形にすると取得はできたので、tagsというプロパティはwhereの対象であると思ったのですが。

サンプルコード1


{% comment %} 初期設定 {% endcomment %}
{% assign _articles = blogs['news'].articles %}{% comment %} ブログ指定用(newsを目的のブログのhandleで記述する) {% endcomment %}
{% assign _tag1 = 'red' %}{% comment %} タグ指定用 {% endcomment %}
{% assign _tag2 = 'blue' %}{% comment %} タグ指定用 {% endcomment %}
{% assign _limit = 2 %}{% comment %} 記事表示数 必ず半角数字で入力 {% endcomment %}
{% comment %} 初期設定ここまで {% endcomment %}

{% comment %} 指定タグの一覧 取得用 {% endcomment %}
{% assign loopnum1 = 0 %}
{% assign loopnum2 = 0 %}
{% assign article_handles1 = '' %}
{% assign article_handles2 = '' %}
{% for article in _articles %}
  {% comment %} 指定タグを持ったブログのhandleをカンマ区切りで変数に追加 {% endcomment %}
  {% for tag in article.tags %}
    {% if tag == _tag1 %}
      {% assign loopnum1 = loopnum1 | plus: 1 %}
      {% assign article_handles1 = article_handles1 | append: ',' | append: article.handle  %}
    {% elsif tag == _tag2 %}
      {% assign loopnum2 = loopnum2 | plus: 1 %}
      {% assign article_handles2 = article_handles2 | append: ',' | append: article.handle  %}
    {% endif %}
  {% endfor %}
  {% comment %} 指定タグのループ回数が制限以上になったらループを停止 {% endcomment %}
  {% if loopnum1 >= _limit and loopnum2 >= _limit%}
    {% break %}
  {% endif %}
{% endfor %}

{% comment %} 指定タグ1 表示用 {% endcomment %}
{% if article_handles1 != '' %}
{% comment %} カンマ区切りの変数を配列に変換 {% endcomment %}
  {% assign article_handles1_array =  article_handles1 | remove_first: "," | split: ',' %}
  <ul>
  {% comment %} handleの配列をforでひとつづつ取り出して実行 {% endcomment %}
  {% for article_handle in article_handles1_array limit: _limit %}
    <li>{{ articles[article_handle].title }}<br>{{ articles[article_handle].created_at | date: '%Y年 %-m月 %d日' }}</li>
  {% endfor %}
  </ul>
{% endif %}

{% comment %} 指定タグ2 表示用 {% endcomment %}
{% if article_handles2 != '' %}
  {% assign article_handles2_array =  article_handles2 | remove_first: "," | split: ',' %}
  <ul>
  {% for article_handle in article_handles2_array limit: _limit  %}
    <li>{{ articles[article_handle].title }}<br>{{ articles[article_handle].created_at | date: '%Y年 %-m月 %d日' }}</li>
  {% endfor %}
  </ul>
{% endif %}

上記は対象とするタグが2つの想定ですが、もちろん増やすことも減らすことも可能です。

不要なループの抑制

最初のタグ一覧を取得するところで際限なくループを回してしまう点は、今まで同種のコードを作っていた際の懸念点でした。

今回ようやく、単にループ回数を計測して外側のforbreakを実行し、表示部分でさらにlimitによる制限をかける形で実現できました。

不要なループの抑制の概要

ループ内で特定タグを持つ投稿だけを取得する場合、loopindexは使えず、配列に変換前ではsizeも使えない、という状況になります。

該当する投稿取得するループのループ回数を計測し、その回数を最も外側のループ内で判定すれば抑制できます。

しかし今回の様に2つ以上のタグが対象の場合は、以下のようにハンドル一覧取得部分においては片方が必要な数を取得できてもループが続いてしまいます。

  • タグ1が付与された記事のハンドルが5つ集まった時点で、タグ2の方が0の場合、タグ2が5つ揃うまでタグ1の方も引き続き一覧に追加され続ける

この問題を解決するために、表示用コードの時点で改めてlimitをつけて必要な数だけ表示する形にしました。多少の無駄がでるものの、その無駄もかなり減らせたように思います。

なお別案として、ループをタグごとに分ける形を考えましたが、この場合タグごとに0からループをやり直すため無駄が多いと判断しました。
appendで追加する部分にも表示数で分岐を作ることもできますが、この処理とlimitのどちらが軽いかが判断できず、見た目にわかりやすそうなlimitを選びました。

セクションの検討

こういったコードの場合、セクションで作成してテーマのカスタマイズから変更できる様に作る方がよいのかもしれません。
タグの指定をカンマ区切りの配列で記載するか、個別に記載して配列にするか、配列にせず個別に実行してしまうか。いずれにしても検討の価値はありそうです。

サンプルコード2

派生と言いますか開発順序的には発展版として、ブログのオブジェクトをconcatで結合して用いる方法を試作したコードです。

concatでオブジェクト(というか配列?)をうまく結合できるとは思わなかったのですが、そのおかげでサンプルコード1よりも記述はシンプルになったと思います。

タグ指定は1つのみ対応なので多少趣旨から外れますが、サンプルコード1を元に書き加えれば動作するはずです。


{%- liquid
  assign _articles = blogs['news'].articles
  assign _tag1 = 'red'
  assign _limit = 1
  assign article_objects1 = ''
  for article in _articles
    for tag in article.tags
      if tag == _tag1
        assign article_object = _articles | where: 'handle', article.handle
        assign article_objects1 = article_objects1 | concat: article_object
      endif
    endfor
  endfor
%}
{% assign article_objects1 = article_objects1 | sort: 'published_at' %}
{% for article_object in article_objects1 limit: _limit %}
 {{article_object.title}}
{% endfor %}

liquidで多少簡略化した記述を意図しています。

次項で詳細を記載しますが、このサンプルコード2でもpaginateタグは動作しません。

liquidタグ

liquidタグに関しては上記ドキュメントが該当します。

しかし、Liquidという言語のドキュメントでliquidというタグを調べるのは本当に難しいです…。

paginateは動作しない

コメントで少しやりとりしていますが記事本文の方にも少し記載します。

上記1つ目で2つ目が参考情報として紹介されていますが、whereフィルターを使うとpaginateは動作せず以下のようにpaginateタグの対象ではないというエラーがでます。


Liquid error (hoge line 000): Array 'fuga' is not paginateable.

なお、assignで変数に入れてpaginateタグを使っただけでもダメでした。
フォーラムの回答にある通り、ページングが必要な場合はJSによる仕組み作りが必要になりそうです。

補足

上記フォーラムで回答しています。当記事のコードと全く同じではなく、改修前の原型のような状態です。

結び

値を取り出し配列を作成できれば大抵の事柄はどうにかできるような気はしますが、より良い方法が他にあるのではとも思います。

4人がこの記事を評価

役に立ったよという方は上の「記事を評価する」ボタンをクリックしてもらえると嬉しいです。

連投防止のためにCookie使用。SNSへの投稿など他サービスとの連動は一切ありません。

コメント:2件

  1. いつも参考とさせていただいております!
    こちらの記事を参考に、特定のブログ内のタグを有する記事の一覧ページを作っておりました。
    今回はまっているのが、、
    1.そもそもタグを指定しても表示されない現象が起こっている
    2.ページングを使用すると機能しない
    上記2点でかなり苦戦しております。。

    1については、余計な記述なく、上記のソースコードのまま、任意のハンドルとタグを指定しても表示されません。。他の出力方法は何か考えられますでしょうか?海外のサイトを探してみたのですが、見つからず。。
    2については最悪の場合はページングを使用しないでの露出方法などで対応しようとは思いますが、可能であれば任意のハンドルと特定のタグだけでもページングが機能する方法をご存知であればそちらも伺いたく。。

    ぜひともご教示のほどお願い致します。

    1. ご覧いただきありがとうございます。

      こちらで確認したところサンプルコードのままで動いたので原因はわかりかねます。
      取り合えず各部で値を出力し、値があるか、意図通りの内容かを確認されることをお勧めします。

      他の方法としては、複数のタグへの対応は難しいと思いますが、以下のようなforループ内で目的のタグの有無を判別し、出力すれば実現はできると思います。
      {% for article in blogs['news'].articles %}

      ページングに関しては、当記事のコードでは対応していないため実現は無理だと思います。

コメント欄