app/template/default/Product/detail.twig line 1

Open in your IDE?
  1. {#
  2. This file is part of EC-CUBE
  3. Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
  4. http://www.ec-cube.co.jp/
  5. For the full copyright and license information, please view the LICENSE
  6. file that was distributed with this source code.
  7. #}
  8. {% extends 'default_frame.twig' %}
  9. {% set body_class = 'product-single-page' %}
  10. {% block javascript %}
  11.     <script>
  12.         $(function() {
  13.             // bfcache無効化
  14.             $(window).bind('pageshow', function(event) {
  15.                 if (event.originalEvent.persisted) {
  16.                     location.reload(true);
  17.                 }
  18.             });
  19.             // Core Web Vital の Cumulative Layout Shift(CLS)対策のため
  20.             // img タグに width, height が付与されている.
  21.             // 630px 未満の画面サイズでは縦横比が壊れるための対策
  22.             // see https://github.com/EC-CUBE/ec-cube/pull/5023
  23.             $('.ec-grid2__cell').hide();
  24.             var removeSize = function () {
  25.                 $('.slide-item').height('');
  26.                 $('.slide-item img')
  27.                     .removeAttr('width')
  28.                     .removeAttr('height')
  29.                     .removeAttr('style');
  30.             };
  31.             var slickInitial = function(slick) {
  32.                 $('.ec-grid2__cell').fadeIn(1500);
  33.                 var baseHeight = $(slick.target).height();
  34.                 var baseWidth = $(slick.target).width();
  35.                 var rate = baseWidth / baseHeight;
  36.                 $('.slide-item').height(baseHeight * rate); // 余白を削除する
  37.                 // transform を使用することでCLSの影響を受けないようにする
  38.                 $('.slide-item img')
  39.                     .css(
  40.                         {
  41.                             'transform-origin': 'top left',
  42.                             'transform': 'scaleY(' + rate + ')',
  43.                             'transition': 'transform .1s'
  44.                         }
  45.                     );
  46.                 // 正しいサイズに近くなったら属性を解除する
  47.                 setTimeout(removeSize, 500);
  48.             };
  49.             $('.item_visual').on('init', slickInitial);
  50.             // リサイズ時は CLS の影響を受けないため属性を解除する
  51.             $(window).resize(removeSize);
  52.             $('.item_visual').slick({
  53.                 dots: false,
  54.                 arrows: false,
  55.                 responsive: [{
  56.                     breakpoint: 768,
  57.                     settings: {
  58.                         dots: true
  59.                     }
  60.                 }]
  61.             });
  62.             $('.slideThumb').on('click', function() {
  63.                 var index = $(this).attr('data-index');
  64.                 $('.item_visual').slick('slickGoTo', index, false);
  65.             })
  66.         });
  67.     </script>
  68.     <script>
  69.         $(function() {
  70.             $('.add-cart').on('click', function(event) {
  71.                 {% if form.classcategory_id1 is defined %}
  72.                 // 規格1フォームの必須チェック
  73.                 if ($('#classcategory_id1').val() == '__unselected' || $('#classcategory_id1').val() == '') {
  74.                     $('#classcategory_id1')[0].setCustomValidity('{{ 'front.product.product_class_unselected'|trans }}');
  75.                     return true;
  76.                 } else {
  77.                     $('#classcategory_id1')[0].setCustomValidity('');
  78.                 }
  79.                 {% endif %}
  80.                 {% if form.classcategory_id2 is defined %}
  81.                 // 規格2フォームの必須チェック
  82.                 if ($('#classcategory_id2').val() == '__unselected' || $('#classcategory_id2').val() == '') {
  83.                     $('#classcategory_id2')[0].setCustomValidity('{{ 'front.product.product_class_unselected'|trans }}');
  84.                     return true;
  85.                 } else {
  86.                     $('#classcategory_id2')[0].setCustomValidity('');
  87.                 }
  88.                 {% endif %}
  89.                 // 個数フォームのチェック
  90.                 if ($('#quantity').val() < 1) {
  91.                     $('#quantity')[0].setCustomValidity('{{ 'front.product.invalid_quantity'|trans }}');
  92.                     return true;
  93.                 } else {
  94.                     $('#quantity')[0].setCustomValidity('');
  95.                 }
  96.                 event.preventDefault();
  97.                 $form = $('#form1');
  98.                 $.ajax({
  99.                     url: $form.attr('action'),
  100.                     type: $form.attr('method'),
  101.                     data: $form.serialize(),
  102.                     dataType: 'json',
  103.                     beforeSend: function(xhr, settings) {
  104.                         // Buttonを無効にする
  105.                         $('.add-cart').prop('disabled', true);
  106.                     }
  107.                 }).done(function(data) {
  108.                     // レスポンス内のメッセージをalertで表示
  109.                     $.each(data.messages, function() {
  110.                         $('#ec-modal-header').text(this);
  111.                     });
  112.                     $('.ec-modal').show()
  113.                     // カートブロックを更新する
  114.                     $.ajax({
  115.                         url: "{{ url('block_cart') }}",
  116.                         type: 'GET',
  117.                         dataType: 'html'
  118.                     }).done(function(html) {
  119.                         $('.ec-headerRole__cart').html(html);
  120.                     });
  121.                 }).fail(function(data) {
  122.                     alert('{{ 'front.product.add_cart_error'|trans }}');
  123.                 }).always(function(data) {
  124.                     // Buttonを有効にする
  125.                     $('.add-cart').prop('disabled', false);
  126.                 });
  127.             });
  128.         });
  129.         $('.ec-modal-wrap').on('click', function(e) {
  130.             // モーダル内の処理は外側にバブリングさせない
  131.             e.stopPropagation();
  132.         });
  133.         $('.ec-modal-overlay, .ec-modal, .ec-modal-close, .ec-inlineBtn--cancel').on('click', function() {
  134.             $('.ec-modal').hide()
  135.         });
  136.         // form_restで出力されるquantityを削除
  137.         $(() => {
  138.           $('#quantity').remove();
  139.         })
  140.     </script>
  141.     <script type="application/ld+json">
  142.     {
  143.         "@context": "https://schema.org/",
  144.         "@type": "Product",
  145.         "name": "{{ Product.name }}",
  146.         "image": [
  147.             {% for img in Product.ProductImage %}
  148.                 "{{ app.request.schemeAndHttpHost }}{{ asset(img, 'save_image') }}"{% if not loop.last %},{% endif %}
  149.             {% else %}
  150.                 "{{ app.request.schemeAndHttpHost }}{{ asset(''|no_image_product, 'save_image') }}"
  151.             {% endfor %}
  152.         ],
  153.         "description": "{{ Product.description_list | default(Product.description_detail) | replace({'\n': '', '\r': ''}) | slice(0,300) }}",
  154.         {% if Product.code_min %}
  155.         "sku": "{{ Product.code_min }}",
  156.         {% endif %}
  157.         "offers": {
  158.             "@type": "Offer",
  159.             "url": "{{ url('product_detail', {'id': Product.id}) }}",
  160.             "priceCurrency": "{{ eccube_config.currency }}",
  161.             "price": {{ Product.getPrice02IncTaxMin ? Product.getPrice02IncTaxMin : 0}},
  162.             "availability": "{{ Product.stock_find ? "InStock" : "OutOfStock" }}"
  163.         }
  164.     }
  165.     </script>
  166. {% endblock %}
  167. {% block main %}
  168. <div class="product-single">
  169.   <h1 class="product-single__head inview">PRODUCT</h1>
  170.   <div class="product-single__main-row inview">
  171.     <div class="product-single__main-thumb img-slider">
  172.       <div class="arrow arrow-left"></div>
  173.       <div class="arrow arrow-right"></div>
  174.       <ul class="index__list">
  175.         {% for ProductImage in Product.ProductImage %}
  176.         <li class="index__item"></li>
  177.         {% endfor %}
  178.       </ul>
  179.       <div class="img-wrap">
  180.         {% for ProductImage in Product.ProductImage %}
  181.           <img src="{{ asset(ProductImage, 'save_image') }}" alt="{{ loop.first ? Product.name : '' }}" width="550" height="550"{% if loop.index > 1 %} loading="lazy"{% endif %}>
  182.         {% else %}
  183.           <img src="{{ asset(''|no_image_product, 'save_image') }}" alt="{{ loop.first ? Product.name : '' }}" width="550" height="550">
  184.         {% endfor %}
  185.       </div>
  186.     </div>
  187.     <div class="product-single__main-col">
  188.       <h2 class="product-single__main-title">{{ Product.name }}</h2>
  189.       <p class="product-single__main-price">
  190.         {% if Product.subscription_discount_rate is defined and Product.subscription_discount_rate > 0 %}
  191.           {% set discount_rate = Product.subscription_discount_rate %}
  192.           {% set original_price = Product.getPrice02IncTaxMin %}
  193.           {% set discounted_price = (original_price * (100 - discount_rate) / 100)|round(0, 'floor') %}
  194.           <span style="color: red; font-weight: bold; margin-right: 10px;">-{{ discount_rate }}%</span>
  195.           <span style="color: var(--color-black);">{{ discounted_price|price }}(税込)</span>
  196.           <br>
  197.           <span style="font-size: 0.9em; color: #999;">参考価格:
  198.           <span style="text-decoration: line-through; color: #999;">{{ original_price|price }}</span>
  199.           <br>
  200.           <span>※送料{{ fee|price }}円~</span>
  201.           </span>
  202.         {% else %}
  203.           {{ Product.getPrice02IncTaxMin|price }}(税込)<span>
  204.             <br>
  205.             ※送料{{ fee|price }}円~(沖縄離島除く国内)</span>
  206.         {% endif %}
  207.       </p>
  208.       {% if Product.subscription %}
  209.       <p class="product-single__main-terms">
  210.       <a href="/subscription_terms">定期購入ご利用規約</a>
  211.       </p>
  212.       {% endif %}
  213.       <div class="product-single__count">
  214.         <form action="{{ url('product_add_cart', {id:Product.id}) }}" method="post" id="form1" name="form1" class="product-single___count-form">
  215.           {% if Product.stock_find %}
  216.           <div class="product-single__count-wrap">
  217.             <label class="product-single__count-label">数量</label>
  218.             <div class="product-single__count-select">
  219.               <select id="customQuantity" name="quantity" required="required">
  220.                 {% for i in 1..(Product.getProductClasses()|length == 1 ? Product.getProductClasses()[0].saleLimit : 7) %}
  221.                   <option value="{{ i }}">{{ i }}</option>
  222.                 {% endfor %}
  223.               </select>
  224.               {{ form_errors(form.quantity) }}
  225.             </div>
  226.           </div>
  227.           <button class="product-single__count-btn btn -cart">
  228.           予約購入
  229.           </button>
  230.           <p style="color: red; padding: 5px 0; font-size: 12px;text-align: center;">※ こちらの商品は2月上旬にお届け予定です。</p>
  231.           {% else %}
  232.           <button class="product-single__count-btn btn" disabled="disabled">
  233.             在庫切れ
  234.           </button>
  235.           {% endif %}
  236.           <div style="display:none">
  237.             {{ form_rest(form) }}
  238.           </div>
  239.         </form>
  240.       </div>
  241.     </div>
  242.   </div>
  243.   <div class="product-single__content inview">
  244.     <div class="container product-single__container">
  245.       <section class="product-single__section">
  246.         <h3 class="product-single__section-title">製品概要</h3>
  247.         <div class="product-single__section-text">
  248.           <p>
  249.           {{ Product.description_list | default(Product.description_detail) }}
  250.           </p>
  251.         </div>
  252.       </section>
  253.       <section class="product-single__section">
  254.         <h3 class="product-single__section-title">原材料</h3>
  255.         <div class="product-single__section-text">
  256.           <p>米ぬか加工食品乳酸菌酵母含有米ぬか発酵物、デキストリン、ビタミンB12、ミルクシスル、HMPC(カプセル)</p>
  257.         </div>
  258.       </section>
  259.       <section class="product-single__section">
  260.         <h3 class="product-single__section-title">成分</h3>
  261.         <div class="product-single__section-text">
  262.           <p>1食分:2カプセル<br />
  263.           <table>
  264.             <tr>
  265.               <th></th>
  266.               <th>1食分の量</th>
  267.             </tr>
  268.             <tbody>
  269.               <tr>
  270.                 <td>エネルギー</td>
  271.                 <td>2.8kcal</td>
  272.               </tr>
  273.               <tr>
  274.                 <td>たんぱく質</td>
  275.                 <td>0.1g</td>
  276.               </tr>
  277.               <tr>
  278.                 <td>脂質</td>
  279.                 <td>0.02g</td>
  280.               </tr>
  281.               <tr>
  282.                 <td>炭水化物</td>
  283.                 <td>0.5g</td>
  284.               </tr>
  285.             </tbody>
  286.           </table>
  287.           <p>
  288.             <span>警告:18歳未満のお子様、妊娠中または授乳中の女性には適していません。<br />薬を服用されている方、持病のある方は、かかりつけ医や専門医にご相談されることをお勧めしま
  289.               す。</span
  290.             >
  291.           </p>
  292.         </div>
  293.       </section>
  294.       <section class="product-single__section">
  295.         <h3 class="product-single__section-title">配送について</h3>
  296.         <div class="product-single__section-text">
  297.           <p>
  298.           ご注文から6日以内にお届け致します。(土日祝日を除く)<br />
  299.           ただし2026年1月中においては商品切替時期のため、2月上旬以降に順次発送いたします。
  300.           </p>
  301.         </div>
  302.       </section>
  303.     </div>
  304.   </div>
  305. </div>
  306. {% endblock %}