【Shopify】メタフィールドにブログ記事のタイトルを入力し,商品ページにブログ記事を表示する

# 1．はじめに
大学生フリーランスの [むささび](https://twitter.com/musasabi_byun) です．
主にWeb制作活動を行っています．

先日，なかなか面白い機能をShopifyストアで実装させていただきました．
私自身のアウトプットと，どなたかの参考になるのではと思い，本記事でまとめさせていただきます．


# 2．実装する機能
今回実装していく機能を紹介します．
ざっくり説明すると，ポイントは以下の2つです．

:::note
1．商品ページに，その商品に関連するブログ記事を表示したい
2．商品管理ページのメタフィールド欄に，ブログ記事のタイトルを入力する
:::


この機能の目的が `商品ページにブログ記事を表示する` ことになります．

では，関連記事をどのように指定するかという点ですが，`商品管理ページのメタフィールド欄に，ブログ記事のタイトルを入力する` という方法を取ります (先方からの要望...)

この2つ目の仕様が実装を少々厄介にしております．
Shopifyでは通常，[記事のハンドル](https://shopify.dev/api/liquid/basics/handle "Object handles") から記事情報を取得します．

![ブログ記事のハンドル](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603971/0a3829b9-c0a2-7e69-1390-197823c05ac3.jpeg)

これはブログ記事に限った話ではありませんが，商品や記事を扱う際はどうにかしてハンドルを入手する必要がありますね．

しかし，今から実装する機能において，スタート地点で持っている情報は `記事タイトル` のみです．
つまり `記事タイトル` から ブログ記事そのものを入手する必要があります．

以降で具体的な実装方法について説明いたします．


# 3．実装方法
それでは実装方法の説明です．
老婆心ながら今回実装する機能をもう一度掲載しておきます．

:::note
1．商品ページに，その商品に関連するブログ記事を表示したい
2．商品管理ページのメタフィールド欄に，ブログ記事のタイトルを入力する
:::

## 実装環境
今回の実装は，Shopify partnersの [開発ストア](https://help.shopify.com/ja/partners/dashboard/managing-stores/development-stores "開発ストアを構築する") で行いました．

使用したテーマは [Online Store 2.0](https://www.shopify.jp/blog/partner-shopify-online-store "Shopify Online Store 2.0") 対応の公式無料テーマ「[Dawn](https://themes.shopify.com/themes/dawn/styles/default?locale=ja)」です．
因みに実装時のテーマバージョンは 4.0.0

実装する機能は Online Store 2.0 に対応していないテーマでも可能です．実際の案件では，Online Store 2.0 未対応のテーマで行いました．


## 実装概要
さて，実際にコーディングを行う前に，実装の手順について考えます．

まず上記で既に説明したように，`記事タイトル` から記事情報を入手する必要があります．
そこで以下のような流れでブログ記事を取得してみます．

```:プログラムの流れ
1．商品メタフィールドに入力されたタイトルから，'ブログ記事タイトルの配列' を取得する
↓
2．すべての 'ブログカテゴリーハンドルの配列' を取得する
↓
3．'ブログカテゴリーハンドルの配列' からすべてのブログ記事のタイトルを取得する
↓
4．'ブログ記事タイトルの配列' の要素を参照し，タイトルが存在すれば記事を表示する
```

上記の流れを順を追って説明します．



```
1．商品メタフィールドに入力されたタイトルから，'ブログ記事タイトルの配列' を取得する
```
まずは商品管理ページのメタフィールドに入力された記事タイトルを配列化します．


```
2．すべての 'ブログカテゴリーハンドルの配列' を取得する
```
次にすべてのブログ記事を取得することを考えます．
しかし，Liquidにはすべてのブログ記事を取得するタグは存在しません．

[blogs](https://shopify.dev/api/liquid/objects?shpxid=8bd5580d-90BE-4EED-8850-5E67616925C7#blogs "Liquid objects") という，一見すべてのブログ記事を取ってこれそうなグローバルオブジェクトが存在しますが，これはブログ記事のカテゴリーをクッションとして挟まなければ機能しません．

ということで，少し遠回りをして，まずは `ブログカテゴリーハンドルの配列` を取得します．

ですがここでも問題があり，Liquidではすべてのブログカテゴリーを取得できるタグもありません．

そこでちょっとひと工夫です．
`ストア管理画面 -> オンラインストア -> メニュー` より，すべてのブログカテゴリーを含むメニューを作成します．
ここでは以下の3つのブログカテゴリーを作成し，そのカテゴリーをすべて含むメニューを作成しました．

![ブログカテゴリーのメニューを作成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603971/815f3a2d-9a91-ef51-c41e-f6c20a8d3d4c.jpeg)

このメニューと [linklist オブジェクト](https://shopify.dev/api/liquid/objects/linklist "The linklist Object") を用いれば，すべてのブログカテゴリーのハンドルを取得できそうです．

```
3．'ブログカテゴリーハンドルの配列' からすべてのブログ記事のタイトルを取得する
4．'ブログ記事タイトルの配列' の要素を参照し，タイトルが存在すれば記事を表示する
```
上記で取得した `ブログカテゴリーのハンドル配列` を用いて，ストアに存在するブログ記事のタイトルにアクセスします．
ストアに存在するブログ記事のタイトルの中に，手順1で用意した `ブログ記事タイトルの配列`  の要素が存在すれば，その記事を表示します．

以上がプログラムの流れです．
それではコーディングに移ります．


## 実装してみる
上記で説明した流れに従い，実際にコーディングを行います．

```:プログラムの流れ
1．商品メタフィールドに入力されたタイトルから，'ブログ記事タイトルの配列' を取得する
↓
2．すべての 'ブログカテゴリーハンドルの配列' を取得する
↓
3．'ブログカテゴリーハンドルの配列' からすべてのブログ記事のタイトルを取得する
↓
4．'ブログ記事タイトルの配列' の要素を参照し，タイトルが存在すれば記事を表示する
```

今回は「Dawn」テーマの `main-product.liquid` にコード追加する形でコーディングを行います．

Dawn は Online Store 2.0 対応のテーマなので，セクション階層にファイルを分離して保存することで
トップページだけでなく，商品ページ内でもそのセクションが使用可能です．

https://www.shopify.jp/blog/partner-shopify-online-store

ですが Online Store 2.0 未対応テーマでも取り込みやすいように，ここでは `main-product.liquid` を修正することにしてみます．

以下が今回実装する部分のコードです．

`main-product.liquid` と CSSファイルは [GitHub](https://github.com/musasabibyun/related-articles-shopify-product-page) に置いておきます．
ファイルの全体や schema 設定，CSS を確認するされる際にご参考いただければと思います．

```liquid:main-product.liquid
...(省略)

<section class="page-width">
  <!-- CSS の読み込み -->
  {{ 'related-articles.css' | asset_url | stylesheet_tag }}

  <!-- 商品メタフィールドからブログ記事タイトルの配列を取得する -->
  {% assign titles =  product.metafields.my_fields.articletitle | split: ',' %}

  <!-- メニューからすべての ブログカテゴリーハンドルの配列を取得する  -->
  {% for link in section.settings.link_list.links %}
    {% assign blog_handles = blog_handles | append: link.object.handle | append: ',' %}
  {% endfor %}
  {% assign blog_handles = blog_handles | split: ',' %}

  <!-- ブログ記事タイトルの配列の大きさが 1 以上であれば表示 -->
  {% if titles.size > 1 %}
    <div class="related-articles" data-template-name="{{ template.name }}">
      <div class="title">{{ section.settings.articles_head }}</div>
      <div class="wrap">```

      {% assign count = 0 %}  <!-- 表示する記事数を制限する変数 -->

      {% for title in titles %}
        {% for blog_handle in blog_handles %}
          {% for article in blogs[blog_handle].articles %}

            <!-- メタフィールドに入力されたブログ記事が存在すれば，記事を表示する -->
            {% if title == article.title %}
              <article class="article_item item">
                <a href="{{ article.url }}" class="flex">
                  <div class="eyecatch">
                    <div style="background-image: url('{{ article.image.src | img_url:'500x' }}');"></div>
                  </div>
                  <div class="info">
                    <div class="title">{{ article.title }}</div>
                    <div class="desc">
                      {{ article.excerpt }}
                    </div>
                  </div>
                </a>
              </article>
              {% assign count = count | plus: 1 %}
              {% break %}
            {% endif %}

          {% endfor %}
        {% endfor %}

        <!-- 出力記事数が一定数を越えたら終了 -->
        {% if count >= section.settings.articles_size %}
          {% break %}
        {% endif %}
      {% endfor %}

      </div>
    </div>
  {% endif %}
</section>

...
```

上記のコードについて補足で説明を入れます．

まず以下の部分で `ブログ記事タイトルの配列` と `ブログカテゴリーハンドルの配列` を用意しています．

```liquid:main-product.liquid
  <!-- 商品メタフィールドからブログ記事タイトルの配列を取得する -->
  {% assign titles =  product.metafields.my_fields.articletitle | split: ',' %}

  <!-- メニューからすべての ブログカテゴリーハンドルの配列を取得する  -->
  {% for link in section.settings.link_list.links %}
    {% assign blog_handles = blog_handles | append: link.object.handle | append: ',' %}
  {% endfor %}
  {% assign blog_handles = blog_handles | split: ',' %}
```

[split](https://shopify.github.io/liquid/filters/split/ "split") は文字列を特定の文字で区切り配列化するLiquidのフィルターです．
また，[append](https://shopify.github.io/liquid/filters/append/ "append") は文字列の終わりに文字列を追加するフィルターになります．

`ブログカテゴリーハンドルの配列` を取得する際に `section.settings.link_list` を参照していますが，これはテーマカスタマイズ画面で設定されたメニューを見ています．

![ブログカテゴリー(メニュー)の設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603971/35d0f0a9-dfc2-8d42-751c-6939493ce61b.jpeg)

テーマカスタマイズ画面の商品ページで，[実装概要](#実装概要) で作成した `すべてのブログカテゴリーを含むメニュー` を指定することで正常に機能します．

次に取得した `ブログカテゴリーハンドルの配列` を用いて，すべてのブログ記事のタイトルを取得します．
これは `for` ループを3回 (も) まわすことで実現できます．

ブログカテゴリーのハンドルを入手したことで，上記で紹介した [blogs](https://shopify.dev/api/liquid/objects?shpxid=8bd5580d-90BE-4EED-8850-5E67616925C7#blogs "Liquid objects") オブジェクトが使用できます．

`記事タイトルの配列` に対して `for` ループを回し，
さらに `ブログカテゴリーハンドルの配列` に対して `for` ループを回し，
`あるブログカテゴリーが保持しているブログ記事` に対して `for` ループを回します．

1つ目のループで注目している`記事タイトルの配列` の要素が
2つ目のループで注目しているブログカテゴリー内に存在する
3つ目のループで注目している記事のタイトルと一致すれば，その記事を表示させる仕組みです．

```liquid:main-product.liquid
      {% assign count = 0 %}  <!-- 表示する記事数を制限する変数 -->

      {% for title in titles %}
        {% for blog_handle in blog_handles %}
          {% for article in blogs[blog_handle].articles %}

            <!-- メタフィールドに入力されたブログ記事が存在すれば，記事を表示する -->
            {% if title == article.title %}
              <article class="article_item item">
                ... (ブログ記事の表示)
              </article>
              {% assign count = count | plus: 1 %}
              {% break %}
            {% endif %}

          {% endfor %}
        {% endfor %}

        <!-- 出力記事数が一定数を越えたら終了 -->
        {% if count >= section.settings.articles_size %}
          {% break %}
        {% endif %}
      {% endfor %}
```

また，表示数を動的に制限するために，`count` 変数と，schema で記事の最大数 `section.settings.articles_size` を定義しています．

## 表示確認
それでは実装した機能が正常に動いているか確認します．
まず既に紹介していますが，ブログカテゴリーとして `Events`, `News`, `Tips` の3つを用意しています．

そして `テスト1` ~ `テスト4` という名前のブログ記事を合計4つ作成し，それぞれのカテゴリーを分けました．ブログ記事の階層構造は以下のようになっています．

```:ブログ記事の階層構造
ブログカテゴリー ─ News   ─ テスト1
              │        └ テスト2
              │
              ├ Tips   ─ テスト3
              └ Events ─ テスト4
```

今更ですが，ブログ記事タイトルを入力する商品メタフィールドも定義しなければいけません．

既にコーディング時にメタフィールドを参照していたので順番が前後してしまいましたが，`articletitle` という商品メタフィールドを定義します．

メタフィールドは複数行のテキストタイプを選択します．

![商品メタフィールドの定義](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603971/540d4030-a9f8-6c09-3d0f-3eb53ddcc6f5.jpeg)

さらに商品として `テスト商品` を用意し，今作成した articletitle メタフィールド欄に `テスト1,テスト2,テスト3,テスト4` と入力します．

![メタフィールドの入力](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603971/3baa1117-d8cd-c9d1-f510-37ed3f528573.jpeg)

これで準備完了です．実際に商品ページを見てみます．
下記の画像内の赤枠で囲った部分が，今回作成した機能にあたります．

![FireShot_Capture_116_-_テスト商品_–_Dev_-_example-shuntaro_myshopify_com.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603971/ed029db5-af3d-8fc9-c7d0-2886fd3d6a9f.jpeg)

うまく表示できているようですね．

`テスト商品` のメタフィールドには `テスト1` ~ `テスト4` の4つのブログ記事を入力していますが，
テーマカスタマイズ画面で最大記事数を3に設定しているので，メタフィールドに入力された順に3つのブログ記事が表示されます．

因みにスマートフォン表示にも対応させていますので，必要であれば [GitHub](https://github.com/musasabibyun/related-articles-shopify-product-page) の CSS ファイルを参考にしてください．

# 4．まとめ
今回は Shopify で以下の機能を実装しました．

:::note
1．商品ページに，その商品に関連するブログ記事を表示したい
2．商品管理ページのメタフィールド欄に，ブログ記事のタイトルを入力する
:::

Shopify は EC サイト向けのプラットフォームであるということもあり，ブログ記事の扱いに関しては少々複雑になってしまう場合もあるようですね．

どなたかの参考になれば幸いです．

むささび

# 参考
▼ 今回実装した機能のソースコード
https://github.com/musasabibyun/related-articles-shopify-product-page

▼ Shopify Online Store 2.0：開発者にとっての意義
https://www.shopify.jp/blog/partner-shopify-online-store

▼ Object handles
https://shopify.dev/api/liquid/basics/handle

▼ Liquid Objects -blogs
https://shopify.dev/api/liquid/objects?shpxid=8bd5580d-90BE-4EED-8850-5E67616925C7#blogs

▼ The article object
https://shopify.dev/api/liquid/objects/article

▼ The blog object
https://shopify.dev/api/liquid/objects/blog

▼ The linklist object
https://shopify.dev/api/liquid/objects/linklist

▼ split filter
https://shopify.github.io/liquid/filters/split/

▼ append filter
https://shopify.github.io/liquid/filters/append/