見出し画像

【Shopify】九十九悪嵐のうぇぶでざいんびぼうろく。〜アプリを使ってカスタムフィールドを作ってみる編〜

※使用テーマは『Dawn』

0.前置き

Shopifyでカスタムフィールドを作る場合、最新のバージョンでは『設定』にある『メタフィールド』を使えば作ることができます。

しかし、『メタフィールド』で作成できるタイプは『商品』、『バリエーション』、『コレクション』、『顧客』、『注文』の5つで、『ブログ』や『ブログ記事』、『ページ』など上記で挙げた5つ以外のタイプにはカスタムフィールドを作れません。

ではどうするのか、というと、自力で開発するか、アプリを使うかの二択になります。
自力で開発するとなると、人によっては時間がかかり、その分コストもかかるので、限られた時間・予算内では厳しいのではないかと思います。

そこで、僕は無料で使えるアプリ『Metafields & Custom Fields』をおすすめします。

1.アプリをインストールする

Shopifyにログインしたら『アプリ管理』を押し、『ストアをカスタマイズする』を押します。
すると、アプリストアが新規タブとして開かれるので、そこでアカウントを選択し、検索ボックスに『Metafields & Custom Fields』を入力し、検索します。

※見つからない、あるいは分からない人はこちら

『アプリを追加』を押し、自分のストアの画面に戻り、『アプリをインストール』を押したら、早速カスタムフィールドを作りましょう。

2.カスタムフィールドを作る

例として、今回はブログ記事にカスタムフィールド『商品』を作ります。

2-1.『Custom Fields』ボタンを選択する

※アプリを開くとデフォルトで『Custom Fields』で選ばれているので、ここの行動は省いても大丈夫です

2-2.『Posts』を選択する

2-3.カスタムフィールドを追加したいブログ記事を選択する

まずブログ(カテゴリーの方)を選び、その後ブログ記事を選びます。

2-4.『Go to Fields』を押す

2-5.『Advanced Custom Fields』から追加したいカスタムフィールドのタイプを選択する

今回は『Posts』を選択します。

2-6.『Products Fields』に『Data Types』または『Reference to』にあるパネルをドラッグアンドドロップする。

今回は『Product』をドラッグアンドドロップします。

2-7.Namespace、Key、Label、Instructionを入力する

例)
Namespace:test_field
Key:test_product
Label:テスト商品フィールド
Instruction:テスト商品フィールドです

2-8.『Field applies to』で追加したいフィールドを選択する

※セレクトボックスですが、一度選択したあとに、また選択すると複数の指定ができます

2-9.複数の値を許可する場合は『Allow selection of multiple values』チェックボックスにチェックを入れる

商品の場合はいくつも選択できた方がいい(と思う)ので、ここのチェックボックスにチェックを入れておきます。

2-10.『Done』ボタンを押す

2-11.『Save』ボタンを押す

Saveしないで画面遷移するとせっかく作ったカスタムフィールドをまた作り直すことになるので、ここでSaveした方がいいです。

3.カスタムフィールドを使う

せっかくカスタムフィールドを作ったので、早速使ってみましょう。

3-1.『販売チャネル』の『ブログ記事』で、カスタムフィールドを使いたい記事を選択する

3-2.画面上部にある『その他の操作』を選択する

3-3.『Edit Custom Field』を押す

3-4.作成したフィールド『テスト商品フィールド』で商品を選択する

『Products Selection』で商品を選択します。今回作成したフィールドでは複数選択できるので、複数の商品にチェックして『追加』することができます。

3-5.『Save』を押す

3-6.『More actions』を選び、『Edit Fields』を選ぶ

3-7.『テスト商品フィールド』の『Code』を選ぶ

3-8.『Metafield Code』にある『Copy』ボタンを押す

3-9.使用するテーマで『コードを編集する』を選択し、カスタムフィールドの値を出力したいファイルで3-8でコピーしたコードをペーストする

例)main-article.liquid

{{ 'section-blog-post.css' | asset_url | stylesheet_tag }}

<article class="article-template" itemscope itemtype="http://schema.org/BlogPosting">
  {%- for block in section.blocks -%}
    {%- case block.type -%}
      {%- when 'featured_image'-%}
        {%- if article.image -%}
          <div class="article-template__hero-container">
            <div class="article-template__hero-{{ block.settings.image_height }} media"
              itemprop="image"
              {% if block.settings.image_height == 'adapt' and article.image %} style="padding-bottom: {{ 1 | divided_by: article.image.aspect_ratio | times: 100 }}%;"{% endif %}
            >
              <img srcset="{% if article.image.width >= 350 %}{{ article.image | img_url: '350x' }} 350w,{% endif %}
                  {% if article.image.width >= 700 %}{{ article.image | img_url: '700x' }} 700w,{% endif %}
                  {% if article.image.width >= 749 %}{{ article.image | img_url: '749x' }} 749w,{% endif %}
                  {% if article.image.width >= 1498 %}{{ article.image | img_url: '1498x' }} 1498w,{% endif %}
                  {% if article.image.width >= 1100 %}{{ article.image | img_url: '1100x' }} 1100w,{% endif %}
                  {% if article.image.width >= 2200 %}{{ article.image | img_url: '2200x' }} 2200w,{% endif %}"
                sizes="(min-width: 1200px) 1100px, (min-width: 750px) calc(100vw - 10rem), 100vw"
                src="{{ article.image | img_url: '1100x' }}"
                loading="lazy"
                width="{{ article.image.width }}"
                height="{{ article.image.height }}"
                alt="{{ article.image.alt | escape }}">
            </div>
          </div>
        {%- endif -%}

        {%- when 'title'-%}
          <header class="page-width page-width--narrow">
            <h1 class="article-template__title" itemprop="headline">{{ article.title | escape }}</h1>
            {%- if block.settings.blog_show_date -%}
              <span class="circle-divider caption-with-letter-spacing" itemprop="dateCreated pubdate datePublished">{{ article.published_at | time_tag: format: 'month_year' }}</span>
            {%- endif -%}
            {%- if block.settings.blog_show_author -%}
              <span class="caption-with-letter-spacing" itemprop="author" itemscope itemtype="http://schema.org/Person">
                <span itemprop="name">{{ article.author }}</span>
              </span>
            {%- endif -%}
          </header>

        {%- when 'content'-%}
          <div class="article-template__content page-width page-width--narrow rte" itemprop="articleBody">
              {{ article.content }}
          </div>
  
        {%- when 'test_product' -%}
          {% assign array = article.metafields.test_field.test_product  | split: '|' %}
          <div class="article-template__product page-width page-width--narrow rte">
            <h2>{{ block.settings.test_product_title }}</h2>
            <ul>
              {%- for field in array -%}
                {% assign link = '/products/' | append: field %}
                <li>
                  <p class="img_wrap">
                    {% if all_products[field].images[0] != blank %}
                      <img src="{{ all_products[field].images[0] | img_url: 'master' }}" alt="{% if all_products[field].images[0].alt != blank %}{{ all_products[field].images[0].alt }}{% else %}{{ all_products[field].title }}{% endif %}">
                    {% else %}
                      <img src="https://placehold.jp/150x150.png" alt="{{ all_products[field].title }}">
                    {% endif %}
                  </p>
                  <p class="product_title">{{ all_products[field].title }}</p>
                  <p class="product_buy_item_button_wrap">
                    {{ block.settings.test_product_button_label | link_to: link }}
                  </p>
                </li>
              {%- endfor -%}
            </ul>
          </div>

        {%- when 'social_sharing'-%}
          <div class="article-template__social-sharing page-width page-width--narrow">
            {% render 'social-sharing', share_title: article.title, share_permalink: article.url, share_image: article.image %}
          </div>
    {%- endcase -%}
  {%- endfor -%}

  <div class="element-margin center">
    <a href="{{ blog.url }}" class="article-template__link link{% if blog.comments_enabled? == false %} spaced-section{% endif %}">
      {% render 'icon-arrow' %}
      {{ 'blogs.article.back_to_blog' | t: title: blog.title }}
    </a>
  </div>
  {%- if blog.comments_enabled? -%}
    <div class="article-template__comment-wrapper background-secondary">
      <div id="comments" class="page-width page-width--narrow">
        {%- if article.comments_count > 0 -%}
          {%- assign anchorId = '#Comments-' | append: article.id -%}

          <h2 id="Comments-{{ article.id }}">{{ 'blogs.article.comments' | t: count: article.comments_count }}</h2>
          {% paginate article.comments by 5 %}
            <div class="article-template__comments">
              {%- if comment.status == 'pending' and comment.content -%}
                <article id="{{ comment.id }}" class="article-template__comments-comment">
                  {{ comment.content }}
                  <footer class="right">
                    <span class="circle-divider caption-with-letter-spacing">{{ comment.author }}</span>
                  </footer>
                </article>
              {%- endif -%}

              {%- for comment in article.comments -%}
                <article id="{{ comment.id }}" class="article-template__comments-comment">
                  {{ comment.content }}
                  <footer class="right">
                    <span class="circle-divider caption-with-letter-spacing">{{ comment.author }}</span><span class="caption-with-letter-spacing">{{ comment.created_at | time_tag: format: 'month_year' }}</span>
                  </footer>
                </article>
              {%- endfor -%}
              {% render 'pagination', paginate: paginate, anchor: anchorId %}
            </div>
          {% endpaginate %}
        {%- endif -%}
        {% form 'new_comment', article %}
          {%- liquid
            assign post_message = 'blogs.article.success'
            if blog.moderated? and comment.status == 'unapproved'
              assign post_message = 'blogs.article.success_moderated'
            endif
          -%}
          <h2>{{ 'blogs.article.comment_form_title' | t }}</h2>
          {%- if form.errors -%}
            <div class="form__message" role="alert">
              <h3 class="form-status caption-large" tabindex="-1" autofocus>
                {% render 'icon-error' %} {{ 'templates.contact.form.error_heading' | t }}
              </h3>
            </div>
            <ul class="form-status-list caption-large">
              {%- for field in form.errors -%}
                <li>
                  <a href="#CommentForm-{{ field }}" class="link">
                    {%- if form.errors.translated_fields[field] contains 'author' -%}
                      {{ 'blogs.article.name' | t }}
                    {%- elsif form.errors.translated_fields[field] contains 'body'-%}
                     {{ 'blogs.article.message' | t }}
                    {%- else -%}
                      {{ form.errors.translated_fields[field] }}
                    {%- endif -%}
                    {{ form.errors.messages[field] }}
                  </a>
                </li>
              {%- endfor -%}
            </ul>
          {%- elsif form.posted_successfully? -%}
            <div class="form-status-list form__message" role="status">
              <h3 class="form-status" tabindex="-1" autofocus>{% render 'icon-success' %} {{ post_message | t }}</h3>
            </div>
          {%- endif -%}

          <div{% if blog.moderated? == false %} class="article-template__comments-fields"{% endif %}>
            <div class="article-template__comment-fields">
              <div class="field field--with-error">
                <input
                  type="text"
                  name="comment[author]"
                  id="CommentForm-author"
                  class="field__input"
                  autocomplete="name"
                  value="{{ form.author }}"
                  aria-required="true"
                  {% if form.errors contains 'author' %}
                    aria-invalid="true"
                    aria-describedby="CommentForm-author-error"
                  {% endif %}
                  placeholder="{{ 'blogs.article.name' | t }}"
                >
                <label class="field__label" for="CommentForm-author">{{ 'blogs.article.name' | t }} <span aria-hidden="true">*</span></label>
                {%- if form.errors contains 'author' -%}
                  <small id="CommentForm-author-error">
                    <span class="form__message">{% render 'icon-error' %}{{ 'blogs.article.name' | t }} {{ form.errors.messages['author'] }}.</span>
                  </small>
                {%- endif -%}
              </div>
              <div class="field field--with-error">
                <input
                  type="email"
                  name="comment[email]"
                  id="CommentForm-email"
                  autocomplete="email"
                  class="field__input"
                  value="{{ form.email }}"
                  autocorrect="off"
                  autocapitalize="off"
                  aria-required="true"
                  {% if form.errors contains 'email' %}
                    aria-invalid="true"
                    aria-describedby="CommentForm-email-error"
                  {% endif %}
                  placeholder="{{ 'blogs.article.email' | t }}"
                >
                <label class="field__label" for="CommentForm-email">{{ 'blogs.article.email' | t }} <span aria-hidden="true">*</span></label>
                {%- if form.errors contains 'email' -%}
                  <small id="CommentForm-email-error">
                    <span class="form__message">{% render 'icon-error' %}{{ 'blogs.article.email' | t }} {{ form.errors.messages['email'] }}.</span>
                  </small>
                {%- endif -%}
              </div>
            </div>
            <div class="field field--with-error">
              <textarea
                rows="5"
                name="comment[body]"
                id="CommentForm-body"
                class="text-area field__input"
                aria-required="true"
                {% if form.errors contains 'body' %}
                  aria-invalid="true"
                  aria-describedby="CommentForm-body-error"
                {% endif %}
                placeholder="{{ 'blogs.article.message' | t }}"
              >{{ form.body }}</textarea>
                <label class="form__label field__label" for="CommentForm-body">{{ 'blogs.article.message' | t }} <span aria-hidden="true">*</span></label>
            </div>
            {%- if form.errors contains 'body' -%}
              <small id="CommentForm-body-error">
                <span class="form__message">{% render 'icon-error' %}{{ 'blogs.article.message' | t }} {{ form.errors.messages['body'] }}.</span>
              </small>
            {%- endif -%}
          </div>
          {%- if blog.moderated? -%}
            <p class="article-template__comment-warning caption">{{ 'blogs.article.moderated' | t }}</p>
          {%- endif -%}
          <input type="submit" class="button" value="{{ 'blogs.article.post' | t }}">
        {% endform %}
      </div>
  </div>
  {%- endif -%}
</article>


{% schema %}
{
  "name": "t:sections.main-article.name",
  "tag": "section",
  "blocks": [
    {
      "type": "featured_image",
      "name": "t:sections.main-article.blocks.featured_image.name",
      "limit": 1,
      "settings": [
        {
          "type": "select",
          "id": "image_height",
          "options": [
            {
              "value": "adapt",
              "label": "t:sections.main-article.blocks.featured_image.settings.image_height.options__1.label"
            },
            {
              "value": "medium",
              "label": "t:sections.main-article.blocks.featured_image.settings.image_height.options__2.label"
            },
            {
              "value": "large",
              "label": "t:sections.main-article.blocks.featured_image.settings.image_height.options__3.label"
            }
          ],
          "default": "adapt",
          "label": "t:sections.main-article.blocks.featured_image.settings.image_height.label",
          "info": "t:sections.main-article.blocks.featured_image.settings.image_height.info"
        }
      ]
    },
    {
      "type": "title",
      "name": "t:sections.main-article.blocks.title.name",
      "limit": 1,
      "settings": [
        {
          "type": "checkbox",
          "id": "blog_show_date",
          "default": true,
          "label": "t:sections.main-article.blocks.title.settings.blog_show_date.label"
        },
        {
          "type": "checkbox",
          "id": "blog_show_author",
          "default": false,
          "label": "t:sections.main-article.blocks.title.settings.blog_show_author.label"
        }
      ]
    },
    {
      "type": "content",
      "name": "t:sections.main-article.blocks.content.name",
      "limit": 1
    },
    {
      "type": "test_product",
      "name": "商品",
      "settings": [
       {
          "type": "text",
          "id": "test_product_title",
          "default": "おすすめ商品",
          "label": "見出し"
        },
        {
          "type": "text",
          "id": "test_product_button_label",
          "default": "詳細を見る",
          "label": "ボタンのラベル"
        }
      ]
    },
    {
      "type": "social_sharing",
      "name": "t:sections.main-article.blocks.social_sharing.name",
      "limit": 1
    }
  ]
}
{% endschema %}

追加したコード抜粋した版↓

<!-- 〜〜省略〜〜 -->
        {%- when 'test_product' -%}
          {% assign array = article.metafields.test_field.test_product  | split: '|' %}
          <div class="article-template__product page-width page-width--narrow rte">
            <h2>{{ block.settings.test_product_title }}</h2>
            <ul>
              {%- for field in array -%}
                {% assign link = '/products/' | append: field %}
                <li>
                  <p class="img_wrap">
                    {% if all_products[field].images[0] != blank %}
                      <img src="{{ all_products[field].images[0] | img_url: 'master' }}" alt="{% if all_products[field].images[0].alt != blank %}{{ all_products[field].images[0].alt }}{% else %}{{ all_products[field].title }}{% endif %}">
                    {% else %}
                      <img src="https://placehold.jp/150x150.png" alt="{{ all_products[field].title }}">
                    {% endif %}
                  </p>
                  <p class="product_title">{{ all_products[field].title }}</p>
                  <p class="product_buy_item_button_wrap">
                    {{ block.settings.test_product_button_label | link_to: link }}
                  </p>
                </li>
              {%- endfor -%}
            </ul>
          </div>
<!-- 〜〜省略〜〜 -->
    {
      "type": "test_product",
      "name": "商品",
      "settings": [
       {
          "type": "text",
          "id": "test_product_title",
          "default": "おすすめ商品",
          "label": "見出し"
        },
        {
          "type": "text",
          "id": "test_product_button_label",
          "default": "詳細を見る",
          "label": "ボタンのラベル"
        }
      ]
    },
<!-- 〜〜省略〜〜 -->

あとはスタイルを調整したら完成です!

お疲れ様でした〜。

4.コード解説

今回コードはあまり追加してないので、ここで解説します。解説なんか要らんよって人はもう閉じちゃって大丈夫です。
それじゃあ解説に入りまーす。

<!-- 〜〜省略〜〜 -->
    {
      "type": "test_product",
      "name": "商品",
      "settings": [
       {
          "type": "text",
          "id": "test_product_title",
          "default": "おすすめ商品",
          "label": "見出し"
        },
        {
          "type": "text",
          "id": "test_product_button_label",
          "default": "詳細を見る",
          "label": "ボタンのラベル"
        }
      ]
    },
<!-- 〜〜省略〜〜 -->

スライダーを作った人にはお馴染みのセクション・ブロックを追加するコード。
今回は見出しとボタンを追加したいので、それらのブロックを作ってあげています。

<!-- 〜〜省略〜〜 -->
        {%- when 'test_product' -%}

作ったセクションが画面上にあった場合の「when」です。

          {% assign array = article.metafields.test_field.test_product  | split: '|' %}

変数「array」を用意し、この変数に作成したカスタムフィールドを格納しています。デフォルトだと「|(パイプ)」で区切られているので、「spilit」でわざと除外しています(その方がカスタマイズしやすいから)。

          <div class="article-template__product page-width page-width--narrow rte">
            
          </div>

他のセクションの流用。そうすることで他のセクションと同じ余白や文字の大きさなどのスタイルを利用することができます。

            <h2>{{ block.settings.test_product_title }}</h2>

カスタマイズ画面で入力した見出し用。

            <ul>

            </ul>

ulタグ。divタグでもいいが、その場合、その子要素はliタグではなくなるので注意。

              {%- for field in array -%}

              {%- endfor -%}

変数arrayのままだとリンクや画像の設定等で使いにくいので、forタグを使って一つずつ取り出すようにしています。

                {% assign link = '/products/' | append: field %}

詳細ページへとバス用のリンクの変数「link」をここで設定しています。「append: field」にすることで「/products/originalpizza」のようなリンクになります。

                <li>

                </li>

liタグです。スタイルを調整しないと中黒(「・」のこと)が表示され、ダサくなってしまうのでカッコよくしましょう。

                  <p class="img_wrap">
                    {% if all_products[field].images[0] != blank %}
                      <img src="{{ all_products[field].images[0] | img_url: 'master' }}" alt="{% if all_products[field].images[0].alt != blank %}{{ all_products[field].images[0].alt }}{% else %}{{ all_products[field].title }}{% endif %}">
                    {% else %}
                      <img src="https://placehold.jp/150x150.png" alt="{{ all_products[field].title }}">
                    {% endif %}
                  </p>

カードリストにする想定しているので、liタグの子要素はpタグで囲んであげています。本当はdivタグとかの方がいいんだろうけど…まあいいや

all_productsオブジェクトはforタグを使わなくてもすべての商品を取得してくれるオブジェクトなので使用していますが、1ページ20件までの制限があります。おすすめ商品を30件とか100件とか出されても…って感じだし、ということで、all_productsオブジェクトを使っています。もし20件以上出したいんだという人はforタグを使いましょう。

あ、そうそう。ここではサムネイルの設定をしています。商品の情報を取得して、商品に画像が設定されていればその画像を出力し、ない場合は代わりの画像を出力するようにしています(ダミー画像を生成してくれるサイトの画像をお借りしています)。

                  <p class="product_title">{{ all_products[field].title }}</p>

ここでは商品名を出力するようにしています。

                  <p class="product_buy_item_button_wrap">
                    {{ block.settings.test_product_button_label | link_to: link }}
                  </p>

ここではセクションで設定したボタンのラベルが出るようにしつつ、link_toフィルターでボタン(リンク)になるように設定しています。aタグを書かなくてもlink_toフィルターで勝手にaタグが生成されるので便利ですねえ。

以上解説でした~。

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