Shopify オリジナルテーマの実装 〜Liquid文法解説〜

Shopifyテーマ作成用言語Liquidの基本文法

本記事は私達の会社で作ったShopifyテーマ開発入門書『Hello Shopify Themes Shopifyテーマ開発ガイド』からの抜粋です。


本節では、Liquidの文法について解説します。
本書をここまで順に読まれた方は既に把握しているかと思いますが、Liquidの構文は主にShopifyのコンテンツデータを取得し、ページ上に表示するために用います。
 
本節で紹介する内容は次のとおりです。

  • Liquidの基礎
  • Liquidオブジェクト
  • Liquidタグ
  • Liquidフィルター

なお、現在利用できるLiquidタグやLiquidオブジェクトの全てを紹介することはしません。数が多すぎることと、Shopifyのアップデートによって本書出版後も増加が見込まれるためです。
公式ドキュメントの読み方も紹介しますので、併せて活用してみてください。

公式ドキュメントの読み方、探し方

まず、Shopify公式によるLiquidのドキュメントとして、次のサイトがあります。

現時点では英語ドキュメントしかありませんが、情報の多さ・更新速度・正確さともに群を抜くため、英語は苦手……という方も、ブラウザに翻訳拡張ツールを入れてぜひ活用してみてください。
Liquidの基礎的な文法に加え、Shopify特有のLiquidオブジェクト・Liquidタグ・Liquidフィルターの情報も掲載されています。
 
なお、上記ドキュメントとは別に、Liquidの公式サイトもあります。

Shopify固有の情報は掲載されていませんが、Liquidの一般的なタグ・フィルターについては網羅されており、併せて参照すると役立ちます。

英語情報を検索する

Shopifyはアップデートが活発なため、ドキュメントの構造やURLが比較的良く変わります。ドキュメントのどこに何があるか覚えるより、都度外部から検索した方が、必要な情報には辿り着きやすいです。
日本語検索ではマーチャント向けの情報が出てきやすいため、技術情報については英語で検索する癖をつけましょう。
Shopify product objectShopify date filter など、「どのコンテンツデータの、何の機能について知りたいのか」で検索すると良いです。

Liquid記法の基礎(型・演算子・真偽値・変数・空白制御)

まずはLiquidの基礎的な文法について紹介します。

Liquidオブジェクトには、次に示す6つのデータ型があります。

  • String
  • Number
  • Boolean
  • Array
  • Nil
  • EmptyDrop

JavaScriptなど、他のプログラミング言語に触れたことがある方には馴染みのものも多いですね。
簡単な特徴を次の表にまとめました。なお、変数宣言の際にデータ型を指定する必要はありません。

説明
String 文字列。 'sample' もしくは "sample" のように、引用符で囲む
Number 数列。数式フィルターで四則演算ができる
Boolean 真偽値。 true または false
Array 配列。配列フィルターを通せば出力を変更できる
Nil コード出力結果が存在しない場合に返される空の値。Liquid上の出力は空白となる
EmptyDrop アクセスしたオブジェクトに何も格納されていない場合に返されるオブジェクト

型変換

Liquidの型変換は、Liquidフィルター(後述)を介して行います。
次に示すサンプルは、文字列である「2022」を数列に変換するLiquidフィルターです。

{{ "2022" | abs }}

演算子

Liquidで扱える論理演算子と比較演算子は次のとおりです。

演算子 意味
== 等しい
!= 等しくない
> より大きい
< より小さい
>= 以上
<= 以下
or 条件Aまたは条件B
and 条件Aおよび条件B
contains ※後述

論理演算子だけ orand と英語が出現するため、JavaScriptとLiquidを同時に触っていると混乱しがちです。 && ではないんですよね……。
 
また、特殊な演算子として contains が存在します。これは文字列および文字列の配列内に、指定した文字列が存在するかどうかを判定し、true/falseを返します。
次に示すコードは、ページのタイトルにオンラインストアの名称が含まれていない場合のみ、オンラインストアの名称を出力します。

{% unless page_title contains shop.name %}
  {{ shop.name }}
{% endunless %}

真偽値

文字列など、Booleanではない型が条件判定タグ内で用いられた場合、nil 以外の全ての型は true として扱われます。空の文字列や EmptyDrop 型であっても同様です。
 
対象が空の場合に条件分岐を行いたい場合は、blankかどうかの条件で判定するようにしましょう。

// page.descriptionが空でなければ、説明文を出力する
{% if page.description != blank %}
  <p>{{ page.description }}</p>
{% endif %}

変数

後述するLiquidタグ {% assign %} もしくは {% capture %} タグを用いて、Liquidファイル内で独自の変数を作成できます。作成した変数はLiquidオブジェクトとして扱えます。

変数のスコープ

Liquidファイル内で作成された変数は、基本的に、そのファイルの中でのみ利用可能です。別のファイルで作られた変数にはアクセスできません。
※例外として、スニペットファイルは読み込み時の {% render %} タグにパラメータが記述されていれば、読み込み元ファイルの変数の値を利用することができます。詳しくは「4-4. テーマファイル解説」のスニペットファイル解説で後述します。

注意:変数の上書きについて

Shopifyによって事前定義されているLiquidオブジェクトもまた、内部の値が可変であるLiquid変数です。
定義済みのLiquidオブジェクトと同名の変数を作成すると、内部の値は上書きされます。

<p>ストアの名称(上書き前):{{ shop.name }}</p>

//Liquidオブジェクトのshopを同名変数で上書き
{% assign shop  = '上書きテスト' %}

<p>ストアの名称(上書き後):{{ shop.name }}</p>

出力結果です。上書き後のshopオブジェクトはもうname属性を持たないため、空白が出力されています。

<p>ストアの名称(上書き前):Sample Store</p>
<p>ストアの名称(上書き後):</p>

値が上書きされるのは該当変数を作成したLiquidファイル内のみですが、意図しない形で上書きしないように注意してください。

空白の制御

Dawnテーマでは、多くのLiquidタグがハイフンを含む形で記述されています。
これは、HTML上に不要な空白を出力しないための記法です。
 
Liquidは、 {% if customer %} のような、ページ上には直接テキストを出力しないタグであっても、空行をHTMLに出力する仕様になっています。ブラウザの開発者ツールで見ていると気づきにくいのですが、ページ全体のソースを表示してみるとよくわかります。
{%- if customer -%} と記述することで、空行の出力を防げるようになります。
 
Liquidの基礎に関する解説は以上です。

Liquidオブジェクト

Liquidオブジェクトは、二重中カッコ {{ }} で囲んで記述することで、Shopifyのコンテンツデータをページ上に出力します。オブジェクトにはさまざまな種類があり、記述先のLiquidファイルによって振る舞いが変化するものもあります。
 
次に示すのは、商品ページごとに適した商品タイトルを表示する product オブジェクトです。 product オブジェクトは、 title 属性の他にも、 descriptionprice など、さまざまな属性を持っています。

{{ product.title }}

前述したとおり、本節ではオブジェクトの全てには触れず、一部に絞って解説します。
個々のオブジェクトの詳細については、Shopify公式ドキュメントも併せて参照してください。

オブジェクトハンドル

Shopifyのコンテンツ(商品・固定ページ・ブログ・ブログ記事・メニュー)は、それぞれを識別するハンドルを持っています。ページを持つコンテンツでは、ハンドルの値はそのままURLを構成します。
 
たとえば、次のURLを持つ商品のハンドル値は special-coffee です。
http://sample-store.myshopify.com/products/special-coffee

複数のコンテンツを含んでいるオブジェクトとハンドルを組み合わせることで、必要なコンテンツを適切に出力できるようになります。

次に示すのは、ストア上の全商品を保持するオブジェクト all_products から、ハンドル値 special-coffee を持つ product オブジェクトにアクセスするサンプルコードです。
{{ オブジェクト.ハンドル値.属性 }} または {{ オブジェクト["ハンドル値"].属性 }} と記述します。

{{ all_products.special-coffee.title }}
{{ all_products["special-coffee"].title }}

グローバルオブジェクト

テーマ内のどのLiquidファイルであっても利用できるのが、グローバルオブジェクトです。
テーマ開発において、利用頻度の高いものを紹介します。

オブジェクト 特徴
all_products ストア上のすべての product オブジェクトを含む配列
articles ストア上のすべての article オブジェクトを含む配列
blogs ストア上のすべての blog オブジェクトを含む配列
cart ストアのカート情報
collections ストア上のすべての collection オブジェクトを含む配列
current_tags 商品・記事など、レンダリング元テンプレートに応じて返されるタグの配列
customer ストアにログインしている顧客の情報(未ログイン時は何も返さない)
linklists ストア上のすべてのメニューリスト linklist オブジェクトを含む配列
pages ストア上のすべての page オブジェクトを含む配列
shop 名称や所在住所など、ストアに関する情報
settings テーマ設定に関する情報

前述したオブジェクトハンドルと組み合わせれば、必要なデータだけを取り出して扱うことができます。

productオブジェクト

テーマ開発において最も頻繁に扱うことになるのが、商品情報を格納しているこの product オブジェクトでしょう。
商品ページ用テンプレートの中では、該当する商品の情報へ自動的にアクセスできます。固定ページやブログ記事など、他のページで用いる場合は、 collection オブジェクトや all_products オブジェクトと組み合わせ、必要な商品にアクセスするようにしましょう。
40近くの属性を持ち、そのすべてを覚える必要はありませんが、利用頻度が高い属性は公式ドキュメントで仕様を把握しておくと役立ちます。

次の表は、利用頻度の高い属性の一例です。

属性 特徴
product.available 商品の購入可否を判定し、true/falseを返す
product.description 商品の説明文
product.featured_media 商品のメイン画像
product.price 商品のバリエーション間における最低価格
product.tags 商品が持つタグ一覧の配列
product.title 商品のタイトル
product.type 商品のタイプ
product.url 商品の相対パスURL
product.variants 商品が持つバリエーション一覧の配列

その他の有用なオブジェクト

その他の有用なLiquidオブジェクトについては、「4-2. 各ページの実装留意点」でページごとに紹介しています。併せて、特に把握しておきたいオブジェクトを次に示します。

line_itemオブジェクト

line_item オブジェクトは、カート内の商品を表す配列 cart.item や注文した商品を表す配列 order.line_item オブジェクト内の、個々の商品を表します。
カートのカスタマイズ時だけでなく、顧客宛ての購入完了通知メールの中などでも用いるオブジェクトです。

sectionオブジェクト・blockオブジェクト

セクションファイルおよび、セクションファイル内でレンダリングされたスニペットファイルでのみ利用できる固有のオブジェクトです。
こちらは次節「4-4. テーマファイル解説」で詳しく掘り下げます。

Liquidタグ

Liquidタグは、Liquidファイルの中で用いるプログラミングロジックの記述です。%付き中カッコ {% %} で記述します。
ざっくりと以下の4区分に分けられます。

  • 条件判定タグ
  • 反復出力タグ
  • テーマタグ
  • 変数タグ

条件判定

if文のような条件判定をLiquid中に作成します。

if

条件判定結果がtrueである場合のみ、内部のコードを出力・実行します。

{% if product.title == 'Hello Shopify Themes' %}
  <p>Enjoy Shopify Development!</p>
{% endif %}

unless

if文とは反対に、条件判定結果がfalseである場合に、内部のコードを出力・実行します。

{% unless product.available %}
  <p>この商品は購入できません。</p>
{% endunless %}

else / elsif

if / unless文の中でさらに条件判定を追加する場合に記述します。JavaScriptに慣れている方は、 {% else if %} ではなく {% elsif %} である点に注意してください。

{% if product.type == 'コーヒー' %}
  <a href="/collections/sweets_coffee">コーヒーに合うお菓子のセット</a>
{% elsif product.type == '紅茶' %}
  <a href="/collections/sweets_tea">紅茶に合うお菓子のセット</a>
{% else %}
  <a href="/collections/other">その他のおすすめ商品</a>
{% endif %}

case / whenについて

より複雑な条件判定、いわゆる switch 文を作成するタグとして {% case %}/{% when %} も存在します。Dawnでは sections/main-product.liquid などで用いられています。
興味がある方は、公式ドキュメントを参照してください。

Control flow tags
https://shopify.dev/api/liquid/tags/control-flow-tags#case-when

反復出力

繰り返し実行されるfor文をLiquid中に作成します。商品一覧ページや、ブログ記事一覧ページなどでよく用いられます。1ループごとの出力上限は50までです。ループ対象がそれ以上の数となる場合は、後述するテーマタグ {% paginate %} でページを分割します。
なお、条件判定文と同様に、{% else %}を記述することで、ループの長さが0の場合の出力結果を追加できます。

{% for item in collection.products %}
  {{ product.title }}
{% else %}
  <p>条件に一致する商品がありません。</p>
{% endfor %}

より複雑なループ処理

{% for %} タグの中では、ループの反復を途中で停止する {% break %} タグや、反復を途中でスキップする {% continue %} タグも利用できます。
また、ループの開始・終了位置や、反復順序を指定するパラメータもあります。次に示すのは limit パラメータでループ終了位置を指定するサンプルコードです。

//コレクション内の商品数に関わらず、2点までしか出力しない
{% for product in collection.products limit:2 %}
  {{ product.title }}
{% endfor %}

▼パラメータ一覧

パラメータ 説明
limit ループ終了位置を指定
offset ループ開始位置を指定
(x...y) 数値ループの範囲を指定。x/yは任意の数字。例: {% for i in (3..5) %}
reversed ループ順序を逆転

forloopオブジェクトの活用

{% for %} タグの中でのみ用いられるLiquidオブジェクトとして forloop があります。
ループ中の最初・最後の項目のみtrueを返す属性などが用意されているので、活用してみてください。
次に示すのは、スライダーを構成するブロックオブジェクトのループ内で、先頭のブロックにのみ追加classを付与するサンプルコードです。

{% for block in section.blocks %}
  <div class="slide {% if forloop.first == true %}firstSlide{% endif %}">
    //中略
  </div>
{% endfor %}

cycle / tablerowについて

本書では割愛しますが、反復タグには他にも {% cycle %}{% tablerow %} が存在します。関心がある方は公式ドキュメントを参照してみてください。

Iteration tags
https://shopify.dev/api/liquid/tags/iteration-tags

テーマタグ

テーマに関わるロジックを指定するテーマタグにもさまざまな種類があります。本節では利用頻度の高いものを紹介します。

コメント

Liquidファイル内にコメントを記述します。
{% comment %}〜{% endcomment %} 間に記述された内容は、出力・実行されません。

レンダリング(section / snippet)

Liquidファイルのレンダリングを担当するタグです。 {% section 'セクションファイル名' %}{% render 'スニペットファイル名' %} があります。
この両者については、次節「4-4. テーマファイル解説」におけるセクションファイルとスニペットファイルのパートで詳しく解説しています。

ページネーション

1ループあたりの出力上限数が50であることは、先ほど「反復出力」の解説で述べました。
商品一覧やブログ記事一覧ページでは、ループ対象が容易に50を突破します(そもそも、50より少なめの数でページ区切りする方が一般的ですね)。その際に {% for %} タグを {% paginate %} タグで囲み、 by パラメータを渡すことで、指定した数ごとにループ結果を分割できます。
次のコードは、ページあたり10個の商品を出力し、出力結果が2ページ目以降に分割される場合はページネーションを追加するサンプルです。

{% paginate collection.products by 10 %}
  {% for product in collection.products %}
    (商品情報)
  {% endfor %}

  {% if paginate.pages > 1 %}
    {{ paginate | default_pagination }}
  {% endif %}
{% endpaginate %}

{{ paginate | default_pagination }} は、Shopifyにおけるデフォルトのページネーションを出力します。
独自のページネーションは、Liquidオブジェクトの paginate オブジェクトを活用して作成できます。

フォーム

商品購入フォームやアカウント作成フォームなど、テーマ開発では多くのフォームに触れる機会があります。テーマタグ {% form %} を用いれば、Shopifyで利用可能なフォームタイプごとに適したHTML要素が出力されます。
 
次に示すのは、顧客ログインフォームを出力するためのサンプルコードです。

{% form 'customer_login' %}
  <div class="field">
    <input type="email">
  </div>
  //その他、各項目のinput要素や、フォーム送信ボタンなど
{% endform %}

出力結果は次のようになります。HTMLの form 要素に、フォームに必要な action 属性などが自動的に付記された形でレンダリングされます。

<form accept-charset="UTF-8" action="https://sample.myshopify.com/account/login" id="customer_login" method="post">
  <input name="form_type" type="hidden" value="customer_login" />
  <input name="utf8" type="hidden" value="✓" />
  <div class="field">
    <input type="email">
  </div>
  //その他、各項目のinput要素や、フォーム送信ボタンなど
</form>

Shopifyには多くのフォームがあるため、本書で全ては解説しませんが、テーマ内に新たなフォームを追加してデータを送信したい場合は、 {% form %} タグのドキュメントを参照してみてください。

Theme tags(form解説セクション)
https://shopify.dev/api/liquid/tags/theme-tags#form

変数タグ

Liquidファイル内で独自の変数を作成します。
{% assign 変数名 = 値 %} もしくは、 {% capture 変数名 %}値{% capture %} の形式で作成可能です。

次に示すコードは {% assign %}{% capture %} を用いた変数作成のサンプルです。作成された変数には、再度値を代入できます。

//変数favoriteを作成
{% assign favorite = '猫' %}

//変数favorite_textを作成(favoriteの出力結果を含む)
{% capture favorite_text %}
<p>私は{{ favorite }}が好きです。</p>
{% endcapture %}

//変数favorite_textの出力
1:{{ favorite_text }}

//変数favoriteに値を再代入
{% assign favorite = 'りんご' %}

//変数favorite_textを再出力
2:{{ favorite_text }}

//変数favoriteを再出力
3:<p>私は{{ favorite }}が好きです。</p>

出力結果は次のようになります。

1:<p>私は猫が好きです。</p>

//変数favorite再代入後だが、{% capture %}作成時の変数favorite_textの値が表示される
2:<p>私は猫が好きです。</p>

//変数favoriteに再代入した値が表示される
3:<p>私はりんごが好きです。</p>

Liquidにおける変数の仕様については、Liquid記法の基礎で前述していますので、併せて参照してください。

increment / decrementについて

数値の変数を作成・増減できるタグとして {% increment %}{% decrement %} が存在します。本書では割愛しますが、関心がある方は公式ドキュメントを参照してみてください。

Variable tags
https://shopify.dev/api/liquid/tags/variable-tags

Liquidフィルター

Liquidフィルターは、出力内容を加工するための記述です。 {{ 加工元オブジェクト | フィルタ指定 }} のように、二重中カッコの中にセパレーター | を加えて記述します。フィルタ指定は複数組み合わせることも可能です。データの型変換(文字列→数列など)もフィルターを用いて行います。
次に示すのは、ブログ記事の投稿日を「2022.01.01」形式で出力するためのサンプルコードです。

{{ article.published_at | date: '%Y.%m.%d' }}

フィルタの指定を変えれば、元となる値はそのままに、出力結果だけを異なるものに加工できます。次のコードは投稿日を「2022年1月1日」形式で出力します。

{{ article.published_at | date: '%Y年%-m月%-d日' }}

2022年2月時点でドキュメントに記載されていたLiquidフィルター分類は次のとおりです。その後、さらに利用できるフィルターは増加しています。

フィルタ区分 特徴
Array filters 配列の加工フィルター
Color filters CSS色情報の加工フィルター
Font filters フォントオブジェクトの加工フィルター
HTML filters HTML要素を生成するフィルター
Math filters 数列の加工フィルター
Media filters 製品画像のURL取得&HTML出力フィルター
Metafield filters メタフィールドの加工フィルター
Money filters ストアの通貨設定に基づく加工フィルター
String filters 文字列の加工フィルター
URL filters URL取得およびリンク要素の出力・加工フィルター
Additional filters その他、一般的な加工フィルター

本書では一部を抜粋して紹介します。

Hosted fileフィルター(旧 URLフィルター)

Shopifyでは、画像などのアセットをCDN(読み込み速度を向上させるためのContent Deliver Network)経由で配信しています。Hosted fileフィルターを用いると、該当アセットのCDN上のURLを取得・出力できます。
その他にも、テーマ内のリンク要素や、指定に即したコレクションへのリンクなども出力可能です。
 
次に示すのは、 assets/theme.css のURLを出力するコードです。

{{ 'theme.css' | asset_url }}

asset_url の他にもおよそ20種ほどの記法があるので、何らかのURLを取得・出力したい場合はHosted fileフィルターのドキュメントを参照してみてください。

画像フィルター

画像URLを出力するだけのものから、HTMLの <img> タグを出力するものまで、画像出力についてもさまざまなフィルターがあります。
次に示すのは、ファビコン画像を <img> タグとして出力するためのサンプルコードです。 image_url フィルタと image_tag フィルタが用いられているのが分かります。

{{ settings.favicon | image_url: width: 200 | image_tag: srcset: nil, loading: lazy }}

※なお、区分としては、URLフィルターの image_url とHTMLフィルターの image_tag が組み合わされています。
出力結果は次のとおりです。

<img
  src="//cdn.shopify.com/s/files/xxxxxxxxxxxx/files/favicon.png?v=xxxxxxxxxx&width=200"
  width="200"
  height="200"
  loading="lazy">

image_urlフィルタで画像URL //cdn.shopify.com/s/files/xxxxxxxxxxxx/files/favicon.png?v=xxxxxxxxxx&amp;width=200 を取得し、さらに image_tag フィルタを通すことで、追加属性 loading="lazy" を含む <img> タグを出力しています。
 
image_url フィルタと image_tag フィルタには、双方とも出力結果を加工するためのパラメータが多数提供されているので、必要に応じて公式ドキュメントも参照してみてください。

dateフィルター

dateフィルターは、タイムスタンプの値を他の日付フォーマットに変換して出力します。次に示すコードの出力結果は、たとえば 2022.01.01 のようになります。

{{ article.published_at | date: '%Y.%m.%d' }}

フィルター内のパラメータ、サンプルコードにおける '%Y.%m.%d' 部分は、Rubyの strftime メソッドに沿って記述してください。
strftime の記法は、Rubyの公式ドキュメントか、フォーマット変換サイトで確認できます。

Ruby公式のstrftimeドキュメント
https://ruby-doc.org/core-3.0.2/Time.html#method-i-strftime
strftime変換サイト
http://www.strfti.me/

 

注意:dateフィルターにおける現在時刻の取得

現在日時を表示するために、 now または todaydate フィルターを組み合わせた事例が紹介されていることがあります。

{{ "now" | date: "%Y-%m-%d %H:%M" }}

こちらは一見、ユーザーにとっての現在時刻を出力するように思えますが、正確にはShopifyサーバー上でLiquidからページが生成された時点の時刻を出力しています。そのため、キャッシュの状況次第ではあるものの、実際の現在時刻から数分〜数時間のタイムラグが生じます。
厳密な現在時刻をロジックに組み込みたければ、JavaScriptの併用を検討してください。

date – Liquid template language(Liquid公式サイトによる解説)
https://shopify.github.io/liquid/filters/date/


私達の会社で制作したShopifyテーマ開発入門書のご購入はこちらから。
完売後の増刷予定は現時点ではございません。印刷書籍をご希望の方は、ぜひお早めにお迎えください。

  • 著者:川島さやか
  • 出版:non-standard world, Inc.
  • 判型・ページ数:B5判・248ページ
  • 価格:3,850円(税込)※PDF版同梱、送料無料

特集ページへ

記事カテゴリー