最近案件で割と大きなECサイトを構築しまして、その際にShopifyのテーマを0から作成しました。その中でLiquidというShopify独自のテンプレート用言語を多く扱いましたので、そのノウハウを少しずつ紹介していきたいと思います。
さて今回はShopifyの中でも地味に重要な存在である「ブログ」関連の処理についてまとめてみます。
Shopifyのブログ機能についておさらい
早速コーディング例をお見せしたいところですが、まず最初にShopifyのブログ機能についてしっかりとおさらいしたいと思います。
ブログとブログ記事
Shopifyのコンテンツには基本的に、ページ(固定ページ)とブログ(投稿)というものが存在します。そしてブログを語る上で外せないのが「ブログ」と「ブログ記事」という二つの言葉ですね。
ブログ記事というのはいわゆる一つ一つの投稿です。簡単に誰でも投稿することができます。タイトルや本文、そしてサムネイル等を設定することができます。例えば「ニュース」など、日々コンテンツが追加されていくようなものについてはこのブログ機能を使って運営することが多いです。
しかしニュース以外にもキャンペーン情報を発信するためのブログが欲しかったとすると、二種類のブログが必要になってきます。 そんな時、ブログの記事を分けるのが「ブログ」という存在です。ややこしいですね。
ブログというノートがあったとして、そのノートのページ一枚一枚がブログ記事です。そしてノート自体も増やすことができるといった感じです。
ブログ自体を分けなくてもブログにカテゴリを持たせればいいのではないかとも思いますが、残念ながらShopifyのブログ機能にはカテゴリというものはなく、あってもタグだけです。(タグをうまいこと使いこなせば別に問題ありません)
ブログのURL構造
ブログ関連のURL構造を知ることでさらにブログのイメージがつくかと思います。基本的にブログ記事を投稿するときに、そのブログ記事がどのブログに属しているのかを指定しなければなりません。
そしてURLの構造は以下のようになります。
/blogs/{ブログのスラッグ}/{記事のスラッグ}
ルートディレクトリに対してblogsというディレクトリがあり、その中にブログのディレクトリがあり、その中にそれぞれの記事が存在している、というような感じです。
また、ブログのスラッグ及び記事のスラッグは自由自在に変えることができます。
そして
/blogs/{ブログのスラッグ}
というURLにアクセスするとこのブログの記事一覧ページを閲覧することができます。この辺は非常に直感的でわかりやすいですね。
ブログのテンプレート
ShopifyではWordPressみたいにテンプレートを使うことができます。これが基本的にLiquid言語で書かれているものですね。
そしてブログ関連のテンプレートは二種類あり、「blog」と「article」です。blogというのはブログ、つまりブログ記事一覧ページ用のテンプレートであり、articleというのはブログ記事用のテンプレートです。
そして
blog.sample.liquid #「sample」という名前を持ったブログ用テンプレート
article.sample.liquid #「sample」という名前を持ったブログ記事用テンプレート
テンプレートファイルの命名規則としては上の通りです(blog.liquidやartciel.liquidとするとデフォルトのテンプレートとして設定できます)
ややこしいですが、「sample」というハンドルをもったブログがあるとして、そのブログに「sample」というブログ用テンプレートが結び付けられるわけではありません。ブログの種類とテンプレートは分離していて、例えば違うブログに対しても同じテンプレートを当てれますし、同じブログ内にある違う記事同士で違うブログ記事用のテンプレートを持たせるということもできます。
それぞれのブログ、それぞれのブログ記事各々に対して設定することができるということですね。
ややこしいですが、まぁカスタマイズ性の良さ故のややこしさみたいなところもありますね。
ブログ記事テンプレートの作り方
さてまず最初にブログ記事のテンプレートについてざっと理解しましょう。 ShopifyのLiquid言語は基本的にオブジェクト指向であり、ブログ記事やブログ、商品情報などをオブジェクトとして扱うことができます。
ブログ記事用のテンプレートを開発するにあたって必要なオブジェクトは「article」です。
https://shopify.dev/docs/api/liquid/objects/article
上の公式ページでめっちゃわかりやすくまとめられていますね。
{
"author": "作者",
"comment_post_url": "コメント送信用のURL",
"comments": [],
"comments_count": 1,
"comments_enabled?": true,
"content": "本文(HTML形式)",
"created_at": "2022-04-14 16:56:02 -0400",
"excerpt": "抜粋(HTML形式)",
"excerpt_or_content": "抜粋がある場合抜粋、なければ本文",
"handle": "ハンドル({ブログのスラッグ}/{記事のスラッグ})",
"id": 556510085185,
"image": {},
"metafields": {},
"moderated?": true,
"published_at": "2022-04-14 16:56:02 -0400",
"tags": [],
"template_suffix": "テンプレートの名前",
"title": "タイトル(文字列)",
"updated_at": "2022-06-04 19:27:33 -0400",
"url": {},
"user": {}
}
上のような構造を持つオブジェクトとなっています。
<article id="content">
<h1>{{ article.title }}</h1>
<date>
{{ article.published_at | date: '%Y.%m.%d' }}
</date>
<div class="thumbnail">
<img src="{{ article.image | img_url: '300x300' }}" alt="">
</div>
<div id="content">
{{ article.content }}
</div>
</article>
articleテンプレート上ではarticleオブジェクトが自動で与えられるので、上のようにarticleオブジェクトを特に定義したりしなくても使うことができます。
そしてブログ記事の情報を引き出すことができます。
ブログテンプレートの作り方
続いてはブログのテンプレート、つまりブログ記事一覧ページのテンプレートの作り方です。こちらについては「blog」というオブジェクトを使います。
{
"all_tags": [],
"articles": [],
"articles_count": 3,
"comments_enabled?": true,
"handle": "{ブログのスラッグ}",
"id": 78580613185,
"metafields": {},
"moderated?": true,
"next_article": {},
"previous_article": {},
"tags": [],
"template_suffix": "{テンプレート名}",
"title": "タイトル(文字列)",
"url": "/blogs/{ブログのスラッグ}"
}
上のようなプロパティがあります。そしてその中に「articles」という配列型の要素も格納されていて、articlesをforループで回すことでarticleオブジェクト、すなわちこのブログ内に格納されているブログ記事のオブジェクトを得ることができます。
<main id="post_list">
{% for article in blog.articles %}
<li>
<article>
<h2><a href="{{ article.url }}">{{ article.title }}</a></h2>
<p>{{ article.excerpt_or_content }}</p>
</article>
</li>
{% endfor %}
</main>
そしてブログのテンプレートですから、blog.~.liquidのようなファイルに記述していきます。ブログテンプレートに与えられるのはblogオブジェクトです。直感的でわかりやすいですよね。
そして上のようにblogオブジェクトのarticlesプロパティからそれぞれの記事情報を取得することができます。articleオブジェクトを取得することができたら後はブログ記事テンプレートの作り方と一緒です。
テンプレート以外からblogオブジェクトを扱う
ブログテンプレート上ではblogオブジェクトが自動的に与えられるのですぐにブログ記事情報を取得することができますが、テンプレートファイル以外でblogオブジェクトを取得するにはどうすれば良いでしょうか。
あるいは、ブログテンプレート内で現在扱っているブログではない他のブログのオブジェクトを取得するにはどうすれば良いでしょうか。
そういったときにまず考えるのがblogオブジェクトをまとめている「blogs」という存在です。
これはグローバルな変数であり、テーマファイルであれば基本的にどこでも利用することができます。
{
"{ブログ1のハンドル}":blogオブジェクト,
"{ブログ2のハンドル}":blogオブジェクト
}
ただこのblogsという存在には厄介な点があって、配列型ではなくオブジェクト(辞書型配列的な)なんですよね。上のような構造になっています。
たとえ辞書型だとしても通常のプログラミング言語であればforでループを回すことができますが、Liquidのforでは普通の配列しか扱うことができないようです。 また、オブジェクトのキーだけを取り出すフィルターも存在しませんし、jsonというオブジェクトの中身をJSON形式で出力してくれるフィルターは一応あるものの、blogsオブジェクトでは利用することができません。
そのため、blogsオブジェクトからblogオブジェクトを取得するにはキー名、つまりブログのハンドル名が事前にわかっていないといけません。
<div id="blog_list">
{% for article in blogs.blog1.articles %}
<li>
<article>
<h2><a href="{{ article.url }}">{{ article.title }}</a></h2>
<p>{{ article.excerpt_or_content }}</p>
</article>
</li>
{% endfor %}
</div>
上のような感じですね。この場合、記事を取得するブログの名前(blog1)が事前にわかっているので、スムーズに記事を取得することができます。 しかしすべてのブログを取得する、すなわちまだそれぞれのブログの名前がよくわかっていない状態で記事情報を取得するにはひと工夫必要になってきます。
全ブログのオブジェクトを取得する
ここで結構引っかかりました。テンプレートの下りまでは非常に直感的でよかったのですが、ブログリストを取得するとなったとたんなんか使い勝手悪くなる言語ですよね。
1.ファイル内で直接ブログリストを登録
一番最初に思い浮かんだのはこの方法です。
{% assign blog_list = 'blog1,blog2,blog3' | split: ',' %}
<div id="blog_list">
{% for blog_handle in blog_list %}
{% assign blog = blogs[blog_handle] %}
{% for article in blog.articles %}
<li>
<article>
<h2><a href="{{ article.url }}">{{ article.title }}</a></h2>
<p>{{ article.excerpt_or_content }}</p>
</article>
</li>
{% endfor %}
{% endfor %}
</div>
ブログの名前リストをあらかじめ配列形式で作っておいて、それをforで回しつつ毎回blogオブジェクトをblogsオブジェクトから取得することであらかじめ記述されているブログのすべての記事を取り出すことができています。
2.テーマ設定から読み込む
上の方法だとブログが増えたときにテーマファイルを直接いじらないといけなくなるので、
{% assign blog_list = settings.blog_list | split: ',' %}
<div id="blog_list">
{% for blog_handle in blog_list %}
{% assign blog = blogs[blog_handle] %}
{% for article in blog.articles %}
<li>
<article>
<h2><a href="{{ article.url }}">{{ article.title }}</a></h2>
<p>{{ article.excerpt_or_content }}</p>
</article>
</li>
{% endfor %}
{% endfor %}
</div>
[
{
"name": "ブログリスト",
"settings": [
{
"type":"text",
"id":"blog_list",
"label":"ブログのリスト",
"default":"blog1,blog2,blog3"
}
]
}
]
こういう風にすればblog_listというハンドルを持っている設定情報と同期することができます。これでテーマファイルを直接いじらなくてもテーマ設定から誰でも簡単に変えれますね。
3.リンクリスト(メニュー)機能を使う
Shopifyの「メニュー」という機能を使えばあらかじめ任意のオブジェクトのグループを作ることができ、それをLiquid上で扱えるようになります。この機能を応用してブログリストを取得することもできます。
左メニューからアクセスすることができる「メニュー」という機能で、上のようにブログを追加してみました。「メニュー項目を追加する」というところからいくらでも追加することができます。
また、このメニューを扱うためのハンドル名もしっかり決めておきましょう。
{% assign blog_list = linklists.blog_list.links %}
<div id="blog_list">
{% for blog_link in blog_list %}
{% assign blog = blog_link.object %}
{% for article in blog.articles %}
<li>
<article>
<h2><a href="{{ article.url }}">{{ article.title }}</a></h2>
<p>{{ article.excerpt_or_content }}</p>
</article>
</li>
{% endfor %}
{% endfor %}
</div>
Liquid上で扱うときは「linklists」というグローバルな変数を使います。そこからリンクリストのオブジェクトにアクセスし、そのlinksというプロパティによってブログのリストを取得します。
ここでループで回されるオブジェクトはblog型ではなく、link型というもので、そのobjectプロパティによってblogオブジェクトを取得することができます。
なんだかオブジェクトの構造が複雑ですが、慣れてしまえばそうでもないですね。
これで、新しくブログが追加されたときは「メニュー」機能から一手間かけて追加するだけでオッケーです。
でもいずれにせよ自動ですべてのブログを取得するような機能はLiquid言語だけでは限界がありそうでうね。 もしかしたらShopify Flowを活用すれば実現できるかもしれませんが、面倒くさそうです。
これをどうしても回避したいなら、単一のブログにタグ(article.tags)を使ってグループ分けするなどの手もありますね。そうすれば難なくすべての記事を取得できたりします。 もしくはブログ記事のテンプレート(article.template_suffix)を使ってグループ分けするという手もあります。
違うグループのブログ記事だったら違うテンプレートを当てたくなることはあると思うので、テンプレートでグループ分けというのは割と理にかなっていますね。
いかがだったでしょうか。BableTechBlogでは今後もShopifyのノウハウを様々公開していきますのでこれからもよろしくお願いします。
Shopify関連の案件や相談も受けていますので、ぜひお問い合わせください!