見出し画像

#05 ダイナミックセクションの作成方法

このページを読むことで以下のことが学べます。
①ダイナミックセクションの設定方法がわかります。
②Liquidについて詳しくなれます。
・for product in~ の product は変数?それともオブジェクトなのか?
collections.frontpage.products プロパティが2つあってもいいのか?
・グローバルオブジェクトについて

Liquidやプログラムについて詳しい方は
Liquidの解説前半・後半は飛ばしてもらって大丈夫です。
{{ content_for_index }} からご覧ください。

ダイナミックセクションとは

Shopifyの管理画面からセクションを追加・編集・削除をドラック&ドロップで操作できるセクションのことです。

オンラインストア2.0になる前まではトップページのみダイナミックセクションを使えていましたが、オンラインストア2.0になりShopifyの仕様が拡張されて、トップページ以外のほぼすべてのページでダイナミックセクションが使えるようになりました。


「templates」フォルダの「index.liquid」を開きます。

{% for product in collections.frontpage.products limit:4 %}
 <div>
   <a href="{{ product.url | within: collection }}">{{ product.title }}</a>
   {{ product.price | money }}
   {% unless product.available %}<br><strong>sold out</strong>{% endunless %}
   <a href="{{ product.url | within: collection }}">
     <img src="{{ product.featured_image.src | img_url: 'large' }}" alt="{{ product.featured_image.alt | escape }}">
   </a>
 </div>
{% endfor %}


Liquidの解説 前半

{% for product in collections.frontpage.products limit:4 %}
     ①      ②               ③
処理内容
{% endfor %}


①product は変数なのか?オブジェクトなのか?
②collections.frontpage.products
 collections オブジェクト
 .frontpage プロパティ
 .products プロパティ2

③limit:4 ループを指定された反復回数に制限する
 この場合 4回ループ処理する

※他のプログラム言語で「0」をループ回数に含む場合があるが、
 Liquidのforループの場合
 limit:4 なら 4回ループということになる
 0をループ回数に含まないので 5回ループということにはならない


for product in 条件

①forの後の product は変数なのか?オブジェクトなのか?

forの後の product は変数です。
ループ内の現在のアイテムを示す変数 になります。

Shopify公式ブログでも記載です。

(例){% for image in product.images %}

各リストアイテムのプロパティにアクセスするには、
ループ内の現在のアイテムを示す変数を指定します。
今回の例では、画像(image)です。

これは明白な選択であり、今後ほかのデザイナーがあなたのロジックを理解するのを助けますが、何でも入れようと思えば入れられます。
たとえば、次のようなalltheimagesintheworldを使うこともできます。

{% for alltheimagesintheworld in product.images %}

もちろんこれは馬鹿げた例です。imageのほうが合理的ですが、Liquidコレクションと関係がない変数であることを強調したかっただけです。

forの後はループ内で使用されている変数と書いてあります。
forの後はオブジェクトではなく変数だということです。
つまり「ループ内で使われている現在の変数」です。

◎ for ループ内で使用される現在の変数 in 条件
×  for オブジェクト in 条件

Liquidについての基本理解が足りなかったので以下の記事を見ました。
Liquidループの見出しのSTEP.1が参考になります!


collections.frontpage.products プロパティが2つ?

②collections.frontpage.products
オブジェクトに対してプロパティ2つ指定していいのか?

これは collections オブジェクトに対して
frontpage products の2つのプロパティがある訳ではありません。

プロパティはオブジェクトにもなる

ということです。

collections.frontpage.products
①     ②    ③

①collections オブジェクト
②.frontpage 
collections オブジェクトに対するプロパティ

プロパティだったものはオブジェクトになるので

frontpage オブジェクト
products   frontpage オブジェクトに対するプロパティ

――
① collections オブジェクトに対して
②frontpage  ③products プロパティがある訳ではありません。

このようなプロパティが2つになる理由は、
プロパティの中にさらにプロパティが存在するためです。

web業界で働く方を少しだけ手助けするメディアさんの記事が参考になります。


グローバルオブジェクト

collections.frontpage.products

collections オブジェクトはcollectionオブジェクトと違いはあるのか?
「s」 があるのと何が違うのか?

公式の開発者ドキュメントにはcollections オブジェクトという項目がなく
collectionがありました。
https://shopify.dev/api/liquid/objects#collections

しかし、私が探しているオブジェクトはcollectionsなので、
collectionsはオブジェクトではないのか?と思いましたが、

@eijiSaitoさんの記事を参考にすると「s」がつくと
どうやらグローバルオブジェクトと呼ばれるものだと気づきました。

グローバルオブジェクトとは
テーマ内の任意のファイルから使用することができるオブジェクトです。

私のイメージですが、オブジェクトを人に例えると、
わからないことをなんでも聞けるサポートセンターのような存在です。

例えば、オブジェクトに「商品名を教えて」と聞くとちゃんと答えを返してくれる存在です。実際にLiquidでコードを書くと「product.title」です。

ただ、そんななんでも聞ける存在でも聞く場所によって、答えを持っていないことがあります。聞きたいことをその場所(ファイル)では聞けないことがあります。これが普通のオブジェクトの役割です。
適切な場所でオブジェクトが知っていることを聞かないと答えてくれないのです。

適切な場所とは、例えばShopifyのテーマのフォルダ「template」にあるファイルから質問したとき、普通のオブジェクトにとっては
「ここでは答えることができません(箱には答えが入っていない)」
となる場合と
「そのことであれば答えはこれです。(箱には答えが入っている)」
となる場合があります。

普通のオブジェクトとグローバルオブジェクトの違いについては、人に例えると、グローバルオブジェクトはShopifyのテーマのどの場所(ファイル)から質問しても答えてくれる人をグローバルオブジェクトと呼び、
場所によって答えを持っていない人をオブジェクトと呼びます。

そして、どの場所からでも答えくれるグローバルオブジェクトは21種類あると言われています。

詳しくは@eijiSaitoさんの記事にまとめられているので参考になります。
https://qiita.com/eijiSaito/items/4e71457fc38a4964f977#global-objects

Liquidの解説 後半

 <div>
   <a href="{{ product.url | within: collection }}">{{ product.title }}</a>
     {{ product.price | money }}
        {% unless product.available %}<br><strong>sold out</strong>
        {% endunless %}
    <a href="{{ product.url | within: collection }}">
    <img src="{{ product.featured_image.src | img_url: 'large' }}" alt="{{ product.featured_image.alt | escape }}">
     </a>
</div>
 <a href="{{ product.url | within: collection }}">{{ product.title }}</a>

product.url 商品詳細ページのURLを取得する
| within: collection URLフィルター

| within: collectionを使うことで
現在表示中のコレクション内にある商品URLを取得することができる

(例)
コレクション名:アイスクリーム / ハンドル:icecreem
商品名:チョコレート /ハンドル chocolate

※ハンドルとはURLのことでWordPressに例えるとパーマリンクのことです

商品のURLは
<a href="{{ product.url | within: collection }}">

<a href="/collections/icecreem/products/chocolate">


@eijiSaitoさんの記事が参考になります。
https://qiita.com/eijiSaito/items/70aad4d59cc5427981e1#within

{{ product.price | money }}

product.price 商品価格
| money moneyフィルター

Shopifyの管理画面からストア通貨の設定ができます。
https://note.com/webyonet/n/ne3ea5df53566?magazine_key=m32b4fb7012f4

左下の「設定」から「一般設定」をクリックします。
ページ下部の「ストア通貨」の項目から「表示形式を変更する」をクリックすると以下の内容が展開されます。

①通貨表示が含まれているHTML ¥{{amount_no_decimals}} JPY
②通貨表示が含まれていないHTML ¥{{amount_no_decimals}}
③通貨表示が含まれているメール ¥{{amount_no_decimals}} JPY
④通貨表示が含まれていないメール ¥{{amount_no_decimals}}

| money moneyフィルターでは、
①通貨表示が含まれているHTML ¥{{amount_no_decimals}} JPY
の形式で表示されます。
なので、100円の場合は、「¥100 JPY」と表示されます。
①~④はShopifyの管理画面から編集できますので、修正することが可能です。

さらに詳しくは@eijiSaitoさんの記事が参考になります!
https://qiita.com/eijiSaito/items/8d20098567e48249bd92

{% unless product.available %}<br><strong>sold out</strong>
{% endunless %}

商品が購入可能でなければ sold out を表示する
<img src="{{ product.featured_image.src | img_url: 'large' }}" alt="{{ product.featured_image.alt | escape }}">


product.featured_image 商品の最初の画像
※featured とは最初の画像の意味

productオブジェクト
https://shopify.dev/api/liquid/objects/product#product-featured_image

featured_image.src 画像の相対パスを取得できる

imageオブジェクト
https://shopify.dev/api/liquid/objects/image#image-src

| img_url: 'large' 「この大きさで」と画像の大きさを指定できる

img_urlフィルター
https://shopify.dev/api/liquid/filters/url-filters#img_url

| img_url: 'large' ってどれくらいの大きさ?480×480

pico    16 x 16
icon    32 x 32
thumb   50 x 50
small     100 x 100
compact  160 x 160
medium  240 x 240
large       480 x 480
grande    600 x 600
1024x1024 1024 x 1024
2048x2048 2048 x 2048
master   largest image

Code Shopifyさんを参考にしました。
http://www.codeshopify.com/blog_posts/new-features-for-the-shopify-img_url-filter
<img src="{{ product.featured_image.src | img_url: 'large' }}" alt="{{ product.featured_image.alt | escape }}">


product.featured_image.alt 商品の説明
| escape エスケイプフィルター

| escape エスケイプフィルターの役割
HTML上で、>や、"、などの特殊文字を表示するための処理です。
例えば、<は、&lt;に変換されます。
https://qiita.com/eijiSaito/items/b4a1675ec196546aa4f2#escape


{{ content_for_index }}

このオブジェクトを使用することでダイナミックセクションの機能を追加することができます。Shopifyの管理画面からセクションの追加・編集・削除ができるようになります。

「templates」フォルダの「index.liquid」を開きます。

{% for product in collections.frontpage.products limit:4 %}
 <div>
   <a href="{{ product.url | within: collection }}">{{ product.title }}</a>
   {{ product.price | money }}
   {% unless product.available %}<br><strong>sold out</strong>{% endunless %}
   <a href="{{ product.url | within: collection }}">
     <img src="{{ product.featured_image.src | img_url: 'large' }}" alt="{{ product.featured_image.alt | escape }}">
   </a>
 </div>
{% endfor %}

元々あるコードを削除して以下のコードを追加します。

{{ content_for_index }}

管理画面に「+セクションを追加」が表示されます。

セクションを追加


ヒーローイメージ(メインビジュアル)を管理画面から編集できるようにする

「sections」フォルダに「hero.liquid」を作成します。

次にBootstrapのコードを記述します。
(Bootstrapの説明について省略させてください)

<section class="container py-5">
 <div class="row">
   <div class="col-lg-6 col-md-8 mx-auto">
     <h1>説明</h1>
     <p>これは説明文です。</p>
     <a href="#">オンラインショップ</a>
   </div>
 </div>
</section>


次にスキーマの記述をします。

スキーマについて
https://note.com/webyonet/n/n115ddd636cd4#NZMEM


スキーマを設定することで管理画面を自分で設定できるようになります。

{% schema %}
{
 "name": "Heroイメージ",
 "class": "hero-section",
 "settings": [
   {
     "type": "text",
     "id": "title",
     "default": "タイトル",
     "label": "見出しのタイトル"
   },
   {
     "type": "richtext",
     "id": "description",
     "default": "<p>段落</p>",
     "label": "見出し説明"
   },
   {
     "type": "text",
     "id": "button_label",
     "default": "ボタン",
     "label": "見出しボタンラベル"
   },
   {
     "type": "url",
     "id": "button_link",
     "label": "見出しボタンリンク"
   }
 ]
}
{% endschema %}

上記のJSONのコードでは「このテンプレートでご利用のテーマセクションはありません。」と管理画面に表示されます。

管理画面にセクションを反映させるにはプリセットの記述が必要

プリセット追加前


プリセットの記述を追加する
"presets": [
{
"category": "Hero",
"name": "Simple Hero"
}
]


プリセットのコードを追加する場所は {% endschema %} の上部です。


{% schema %}
{
 "name": "Heroイメージ",
 "class": "hero-section",
 
 "settings": [
   {
     "type": "text",
     "id": "title",
     "default": "タイトル",
     "label": "見出しのタイトル"
   },
   {
     "type": "richtext",
     "id": "description",
     "default": "<p>段落</p>",
     "label": "見出し説明"
   },
   {
     "type": "text",
     "id": "button_label",
     "default": "ボタン",
     "label": "見出しボタンラベル"
   },
   {
     "type": "url",
     "id": "button_link",
     "label": "見出しボタンリンク"
   }
 ],

 "presets": [
   {
     "category": "Hero",
     "name": "Simple Hero"
   }
 ]
}
{% endschema %}

プリセットの記述を追加することで
管理画面にダイナミックセクションのセクションの追加ができます。

「Simple Hero」というセクションが追加されています。

プリセット追加

管理画面にセクションを反映させるにはプリセットの記述が必要


JSONの記述が管理画面にどう影響するのか?

ダイナミックセクションのセクションの項目設定-01

◆JSON
"name": "Heroイメージ",
"class": "hero-section",



「"settings"」はセクションの中身を設定するときに必要な記述です。

ダイナミックセクションのセクションの項目設定-01

◆JSON
"settings": [
{
"type": "text",
"id": "title",
"default": "タイトル",
"label": "見出しのタイトル"
}
]


ダイナミックセクションのセクションの項目設定-01

◆JSON
"settings": [
{
"type": "richtext",
"id": "description",
"default": "<p>段落</p>",
"label": "見出し説明"
}
]
リッチテキストでは太字や斜体やリンクを設定することができる


ダイナミックセクションのセクションの項目設定-01

◆JSON
"settings": [
{
"type": "text",
"id": "button_label",
"default": "ボタン",
"label": "見出しボタンラベル"
}
]


ダイナミックセクションのセクションの項目設定-01

◆JSON
"settings": [
{
"type": "url",
"id": "button_link",
"label": "見出しボタンリンク"
}
]


管理画面から編集した内容がプレビューに反映されない

編集内容が反映されない

見出しのタイトルに「テスト」と入力してみましたが、プレビュー画面は「説明」のままです。本当なら「テスト」変更されるはずですが、反映されません。

編集画面の内容が反映されない理由
変数や値を実際に使用していないため

「sections」フォルダの「hero.liquid」を開きます。

<section class="container py-5">
 <div class="row">
   <div class="col-lg-6 col-md-8 mx-auto">
     <h1>説明</h1>
     <p>これは説明文です。</p>
     <a href="#">オンラインショップ</a>
   </div>
 </div>
</section>

上記のコードにBootstrapのクラスやLiquidの変数を追加していきます。

<section class="container py-5">
 <div class="row">
   <div class="col-lg-6 col-md-8 mx-auto">
     <h1>{{ section.settings.title }}</h1>
     {{ section.settings.description }}
     <a href="{{ section.settings.button_link }}" class="btn btn-secondary">
     {{ section.settings.button_label }}</a>
   </div>
 </div>
</section>

編集内容が反映されるとき

見出しのタイトルに「こんにちわ」を入力するとプレビュー画面も「こんにちわ」とリアルタイムで反映されました。


{{ section.settings.title }}

JSONの"settings": [ ] 内で設定したid「title」を出力する という意味
※idは任意で決めてOK
◆JSON
"settings":
[
{
"type": "text",
"id": "title",
"default": "タイトル",
"label": "見出しのタイトル"
}
]

残りは同じ要領です。
idだけを変えるだけでJSONで設定した項目を出力することが可能です。

{{ section.settings.description }}
{{ section.settings.button_link }}
{{ section.settings.button_label }}



画像アップローダーを付ける
ヒーロー画像を管理画面からアップロードできるようにするため

◆JSON(変更前)
"settings": [
{
"type": "text",
"id": "title",
"default": "タイトル",
"label": "見出しのタイトル"
}
]
◆JSON(変更後)
"settings": [
{
"type": "image_picker",
"id": "image",
"label": "Hero画像"
}
]

管理画面に画像アップローダーが追加できます。

画像アップロード追加




アップロードした画像の上に「見出しのタイトル」が来るように設定する

「hero.liquid」のHTMLコードを以下のように編集します。
(Liquidの解説は下に記載します)

<section class="inner-hero-section">
 {% if section.settings.image != blank %}
   <img src="{{ section.settings.image | img_url }}" alt="">
 {% else %}
   {% capture current %}{% cycle 1,2 %}{% endcapture %}
   {{ 'lifestyle-' | append: current | placeholder_svg_tag: 'placeholder-svg' }}
 {% endif %}
 
 <div class="container py-5">
   <div class="row">
     <div class="col-lg-6 col-md-8 mx-auto">
       <h1>{{ section.settings.title }}</h1>
       {{ section.settings.description }}
       <a href="{{ section.settings.button_link }}" class="btn btn-secondary">{{ section.settings.button_label }}</a>
     </div>
   </div>
 </div>  
</section>

プレビュー画面

プレビュー画面

画像の下にタイトルや段落とボタンがあります。
これを画像の上に追加するためのCSSを設定します。

「application.css.scss」に以下のコードを追記します。

html, body {}

.inner-hero-section {
   position: relative;
   overflow: hidden;
   width: 100%;
   height: 400px;
}

.inner-hero-section img,
.inner-hero-section svg {
   position: absolute;
   top: 0;
   bottom: 0;
   width: 100%;
}


プレビュー画面

プレビュー画面2


Liquidの解説

{% if section.settings.image != blank %}
  <img src="{{ section.settings.image | img_url }}" alt="">
{% else %}
  {% capture current %}{% cycle 1,2 %}{% endcapture %}
  {{ 'lifestyle-' | append: current | placeholder_svg_tag: 'placeholder-svg' }}
{% endif %}
もし管理画面の画像アップローダーに画像が設定されている場合は
アップロードした画像を表示する
それ以外の場合は
Shopifyが用意したnoimg用の画像を表示する


<img src="{{ section.settings.image | img_url }}" alt="">
section.settings.image
管理画面から画像をアップロードした画像

| img_url フィルター 画像のURLを取得できる

管理画面から画像をアップロードした画像のURLを出力する


{% capture current %}{% cycle 1,2 %}{% endcapture %}
{% capture current %}
  変数の中身
{% endcapture %}
assignではなくcaptureを使う理由①
複数行の文字列を含めた変数を作ることができる
(例)
{% assign favorite_food = "pizza" %}
{% assign age = 35 %}
{% capture about_me %}
  I am {{ age }} and my favorite food is {{ favorite_food }}.
{% endcapture %}
{{ about_me }}

I am 35 and my favorite food is piza.
assignではなくcaptureを使う理由②
変数の中身にLiquidを含めることができる
(例){% capture current %}{% cycle 1,2 %}{% endcapture %}

変数currentに1と2を交互に繰り返し代入する
{% cycle 1,2 %}

cycle 繰り返し処理 (この場合1と2を交互に出力する)

liquid noteさんの記事が参考になります。
https://ikdlog.com/liquid-tag/#rtoc-14

@eijiSaitoさんの記事が参考になります。
https://qiita.com/eijiSaito/items/1dcee7a82e47a7b20560#cycle


{{ 'lifestyle-' | append: current | placeholder_svg_tag: 'placeholder-svg' }}

出力されるHTMLコード
<svg class="placeholder-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1052 400"><path d="M727.XXXXX"></svg>

Shopifyが用意した画像(SVG)を表示するためのコード
| append: current
指定された文字列を別の文字列の末尾に追加する

この場合
'lifestyle-' | append: current
→ lifestyle-1
→ 
lifestyle-2


placeholder(プレースホルダー)名とは?

Shopifyが用意している画像(SVG)は複数あり、どの画像(SVG)を呼び出すのかを指定するために名前が割り振られています。
今回使用されている画像(SVG)は
プレースホルダー名「lifestyle-1」「lifestyle-2」です。
詳しくは公式のサイトで確認ができます。
https://shopify.dev/api/liquid/filters/additional-filters#placeholder_svg_tag

プレースホルダ―


| placeholder_svg_tag: 'placeholder-svg'

| placeholder_svg_tag: 'クラス名' SVGにCSSのクラス名を追加できる

<svg class="placeholder-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1052 400"><path d="M727.XXXXX"></svg>


placeholder_svg_tag
①プレースホルダー名を取得し、Shopifyが用意した画像(SVG)を出力する
②HTMLのSVGの記述が出力される

この記事が気に入ったらサポートをしてみませんか?