app/template/default/Product/list.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. {% block stylesheet %}
  10.     {#<link rel="stylesheet" href="{{ asset('assets/css/contents-list.css', 'user_data') }}">#}
  11.     {#<link rel="stylesheet" href="{{ asset('assets/css/main_module.css', 'user_data') }}">#}
  12.     <link rel="stylesheet" href="{{ asset('assets/css/page/_product_list.css', 'user_data') }}">
  13. <style>
  14.     .ec-topicpath{
  15.         display: flex;
  16.         margin: 30px 10% 0;
  17.     }
  18.     .ec-topicpath a{
  19.         color: #09860D;
  20.     }
  21.     .ec-topicpath__divider{
  22.         margin: 0 5px;
  23.     }
  24.     
  25.         .category-btn{
  26.             justify-content: center;
  27.         }
  28.         .category-btn img{
  29.             margin-right: 15px;
  30.         }
  31.         .tab {
  32.             margin: 30px 10% 0;
  33.         }
  34.         
  35.         @media screen and (max-width:768px){
  36.             .tab img{
  37.                 width: 15%;
  38.             }
  39.             .category-btn{
  40.                 margin-bottom: 15px;
  41.             }
  42.         }
  43.         .tab-list {
  44.             display: flex;
  45.             gap: 1px;
  46.         }
  47.         @media screen and (max-width:768px){
  48.             .tab-list{
  49.                 flex-direction: column;
  50.             }
  51.         }
  52.         .tab-item {
  53.             border-radius: 5px 5px 0 0;
  54.             background-color: #09860D;
  55.             border-top: solid 1px #09860D;
  56.             border-right: solid 1px #09860D;
  57.             border-left: solid 1px #09860D;
  58.         }
  59.         .tab-item a{
  60.             color: #FFF;
  61.             padding: 0.5em 1.2em;
  62.             display: block;
  63.         }
  64.         .tab-content:has(.tab-panel.active){
  65.             display: flex;
  66.             justify-content: space-between;
  67.             border-top:solid 1px #09860D;
  68.             border-right:solid 1px #09860D;
  69.             border-left:solid 1px #09860D;
  70.             border-radius: 0 8px 0 0;
  71.         }
  72.         
  73.         .tab-content:not(:has(.tab-panel.active)){
  74.             display: none;
  75.         }
  76.         
  77.         @media screen and (max-width:768px){
  78.             .tab-content{
  79.                 flex-direction: column;
  80.                 border-radius: 0;
  81.             }
  82.         }
  83.         .tab-panel {
  84.             display: none;
  85.         }
  86.         .tab-item.active {
  87.             background-color: #fff;
  88.             font-weight: bold;
  89.             position: relative;
  90.         }
  91.         
  92.         .tab-item.active a{
  93.             color: #09860D;
  94.         }
  95.         
  96.         .tab-item.active::before{
  97.             content: "";
  98.             width: 100%;
  99.             border-bottom: 1px solid  #FFF;
  100.             position: absolute;
  101.             bottom: -1px;
  102.             left: 0;
  103.         }
  104.         .tab-panel.active {
  105.             display: flex;
  106.             padding: 3%;
  107.             flex-wrap: wrap;
  108.             gap:20px 20px;
  109.             flex: 10;
  110.         }
  111.         @media screen and (max-width:768px){
  112.             .tab-panel.active{
  113.                 justify-content: center;
  114.                 
  115.             }
  116.         }
  117.         .tab-panel.active h2 {
  118.             font-size: 20px;
  119.             font-weight: bold;
  120.         }
  121.         
  122.         .search-form-box{
  123.             border: solid 1px #09860D;
  124.             border-radius: 0 0 8px 8px;
  125.         }
  126.         
  127.         .tab-content:not(:has(.active)) + .search-form-box{
  128.             border-radius: 0 8px 8px 8px;
  129.         }
  130.         .search-form-box .search-form-content{
  131.             display: flex;
  132.             justify-content: space-between;
  133.         }
  134.         @media screen and (max-width:768px){
  135.             .search-form-box .search-form-content{
  136.                 flex-direction: column;
  137.                 
  138.             }
  139.         }
  140.         .search-form-box .search-form-item{
  141.             padding: 3%;
  142.         }
  143.         @media screen and (max-width:768px){
  144.             .search-form-box .search-form-item{
  145.                 margin: 0 auto;
  146.                 
  147.             }
  148.         }
  149.         
  150.         .search-form-box .search-form-input{
  151.             -webkit-appearance: none;
  152.             -moz-appearance:none;
  153.             appearance: none;
  154.             border:1px solid #09860d;
  155.             border-radius:25px;
  156.             width: 30vw;
  157.             padding-left: 20px;
  158.             height: 36px;
  159.             background-color: #f5f5f5;
  160.         }
  161.         @media screen and (max-width:768px){
  162.             .search-form-box .search-form-input{
  163.                 width: 50vw;
  164.                 box-sizing: border-box;
  165.             }
  166.             .search-form-box .search-form-input::placeholder{
  167.                 font-size: 2vw;
  168.             }
  169.         }
  170.         
  171.         select{
  172.             background:none;
  173.             appearance: none;
  174.             font:inherit;
  175.             padding:9.5px 10px;
  176.             border:1px solid #09860d;
  177.             box-sizing: border-box;
  178.             width: 250px;
  179.             border-radius:2px;
  180.         }
  181.         
  182.         .select {
  183.           display: inline-block;
  184.           position: relative;
  185.           vertical-align: middle;
  186.         }
  187.         
  188.         .select::before {
  189.           position: absolute;
  190.           top: 18px;
  191.           right: 16px;
  192.           width: 0;
  193.           height: 0;
  194.           border-width: 10px 5px 0 5px;
  195.           border-style: solid;
  196.           border-color: #09860d transparent transparent transparent;
  197.           content: "";
  198.           pointer-events: none;
  199.         }
  200.     </style>
  201. {% endblock %}
  202. {% set body_class = 'product_page' %}
  203. {% block javascript %}
  204.     <script>
  205.         eccube.productsClassCategories = {
  206.             {% for Product in pagination %}
  207.             "{{ Product.id|escape('js') }}": {{ class_categories_as_json(Product)|raw }}{% if loop.last == false %}, {% endif %}
  208.             {% endfor %}
  209.         };
  210.         $(function() {
  211.             // 表示件数を変更
  212.             $('.disp-number').change(function() {
  213.                 var dispNumber = $(this).val();
  214.                 $('#disp_number').val(dispNumber);
  215.                 $('#pageno').val(1);
  216.                 $("#form1").submit();
  217.             });
  218.             // // 並び順を変更
  219.             $('.order-by').change(function() {
  220.                 var orderBy = $(this).val();
  221.                 $('#orderby').val(orderBy);
  222.                 $('#pageno').val(1);
  223.                 $("#form1").submit();
  224.             });
  225.             $('.add-cart').on('click', function(e) {
  226.                 var $form = $(this).parents('li').find('form');
  227.                 // 個数フォームのチェック
  228.                 var $quantity = $form.parent().find('.quantity');
  229.                 if ($quantity.val() < 1) {
  230.                     $quantity[0].setCustomValidity('{{ '1以上で入力してください。'|trans }}');
  231.                     setTimeout(function() {
  232.                         loadingOverlay('hide');
  233.                     }, 100);
  234.                     return true;
  235.                 } else {
  236.                     $quantity[0].setCustomValidity('');
  237.                 }
  238.                 e.preventDefault();
  239.                 $.ajax({
  240.                     url: $form.attr('action'),
  241.                     type: $form.attr('method'),
  242.                     data: $form.serialize(),
  243.                     dataType: 'json',
  244.                     beforeSend: function(xhr, settings) {
  245.                         // Buttonを無効にする
  246.                         $('.add-cart').prop('disabled', true);
  247.                     }
  248.                 }).done(function(data) {
  249.                     // レスポンス内のメッセージをalertで表示
  250.                     $.each(data.messages, function() {
  251.                         $('#ec-modal-header').html(this);
  252.                     });
  253.                     $('.ec-modal').show()
  254.                     // カートブロックを更新する
  255.                     $.ajax({
  256.                         url: '{{ url('block_cart') }}',
  257.                         type: 'GET',
  258.                         dataType: 'html'
  259.                     }).done(function(html) {
  260.                         $('.ec-headerRole__cart').html(html);
  261.                     });
  262.                 }).fail(function(data) {
  263.                     alert('{{ 'カートへの追加に失敗しました。'|trans }}');
  264.                 }).always(function(data) {
  265.                     // Buttonを有効にする
  266.                     $('.add-cart').prop('disabled', false);
  267.                 });
  268.             });
  269.         });
  270.         $('.ec-modal-wrap').on('click', function(e) {
  271.             // モーダル内の処理は外側にバブリングさせない
  272.             e.stopPropagation();
  273.         });
  274.         $('.ec-modal-overlay, .ec-modal, .ec-modal-close, .ec-inlineBtn--cancel').on('click', function() {
  275.             $('.ec-modal').hide()
  276.         });
  277.         
  278.     </script>
  279.     <script>
  280.     //  絞り込み検索は、絞り込み検索ボタンが押されたとき、もしくは入力欄にフォーカスされたときに、
  281.     //  enterを押したときのみ発動する。カテゴリ検索を押したときは発火させない
  282.         document.addEventListener('DOMContentLoaded', function () {
  283.             const form = document.querySelector('.tab');
  284.             const categoryBtn = form.querySelector('button[name="search_parent_id"]:not(.word-search)');
  285.             const wordBtn = form.querySelector('.category-btn.word-search');
  286.             const wordInput = form.querySelector('input.search-form-input');
  287.         
  288.             // カテゴリ検索 → name を送らせない
  289.             categoryBtn.addEventListener('click', function () {
  290.                 if (wordInput) {
  291.                     wordInput.disabled = true;
  292.                 }
  293.             });
  294.         
  295.             // 絞り込み検索 → name を有効化
  296.             wordBtn?.addEventListener('click', function () {
  297.                 if (wordInput) {
  298.                     wordInput.disabled = false;
  299.                 }
  300.             });
  301.         
  302.             // Enterキーで submit 時に絞り込み検索の値を送信するための hidden input 挿入
  303.             form.addEventListener('submit', function (e) {
  304.                 if (document.activeElement === wordInput) {
  305.                     e.preventDefault();
  306.         
  307.                     // 既に存在していたら除去(2重防止)
  308.                     const existingHidden = form.querySelector('input[name="search_parent_id"].auto-injected');
  309.                     if (existingHidden) {
  310.                         existingHidden.remove();
  311.                     }
  312.         
  313.                     // ボタンのvalue属性をそのまま使う
  314.                     const valueFromButton = wordBtn?.getAttribute('value') || '';
  315.         
  316.                     const hidden = document.createElement('input');
  317.                     hidden.type = 'hidden';
  318.                     hidden.name = 'search_parent_id';
  319.                     hidden.value = valueFromButton;
  320.                     hidden.classList.add('auto-injected');
  321.                     form.appendChild(hidden);
  322.                     if (wordInput) {
  323.                         wordInput.disabled = false;
  324.                     }
  325.                     form.submit();
  326.                 }
  327.             });
  328.         });
  329.         
  330.         // カテゴリの詳細検索に値が入っていない場合、buttonのnameの値をcategory_idに変更する
  331.         document.addEventListener('DOMContentLoaded', function () {
  332.             const form = document.querySelector('.tab');
  333.             const submitBtn = form.querySelector('button.category-btn');
  334.             const categorySelects = form.querySelectorAll('select[name="category_ids[]"]');
  335.         
  336.             form.addEventListener('submit', function (e) {
  337.                 // category_ids[] に選択されている値が1つもない場合、ボタンのnameを変更
  338.                 const hasValue = Array.from(categorySelects).some(select => select.value && select.value !== '');
  339.         
  340.                 if (!hasValue) {
  341.                     submitBtn.setAttribute('name', 'category_id');
  342.                 } else {
  343.                     submitBtn.setAttribute('name', 'search_parent_id');
  344.                 }
  345.             });
  346.         });
  347.     </script>
  348. {% endblock %}
  349. {% block main %}
  350.     {% set AllCategories = repository('Eccube\\Entity\\Category').getList() %}
  351.     {% set Categories = AllCategories|filter(c => c.id is not null and c.id == 1) %}
  352.     {% set selected_categories = app.request.query.get('category_ids', []) %}
  353.     {% set parent_category = app.request.query.get('search_parent_id') %}
  354.     {#パンくず#}
  355.         <div class="ec-searchnavRole__topicpath">
  356.             <ol class="ec-topicpath">
  357.                 <li class="ec-topicpath__item"><a href="{{ url('product_list') }}">{{ 'front.product.all_category'|trans }}</a>
  358.                 </li>
  359.                 {% if Category is not null %}
  360.                     {% for Path in Category.path %}
  361.                         {% if not loop.first %} 
  362.                             <li class="ec-topicpath__divider">></li>
  363.                             <li class="ec-topicpath__item{% if loop.last %}--active{% endif %}">
  364.                                 <a href="{{ url('product_list') }}?category_id={{ Path.id }}">{{ Path.name }}</a>
  365.                             </li>
  366.                         {% endif %}
  367.                     {% endfor %}
  368.                 {% endif %}
  369.                 {% if selected_categories %}
  370.                 {% set first_matched = true %}
  371.                     {% for Category in Categories %}
  372.                         {% for children in Category.Children %}
  373.                             {% if children.id == parent_category %}
  374.                                 <li class="ec-topicpath__divider">></li>
  375.                                 <li><a href="{{ url('product_list') }}?category_id={{ children.id }}">{{ children.name }}</a></li>
  376.                             {% endif %}
  377.                                 {% for grandChildren in children.Children %}
  378.                                     {% for grandgrandChild in grandChildren.Children %}
  379.                                         {% if grandgrandChild.id in selected_categories %}
  380.                                             {% if first_matched %}
  381.                                                 <li class="ec-topicpath__divider">></li>
  382.                                                 <li><a href="{{ url('product_list') }}?category_ids[]={{ grandgrandChild.id }}&search_parent_id={{parent_category}}">{{ grandgrandChild.name }}</a></li>
  383.                                                 {% set first_matched = false %}
  384.                                             {% else %}
  385.                                                 <li><a href="{{ url('product_list') }}?category_ids[]={{ grandgrandChild.id }}&search_parent_id={{parent_category}}">・{{ grandgrandChild.name }}</a></li>
  386.                                             {% endif %}
  387.                                         {% endif %}
  388.                                     {% endfor %}
  389.                                 {% endfor %}
  390.                             {% endfor %}
  391.                     {% endfor %}
  392.                 {% endif %}
  393.                 {% if search_form.vars.value.name %}
  394.                     <li class="ec-topicpath__divider">></li>
  395.                     <li class="ec-topicpath__item"><a href="{{ url('product_list') }}?name={{ search_form.vars.value.name }}">{{ search_form.vars.value.name }}</a></li>
  396.                 {% endif %}
  397.             </ol>
  398.         </div>
  399.     <!-- カテゴリ検索 -->
  400.     {% if app.request.query.get('category_id') or app.request.query.get('search_parent_id') %}
  401.         <form action="{{ url('product_list') }}" method="get" class="tab" >
  402.             <ul class="tab-list">
  403.                 {% for tab in Categories%}
  404.                     {% for children in tab.Children %}
  405.                         {% set children_ids = [] %}
  406.                         {% for grandChild in children.Children %}
  407.                             {% set children_ids = children_ids|merge([grandChild.id]) %}
  408.                         {% endfor %}
  409.                             {% if children.id == app.request.query.get('category_id')
  410.                             or children.id == app.request.query.get('search_parent_id')
  411.                             or app.request.query.get('category_id') in children_ids
  412.                             %}
  413.                                 <li class="tab-item active" tabindex="0" id="{{children.id}}">
  414.                                     <a href="{{ url('product_list', {'category_id': children.id}) }}">{{children.name}}</a>
  415.                                 </li>
  416.                             {% else %}
  417.                                 <li class="tab-item" tabindex="0" id="{{children.id}}">
  418.                                     <a href="{{ url('product_list', {'category_id': children.id}) }}">{{children.name}}</a>
  419.                                 </li>
  420.                             {% endif %}
  421.                     {% endfor %}
  422.                 {% endfor %}
  423.             </ul>
  424.             <div class="tab-content" id="category-form">
  425.                 {% for Category in Categories%}
  426.                     {% for children in Category.Children %}
  427.                         {% set children_ids = [] %}
  428.                         {% for grandChild in children.Children %}
  429.                             {% set children_ids = children_ids|merge([grandChild.id]) %}
  430.                         {% endfor %}
  431.                         {% if (children.id == app.request.query.get('category_id')
  432.                         or children.id == app.request.query.get('search_parent_id')
  433.                         or app.request.query.get('category_id') in children_ids)
  434.                         and children.Children is not empty
  435.                         %}
  436.                             <div class="tab-panel active">
  437.                                     {% for grandChild in children.Children%}
  438.                                     <div class=select>
  439.                                         <select name="category_ids[]">
  440.                                             <option value="">{{grandChild.name}}から選択</option>
  441.                                                 {% for grandgrandchild in grandChild.Children %}
  442.                                                     <option value="{{grandgrandchild.id}}"
  443.                                                         {% if grandgrandchild.id in selected_categories %}
  444.                                                             selected
  445.                                                         {% endif %}
  446.                                                     >{{grandgrandchild.name}}</option>
  447.                                                 {% endfor %}
  448.                                         </select>
  449.                                     </div>
  450.                                     {% endfor %}
  451.                             </div>
  452.                             <button type="submit" class="category-btn" name="search_parent_id"
  453.                             {% if app.request.query.get('category_id') or app.request.query.get('search_parent_id') %}
  454.                             value="{{ app.request.query.get('category_id') ?: app.request.query.get('search_parent_id') }}"
  455.                             >
  456.                             {% endif %}
  457.                                 <img src="{{ asset('/html/user_data/assets/icon/btn_ic_search.svg ', 'user_data') }}" alt="お気に入り">
  458.                                 カテゴリ検索
  459.                             </button>
  460.                         {% else %}
  461.                             <div class="tab-panel"></div>
  462.                         {% endif %}
  463.                     {% endfor %}
  464.                 {% endfor %}
  465.             </div>
  466.             <div class="search-form-box">
  467.                 <div class="search-form-content">
  468.                     <div class="search-form-item">
  469.                         <input type="text" name="name" class="search-form-input"
  470.                         placeholder="カテゴリから絞り込み"
  471.                         {% if search_form.vars.value.name
  472.                         and (app.request.query.get('search_parent_id') is not empty or app.request.query.get('category_id') is not empty) %}
  473.                         value="{{ search_form.vars.value.name }}"
  474.                         {% endif %}
  475.                         >
  476.                     </div>
  477.                     <button type="submit" class="category-btn word-search" name="search_parent_id"
  478.                         {% if app.request.query.get('category_id') or app.request.query.get('search_parent_id') %}
  479.                         value="{{ app.request.query.get('category_id') ?: app.request.query.get('search_parent_id') }}"
  480.                         >
  481.                         {% endif%}
  482.                         <img src="{{ asset('/html/user_data/assets/icon/btn_ic_search.svg ', 'user_data') }}" alt="お気に入り">
  483.                         絞り込み検索
  484.                     </button>
  485.                 </div>
  486.             </div>
  487.         </form>
  488.     {% endif %}
  489. <section class="contents-list">
  490.     <!-- 商品リスト -->
  491.     <div class="list-view">
  492.         <div class="list-title">
  493.                 {#カテゴリが存在しない(全体検索の場合)#}
  494.                 {% if search_form.vars.value.name
  495.                 and app.request.query.get('category_id') is empty
  496.                 and app.request.query.get('search_parent_id') is empty%}
  497.                     <h2 class="ec-topicpath__item">「{{ search_form.vars.value.name }}」で検索</h2>
  498.                 {% endif %}
  499.                 
  500.                 {#カテゴリが存在する場合(単純にカテゴリナビから言った場合など)#}
  501.                 {% if Category is not null %}
  502.                     <h2 class="ec-topicpath__item">{{ Category.name }}</h2>
  503.                     {% if search_form.vars.value.name %}
  504.                         <p class="ec-topicpath__item">> {{ search_form.vars.value.name }}</p>
  505.                     {% endif %}
  506.                 {% endif %}
  507.                 {#カテゴリの詳細検索を使った場合#}
  508.                 {% if selected_categories %}
  509.                 {% set first_matched = true %}
  510.                     {% for firstCategory in Categories %}
  511.                         {% for Category in firstCategory.Children %}
  512.                         {% if Category.id == parent_category %}
  513.                             <h2 class="ec-topicpath__item">{{ Category.name }}</h2>
  514.                         {% endif %}
  515.                             {% for Child in Category.Children %}
  516.                                 {% for GrandChildren in Child.Children %}
  517.                                     {% if GrandChildren.id in selected_categories %}
  518.                                         {% if first_matched %}
  519.                                             <p class="ec-topicpath__item">> {{ GrandChildren.name }}</p>
  520.                                             {% set first_matched = false %}
  521.                                         {% else %}
  522.                                             <p class="ec-topicpath__item">・ {{ GrandChildren.name }}</p>
  523.                                         {% endif %}
  524.                                     {% endif %}
  525.                                 {% endfor %}
  526.                             {% endfor %}
  527.                         {% endfor %}
  528.                     {% endfor %}
  529.                     {% if search_form.vars.value.name %}
  530.                         <p class="ec-topicpath__item">> {{ search_form.vars.value.name }}</p>
  531.                     {% endif %}
  532.                 {% endif %}
  533.                 {% if pagination.totalItemCount > 0 %}
  534.                     {{ '<p>(%count%冊)</p>'|trans({ '%count%': pagination.totalItemCount })|raw }}
  535.                 {% else %}
  536.                     <p>{{ 'お探しの商品は見つかりませんでした'|trans }}</p>
  537.                 {% endif %}
  538.         </div> 
  539.         <!-- リストエリア -->
  540.         {% if pagination.totalItemCount|length > 0 %}
  541.             <div>
  542.                 <div class="list-area">
  543.                 {% for Product in pagination %}
  544.                     <div class="card-1">
  545.                         <a href="{{ url('product_detail', {'id': Product.id}) }}">
  546.                             <img src="{{ asset(Product.main_list_image|no_image_product, 'save_image') }}" alt="{{ Product.name }}" {% if loop.index > 5 %} loading="lazy"{% endif %}>
  547.                             <h3>{{ Product.name }}</h3>
  548.                             <p class="price02-default">
  549.                                 {{ Product.getPrice02IncTaxMin|price }}<span class="tax">(税込)</span>
  550.                             </p>
  551.                         </a>
  552.                     </div>
  553.                 {% endfor %}
  554.                 </div>
  555.                 <!-- ページネーション -->
  556.                 {% set pages = pagination.paginationData %}
  557.                 {% if pages.pageCount > 1 %}
  558.                     <div class="ec-pager pagination">
  559.                         {# 前へ #}
  560.                         {% if pages.previous is defined %}
  561.                             <a class="page-return" href="{{ path(
  562.                             app.request.attributes.get('_route'),
  563.                             app.request.query.all|merge({'pageno': pages.previous})) }}">←</a>
  564.                         {% endif %}
  565.                         <div class="page-num">
  566.                             {% for page in pages.pagesInRange %}
  567.                                 {% if page == pages.current %}
  568.                                     <a class="active" href="{{ path(
  569.                                         app.request.attributes.get('_route'),
  570.                                         app.request.query.all|merge({'pageno': page})) }}"> {{ page }} </a>
  571.                                 {% else %}
  572.                                     <a href="{{ path(
  573.                                         app.request.attributes.get('_route'),
  574.                                         app.request.query.all|merge({'pageno': page})) }}"> {{ page }} </a>
  575.                                 {% endif %}
  576.                             {% endfor %}
  577.                         </div>
  578.                         {# 次へ #}
  579.                         {% if pages.next is defined %}
  580.                             <a class="page-advance" href="{{ path(
  581.                             app.request.attributes.get('_route'),
  582.                             app.request.query.all|merge({'pageno': pages.next})) }}">→</a>
  583.                         {% endif %}
  584.                     </div>
  585.                 {% endif %}
  586.             </div>
  587.         {% endif %}
  588.     </div>
  589.             
  590. </section>
  591. {% endblock %}