オプション販売やセット販売時に、 商品ページ内で別商品をカートに追加するコードの紹介

オプション販売やセット販売時に、 商品ページ内で別商品をカートに追加するコードの紹介

以下の画像のように、例えばスポーツウェアの上下セットの商品ページ内で、単品商品(別商品)をカートに追加するためのフォームを出力するためのコードの紹介になります。

以下が実装できれば、アプリを使用せずとも、1つの商品ページの中でセット販売やオプション商品をカートに追加することができるようになります。

まずは、以下の3つのファイルをスニペットフォルダ内に準備します。

↓ファイル名 product-bundle-card.liquid



{%- style -%}
.product-bundle{
border-radius: 8px!important;
padding: 10px 0;
}


.product-bundle__title{
font-size: 20px;
font-weight: 600;
text-align: left;
padding: 0 0 5px;
}

.product-bundle__image{
width: 100%;
}

.product-bundle__meta{
text-align: left;
font-size: 16px;
padding: 0px 0;
}
.product-bundle__form{
text-align: center;
}

.product-bundle__submit{
width: 80%;
font-size: 1.1em;
background-color: #11345e;
color: #fff;
font-weight: bold;
border-radius: 50em!important;
}

.product-bundle-selector-wrapper{
width: 100%;
margin-bottom: 10px;
}
.product-bundle-selector-wrapper select{
width: 100%;
outline: none;
}

.product-bundle-form__variants{
display: none;
}

.option_name{
font-size: 12px;
text-align: left;
padding-left: 0px;
margin: 0;
}

.product-bundle-form__item:after {
content: "";
position: absolute;
right: 5px;
top: 50%;
width: 0;
height: 0;
border-style: solid;
border-width: 7px 7px 0 7px;
border-color: #ccc transparent transparent transparent;
transform: translate(-50%, -50%);
font-size: 20px;
pointer-events: none;
}
.product-bundle-form__item {
position: relative
}
.product-bundle-form__item select{
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
outline: none;
}
{%- endstyle -%}


<div class="product-bundle {% unless product.available %} product-bundle--sold-out{% endunless %}">
  <a class="product-bundle__link" href="{{ product.url | within: collection }}">
  <div class="h4 product-bundle__title">{{ product.title }}</div>
  <img class="product-bundle__image" src="{{ product.featured_image.src | img_url: '500x', scale: grid_image_scale }}" alt="{{ product.featured_image.alt }}">
  <div class="product-bundle__meta">
    {% include 'product-bundle-price' %}
  </div>
  </a>

  <form action="/cart/add" method="post" enctype="multipart/form-data"
    class="product-bundle__form product-form--hide-variant-labels">
    {% unless product.options.size == 1 and product.variants[0].title == 'Default Title' %}
    {% for option in product.options_with_values %}
    <p class="option_name">{{ option.name }}</p>
    <div class="product-bundle-selector-wrapper product-bundle-form__item">
      <select class="product-bundle-single-option-selector product-bundle-form__input"
        id="SingleOptionSelector-{{ forloop.index0 }}" data-index="option{{ forloop.index }}">
        {% for value in option.values %}
        <option value="{{ value | escape }}" {% if option.selected_value==value %} selected="selected" {% endif %}>{{
          value }}</option>
        {% endfor %}
      </select>
    </div>
    {% endfor %}
    {% endunless %}

    <select name="id" class="product-bundle-form__variants">
      {% for variant in product.variants %}
      {% if variant.available %}
      <option {% if variant==product.selected_or_first_available_variant %} selected="selected" {% endif %}
        value="{{ variant.id }}">
        {{ variant.title }}
      </option>
      {% else %}
      <option disabled="disabled">{{ variant.title }} - {{ 'products.product.sold_out' | t }}</option>
      {% endif %}
      {% endfor %}
    </select>
    <input type="hidden" id="Quantity" name="quantity" value="1" min="1" class="product-form__input" pattern="[0-9]*">
    <input type="submit" value="{{ 'products.product.add_to_cart' | t }}" class="product-bundle__submit" />

  </form>

</div>

↓ファイル名 product-bundle-collection.liquid



{%- style -%}
.bundle_collection__title{ text-align:center; padding: 20px;
font-size: 1.8em; color: #111; }
.bundle_collection{
display: flex;
flex-direction: row;
justify-content: space-between;
}
.bundle_item {
border-radius: 8px;
border: 1px solid #ddd;
width: 49%;
padding: 15px;
background-color: #f5f5f5;}
.bundle_item:first-of-type {display: none;}
@media only screen and (min-width: 750px) {
.product-bundle__title{
font-size: 18px;
}
.bundle_item {
padding: 8px;
}
.bundle_item:first-of-type {display: none;}
.bundle_item{ {% if columns == 4 %} width: 25%; margin-bottom: 0;
{% else %}
{% comment %}
{% if columns== 3 %}
width: 33%; margin-bottom: 0; {% else %} width: 50%; margin-bottom:20px;
{% endif %}
{% endcomment %}
{% endif %} } }
{%- endstyle -%} {% assign
bundle_tag = "" %} {% for tag in product.tags %} {% if tag contains "bundle-" %}
{% assign bundle_tag = tag %} {% endif %} {% endfor %}

<script>
  var bundleProducts = [];
</script>

{% assign has_bundle = false %} {% for product in collections.all.products %} {%
if product.tags contains bundle_tag %} {% assign has_bundle = true %} {% endif
%} {% endfor %} {% if has_bundle %}

<div class="page-width" id="Collection" style="margin-top: 50px">
  {%- assign grid_item_width = 'medium-up--one-third' -%} {%- assign image_size
  = '530x530' -%}

  <div id="bundle_dsn" class="bundle_collection">
    {% assign has_bundle = false %} {% for product in collections.all.products
    %} {% if product.tags contains bundle_tag %}

    <script>

      bundleProducts.push({{ product | json }});
    </script>

    <div class="bundle_item" id="{{ product.id }}">
      {% include 'product-bundle-card' %}
    </div>

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

<script>
  var bundleProductMapping = {};

  bundleProducts.forEach(function (p) {
    if (!bundleProductMapping[p.id]) bundleProductMapping[p.id] = {};

    p["variants"].forEach(function (v) {
      bundleProductMapping[p.id][v.title] = {
        price: v.price / 100.0,

        compare_at_price: v.compare_at_price / 100.0,

        id: v.id,
      };
    });
  });
  $(window).on("load", function () {
    // $(document).ready(function () {
    $(".product-bundle-single-option-selector").change(function (e) {
      var selectedVariants = [];

      $(e.target)
        .closest(".product-bundle__form")
        .find(".product-bundle-single-option-selector")
        .each(function (el) {
          selectedVariants.push($(this).val());
        });

      var variantsString = selectedVariants.join(" / ");

      var productId = $(e.target).closest(".bundle_item").attr("id");

      var variant = bundleProductMapping[productId][variantsString];

      if (variant) {
        var finalPrice = variant.compare_at_price || variant.price;
        var finalPrice = String(finalPrice).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
        $(e.target)
          .closest(".product-bundle")
          .find(".product-bundle__meta .product-bundle-price__price")
          .html("¥" + finalPrice);

        $(e.target)
          .closest(".product-bundle")
          .find(".product-bundle__meta .product-bundle-price__sale")
          .html("¥" + variant.price);

        $(e.target)
          .closest(".product-bundle__form")
          .find(".product-bundle__submit")
          .removeAttr("disabled");

        $(e.target)
          .closest(".product-bundle__form")
          .find('[name="id"]')
          .val(variant.id);
      } else {
        $(e.target)
          .closest(".product-bundle")
          .find(".product-bundle__meta .product-bundle-price__price")
          .html("");

        $(e.target)
          .closest(".product-bundle")
          .find(".product-bundle__meta .product-bundle-price__sale")
          .html("");

        $(e.target)
          .closest(".product-bundle__form")
          .find(".product-bundle__submit")
          .attr("disabled", "disabled");
      }
    });
  });
</script>

{% endif %}

↓ファイル名 product-bundle-price.liquid


{%- style -%} .product-bundle__price_title{ font-size: 1em; font-weight: bold;
text-align: center; padding: 10px; color: #666; } {% if compare_at_price > price
%} .product-bundle-price__price{ color: #bbb; font-size: 1em; } {% else %}
.product-bundle-price__price{ color: #111; font-size: 1.1em; } {% endif %}
.product-bundle-price__sale{ color: #111; font-weight: bold; font-size: 1.1em; }
{%- endstyle -%}
 {% if product.title %}
  {%- assign compare_at_price = product.compare_at_price -%}
  {%- assign price = product.price -%}
  {%- assign price_varies = product.price_varies -%}
  {%- assign available = product.available -%}
 {% else %} {%- assign compare_at_price = 1999 -%} {%- assign price = 1999
-%} {%- assign price_varies = false -%} {%- assign available = true -%} {% endif
%} {%- assign money_price = price | money -%}


{% if compare_at_price > price %} {% if price_varies %}

<span class="visually-hidden">{{ "products.product.regular_price" | t }}</span>

<span class="product-bundle-price__price">{{ compare_at_price | money }}</span>

<span class="product-bundle-price__price product-bundle-price__sale">
  {{ money_price }}
</span>

{% else %}

<span class="visually-hidden">{{ "products.product.regular_price" | t }}</span>

<s class="product-bundle-price__price">{{ compare_at_price | money }}</s>

<span class="product-bundle-price__price product-bundle-price__sale">
  {{ money_price }}
</span>

{% endif %} {% else %} {% if price_varies %}

<span class="product-bundle-price__price">{{ money_price }}</span>

{% else %}

<span class="visually-hidden">{{ "products.product.regular_price" | t }}</span>

<span class="product-bundle-price__price"
  >{% if product.variants.size > 1 %}{% endif %}{{ money_price }}</span
>

{% endif %} {% endif %}

そして出力したい商品ページのテンプレート(基本はpage.liquid)の中で以下を記述


{% include 'product-bundle-collection', columns: 3 %}

そして、商品管理画面を開き、対象商品のタグの部分に「bundle-doubles」などと記述し、一緒に表示したい商品のタグにも同様にタグに同じ記述をする。今回の場合だと3商品のタグに同じタグを記述しました。product-bundle-collection.liquidの中で「bundle-」で始まるものを条件にしているのでタグは必ず「bundle-」で始まる必要があります。

参考:https://ecomexperts.io/blogs/liquid-tutorial-shopify/product-bundles

ブログに戻る

感想・コメント

コメントを残す

コメントは公開前に承認される必要があることにご注意ください。