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. {% set body_class = 'product_page' %}
  10. {% block stylesheet %}
  11.     <link rel="stylesheet" href="/html/plugins/ion-rangeslider/css/ion.rangeSlider.min.css">
  12.     <style>
  13.         /* ============================================
  14.            サイドバー付き 2カラムレイアウト
  15.         ============================================ */
  16.         .ec-productListLayout {
  17.             display: flex;
  18.             gap: 32px;
  19.             align-items: flex-start;
  20.         }
  21.         /* ============================================
  22.            サイドバー(フィルターパネル)
  23.         ============================================ */
  24.         .ec-filterSidebar {
  25.             position: -webkit-sticky;
  26.             position: sticky;
  27.             top: 80px;
  28.             align-self: flex-start;
  29.             max-height: calc(100vh - 100px);
  30.             overflow-y: auto;
  31.             margin: 20px;
  32.             width: 240px;
  33.             flex-shrink: 0;
  34.         }
  35.         @media (max-width: 767px) {
  36.             .ec-filterSidebar {
  37.                 position: static;
  38.                 max-height: none;
  39.                 overflow-y: visible;
  40.             }
  41.         }
  42.         /* Wide desktop: center the main column to align with site grid (1130 + 280 sidebar) */
  43.         @media (min-width: 1410px) {
  44.             .ec-searchnavRole {
  45.                 margin-left: calc((100% - 1130px - 280px) / 2);
  46.             }
  47.             .ec-shelfRole {
  48.                 margin-left: calc((100% - 1130px - 280px) / 2);
  49.             }
  50.             .ec-pagerRole {
  51.                 margin-left: calc((100% - 1130px - 280px) / 2);
  52.             }
  53.         }
  54.         .ec-filterSidebar__section {
  55.             background: #fff;
  56.             border: 1px solid #e8e8e8;
  57.             border-radius: 6px;
  58.             padding: 18px 16px;
  59.             margin-bottom: 16px;
  60.         }
  61.         .ec-filterSidebar__heading {
  62.             font-size: 13px;
  63.             font-weight: bold;
  64.             color: #222;
  65.             margin: 0 0 12px;
  66.             padding-bottom: 10px;
  67.             border-bottom: 2px solid #222;
  68.             letter-spacing: 0.05em;
  69.         }
  70.         /* カテゴリ: 見出しを上部に固定し、一覧を内部スクロールさせる */
  71.         .ec-filterSidebar__section--category .ec-filterSidebar__heading {
  72.             position: -webkit-sticky;
  73.             position: sticky;
  74.             top: 0;
  75.             background: #fff;
  76.             z-index: 2;
  77.         }
  78.         .ec-filterSidebar__section--category .ec-filterSidebar__body {
  79.             max-height: 60vh;
  80.             overflow-y: auto;
  81.         }
  82.         /* カテゴリリスト */
  83.         .ec-filterSidebar__categoryList,
  84.         .ec-filterSidebar__categoryList ul {
  85.             list-style: none;
  86.             padding: 0;
  87.             margin: 0;
  88.         }
  89.         .ec-filterSidebar__categoryList li a {
  90.             display: block;
  91.             padding: 6px 8px;
  92.             font-size: 13px;
  93.             color: #444;
  94.             text-decoration: none;
  95.             border-radius: 4px;
  96.             transition: background 0.15s, color 0.15s;
  97.         }
  98.         .ec-filterSidebar__categoryList li a:hover {
  99.             background: #f5f5f5;
  100.             color: #222;
  101.         }
  102.         .ec-filterSidebar__categoryList li.is-active > a {
  103.             font-weight: bold;
  104.             color: #c00;
  105.             background: #fff5f5;
  106.         }
  107.         /* サブカテゴリ */
  108.         .ec-filterSidebar__categoryList ul {
  109.             padding-left: 12px;
  110.             margin-top: 2px;
  111.         }
  112.         .ec-filterSidebar__categoryList ul li a {
  113.             font-size: 12px;
  114.             color: #666;
  115.             padding: 4px 8px;
  116.         }
  117.         /* メーカーリスト */
  118.         .ec-filterSidebar__makerList {
  119.             list-style: none;
  120.             padding: 0;
  121.             margin: 0;
  122.         }
  123.         .ec-filterSidebar__makerList li {
  124.             border-bottom: 1px solid #f0f0f0;
  125.         }
  126.         .ec-filterSidebar__makerList li:last-child {
  127.             border-bottom: none;
  128.         }
  129.         .ec-filterSidebar__makerList li a {
  130.             display: flex;
  131.             align-items: center;
  132.             gap: 6px;
  133.             padding: 7px 6px;
  134.             font-size: 13px;
  135.             color: #444;
  136.             text-decoration: none;
  137.             transition: color 0.15s;
  138.         }
  139.         .ec-filterSidebar__makerList li a:hover {
  140.             color: #c00;
  141.         }
  142.         .ec-filterSidebar__makerList li.is-active a {
  143.             font-weight: bold;
  144.             color: #c00;
  145.         }
  146.         .ec-filterSidebar__makerList li.is-active a::before {
  147.             content: "✓";
  148.             font-size: 11px;
  149.             color: #c00;
  150.         }
  151.         /* 価格帯フィルター */
  152.         .ec-filterSidebar__priceDisplay {
  153.             text-align: center;
  154.             font-size: 13px;
  155.             color: #333;
  156.             margin-bottom: 12px;
  157.             font-weight: bold;
  158.         }
  159.         .ec-filterSidebar__priceDisplay span {
  160.             color: #c00;
  161.         }
  162.         .ec-filterSidebar__priceSliderWrap {
  163.             /* Side padding holds room for ion-rangeSlider .irs-to / .irs-from labels
  164.                so the max-value tooltip never spills past the sidebar edge. */
  165.             padding: 0 14px;
  166.             margin-bottom: 16px;
  167.         }
  168.         .ec-filterSidebar__priceApply {
  169.             display: block;
  170.             width: 100%;
  171.             padding: 8px;
  172.             background: #222;
  173.             color: #fff;
  174.             border: none;
  175.             border-radius: 4px;
  176.             font-size: 13px;
  177.             cursor: pointer;
  178.             text-align: center;
  179.             transition: background 0.2s;
  180.         }
  181.         .ec-filterSidebar__priceApply:hover {
  182.             background: #444;
  183.         }
  184.         .ec-filterSidebar__pricePreset {
  185.             list-style: none;
  186.             padding: 0;
  187.             margin: 10px 0 0;
  188.         }
  189.         .ec-filterSidebar__pricePreset li a {
  190.             display: block;
  191.             padding: 5px 6px;
  192.             font-size: 12px;
  193.             color: #666;
  194.             text-decoration: none;
  195.             border-radius: 4px;
  196.             transition: background 0.15s;
  197.         }
  198.         .ec-filterSidebar__pricePreset li a:hover {
  199.             background: #f5f5f5;
  200.             color: #333;
  201.         }
  202.         /* 絞り込みリセット */
  203.         .ec-filterSidebar__reset a {
  204.             display: block;
  205.             text-align: center;
  206.             padding: 8px;
  207.             font-size: 12px;
  208.             color: #888;
  209.             text-decoration: underline;
  210.             border: 1px solid #ddd;
  211.             border-radius: 4px;
  212.             transition: color 0.15s, border-color 0.15s;
  213.         }
  214.         .ec-filterSidebar__reset a:hover {
  215.             color: #c00;
  216.             border-color: #c00;
  217.         }
  218.         /* 現在の絞り込み条件バッジ */
  219.         .ec-filterActive {
  220.             display: flex;
  221.             flex-wrap: wrap;
  222.             gap: 6px;
  223.             margin-bottom: 12px;
  224.             padding: 10px 12px;
  225.             background: #f9f9f9;
  226.             border: 1px solid #e8e8e8;
  227.             border-radius: 6px;
  228.         }
  229.         .ec-filterActive__label {
  230.             font-size: 11px;
  231.             color: #888;
  232.             align-self: center;
  233.             margin-right: 4px;
  234.         }
  235.         .ec-filterActive__tag {
  236.             display: inline-flex;
  237.             align-items: center;
  238.             gap: 4px;
  239.             padding: 3px 8px;
  240.             background: #222;
  241.             color: #fff;
  242.             border-radius: 20px;
  243.             font-size: 11px;
  244.         }
  245.         .ec-filterActive__tag a {
  246.             color: #aaa;
  247.             text-decoration: none;
  248.             font-size: 12px;
  249.             line-height: 1;
  250.         }
  251.         .ec-filterActive__tag a:hover {
  252.             color: #fff;
  253.         }
  254.         /* メインコンテンツ側 */
  255.         .ec-productListMain {
  256.             flex: 1;
  257.             min-width: 0;
  258.             width: 100%;  
  259.         }
  260.         /* ion-rangeslider カスタマイズ */
  261.         .irs--flat .irs-bar {
  262.             background-color: #222;
  263.         }
  264.         .irs--flat .irs-handle > i:first-child {
  265.             background-color: #222;
  266.         }
  267.         .irs--flat .irs-from,
  268.         .irs--flat .irs-to,
  269.         .irs--flat .irs-single {
  270.             background-color: #222;
  271.             font-size: 11px;
  272.         }
  273.         /* Tablet (768-1199px): stack sidebar above grid to avoid overlap */
  274.         @media (min-width: 768px) and (max-width: 1199px) {
  275.             .ec-productListLayout {
  276.                 flex-direction: column;
  277.                 gap: 0;
  278.             }
  279.             .ec-filterSidebar {
  280.                 width: 100%;
  281.                 margin: 0 0 16px;
  282.             }
  283.             .ec-productListMain {
  284.                 width: 100%;
  285.             }
  286.             /* Allow CTA to wrap and avoid truncation in 2-col tablet grid */
  287.             .ec-shelfGrid .ec-blockBtn--action {
  288.                 white-space: normal;
  289.                 line-height: 1.3;
  290.                 height: auto;
  291.                 min-height: 56px;
  292.                 padding: 12px 8px;
  293.                 font-size: 0.8rem;
  294.             }
  295.         }
  296.         /* Price slider tooltip: prevent label overlap when results are empty */
  297.         .ec-productListMain .ec-shelfRole:empty + .ec-pagerRole,
  298.         .ec-productListMain:has(.ec-shelfGrid:empty) .ec-filterSidebar__priceSliderWrap .irs-from,
  299.         .ec-productListMain:has(.ec-shelfGrid:empty) .ec-filterSidebar__priceSliderWrap .irs-to,
  300.         .ec-productListMain:has(.ec-shelfGrid:empty) .ec-filterSidebar__priceSliderWrap .irs-single {
  301.             visibility: hidden;
  302.         }
  303.         .ec-filterSidebar__priceSliderWrap .irs {
  304.             position: relative;
  305.             z-index: 1;
  306.         }
  307.         .ec-filterSidebar__priceSliderWrap .irs-from,
  308.         .ec-filterSidebar__priceSliderWrap .irs-to,
  309.         .ec-filterSidebar__priceSliderWrap .irs-single {
  310.             white-space: nowrap;
  311.         }
  312.         /* モバイル対応 */
  313.         @media (max-width: 767px) {
  314.             /* 横スクロール禁止 */
  315.             body, html {
  316.                 overflow-x: hidden;
  317.                 max-width: 100vw;
  318.             }
  319.             .ec-productListLayout {
  320.                 flex-direction: column;
  321.                 gap: 0;
  322.             }
  323.             .ec-filterSidebar {
  324.                 width: 100%;
  325.                 margin: 0;
  326.             }
  327.             /* スマホで商品一覧を確実に表示 */
  328.             .ec-productListMain {
  329.                 width: 100%;
  330.                 flex: none;
  331.                 margin: 0;
  332.             }
  333.             .ec-filterSidebar__section {
  334.                 margin-bottom: 10px;
  335.             }
  336.             /* Allow CTA to wrap and avoid truncation in narrow mobile cards */
  337.             .ec-shelfGrid .ec-blockBtn--action {
  338.                 white-space: normal;
  339.                 word-break: keep-all;
  340.                 overflow-wrap: anywhere;
  341.                 line-height: 1.3;
  342.                 height: auto;
  343.                 min-height: 56px;
  344.                 padding: 12px 8px;
  345.                 font-size: 0.8rem;
  346.             }
  347.             /* モバイルではアコーディオン形式 */
  348.             .ec-filterSidebar__body {
  349.                 display: none;
  350.             }
  351.             .ec-filterSidebar__body.is-open {
  352.                 display: block;
  353.             }
  354.             .ec-filterSidebar__heading {
  355.                 cursor: pointer;
  356.                 display: flex;
  357.                 justify-content: space-between;
  358.                 align-items: center;
  359.                 margin-bottom: 0;
  360.                 padding-bottom: 0;
  361.                 border-bottom: none;
  362.             }
  363.             .ec-filterSidebar__heading.is-open {
  364.                 margin-bottom: 12px;
  365.                 padding-bottom: 10px;
  366.                 border-bottom: 2px solid #222;
  367.             }
  368.             .ec-filterSidebar__heading::after {
  369.                 content: "▼";
  370.                 font-size: 10px;
  371.                 color: #888;
  372.                 transition: transform 0.2s;
  373.             }
  374.             .ec-filterSidebar__heading.is-open::after {
  375.                 transform: rotate(180deg);
  376.             }
  377.         }
  378.     </style>
  379. {% endblock %}
  380. {% block javascript %}
  381.     <script>
  382.         eccube.productsClassCategories = {
  383.             {% for Product in pagination %}
  384.             "{{ Product.id|escape('js') }}": {{ class_categories_as_json(Product)|raw }}{% if loop.last == false %}, {% endif %}
  385.             {% endfor %}
  386.         };
  387.         $(function() {
  388.             // 表示件数を変更
  389.             $('.disp-number').change(function() {
  390.                 var dispNumber = $(this).val();
  391.                 $('#disp_number').val(dispNumber);
  392.                 $('#pageno').val(1);
  393.                 $("#form1").submit();
  394.             });
  395.             // 並び順を変更
  396.             $('.order-by').change(function() {
  397.                 var orderBy = $(this).val();
  398.                 $('#orderby').val(orderBy);
  399.                 $('#pageno').val(1);
  400.                 $("#form1").submit();
  401.             });
  402.             // ==============================
  403.             // 価格帯スライダー(ion-rangeslider)
  404.             // ==============================
  405.             var priceMin = parseInt('{{ app.request.query.get("price_min") ?: 0 }}') || 0;
  406.             var priceMax = parseInt('{{ app.request.query.get("price_max") ?: 5000000 }}') || 5000000;
  407.             $("#price-range-slider").ionRangeSlider({
  408.                 type: "double",
  409.                 min: 0,
  410.                 max: 5000000,
  411.                 from: priceMin,
  412.                 to: priceMax,
  413.                 step: 10000,
  414.                 prettify_enabled: true,
  415.                 prettify_separator: ",",
  416.                 postfix: "円",
  417.                 onFinish: function(data) {
  418.                     $('#price_min_val').val(data.from);
  419.                     $('#price_max_val').val(data.to);
  420.                     // 表示更新
  421.                     $('#price-display-min').text(Number(data.from).toLocaleString());
  422.                     $('#price-display-max').text(Number(data.to).toLocaleString());
  423.                 }
  424.             });
  425.             // 価格フォーム送信
  426.             $('#price-filter-apply').on('click', function() {
  427.                 var min = $('#price_min_val').val();
  428.                 var max = $('#price_max_val').val();
  429.                 var url = new URL(window.location.href);
  430.                 url.searchParams.set('price_min', min);
  431.                 url.searchParams.set('price_max', max);
  432.                 url.searchParams.delete('pageno');
  433.                 window.location.href = url.toString();
  434.             });
  435.             // モバイル:アコーディオン(初期状態を閉じる)
  436.             function initMobileAccordion() {
  437.                 if ($(window).width() <= 767) {
  438.                     $('.ec-filterSidebar__heading').removeClass('is-open');
  439.                     $('.ec-filterSidebar__body').removeClass('is-open');
  440.                 }
  441.             }
  442.             initMobileAccordion();
  443.             $(window).on('resize', initMobileAccordion);
  444.             $('.ec-filterSidebar__heading').on('click', function() {
  445.                 if ($(window).width() <= 767) {
  446.                     $(this).toggleClass('is-open');
  447.                     $(this).next('.ec-filterSidebar__body').toggleClass('is-open');
  448.                 }
  449.             });
  450.         });
  451.         $('.ec-modal-wrap').on('click', function(e) {
  452.             e.stopPropagation();
  453.         });
  454.         $('.ec-modal-overlay, .ec-modal, .ec-modal-close, .ec-inlineBtn--cancel').on('click', function() {
  455.             $('.ec-modal').hide()
  456.         });
  457.         // 価格プリセットのクイック適用
  458.         function applyPrice(min, max) {
  459.             var url = new URL(window.location.href);
  460.             url.searchParams.set('price_min', min);
  461.             url.searchParams.set('price_max', max);
  462.             url.searchParams.delete('pageno');
  463.             window.location.href = url.toString();
  464.         }
  465.     </script>
  466.     <script src="/html/plugins/ion-rangeslider/js/ion.rangeSlider.min.js"></script>
  467. {% endblock %}
  468. {% block main %}
  469.         {% set hasFilter = app.request.query.get('category_id') or app.request.query.get('maker_id') or app.request.query.get('price_min') or app.request.query.get('price_max') %}
  470.         {# ==============================
  471.            2カラムレイアウト本体
  472.         ============================== #}
  473.         <div class="ec-productListLayout">
  474.             {# ===== サイドバー(左カラム)===== #}
  475.             <aside class="ec-filterSidebar">
  476.                 {# ----- 価格帯 ----- #}
  477.                 <div class="ec-filterSidebar__section">
  478.                     <h3 class="ec-filterSidebar__heading">価格帯</h3>
  479.                     <div class="ec-filterSidebar__body is-open">
  480.                         <div class="ec-filterSidebar__priceDisplay">
  481.                             <span id="price-display-min">{{ app.request.query.get('price_min') ? app.request.query.get('price_min')|number_format : '0' }}</span>円
  482.                             〜
  483.                             <span id="price-display-max">{{ app.request.query.get('price_max') ? app.request.query.get('price_max')|number_format : '5,000,000' }}</span>円
  484.                         </div>
  485.                         <div class="ec-filterSidebar__priceSliderWrap">
  486.                             <input type="text" id="price-range-slider" style="display:none;">
  487.                             <input type="hidden" id="price_min_val" value="{{ app.request.query.get('price_min') ?: 0 }}">
  488.                             <input type="hidden" id="price_max_val" value="{{ app.request.query.get('price_max') ?: 5000000 }}">
  489.                         </div>
  490.                         <button id="price-filter-apply" class="ec-filterSidebar__priceApply">この価格帯で絞り込む</button>
  491.                         <ul class="ec-filterSidebar__pricePreset">
  492.                             <li><a href="javascript:void(0);" onclick="applyPrice(0, 300000)">〜30万円</a></li>
  493.                             <li><a href="javascript:void(0);" onclick="applyPrice(300000, 600000)">30万円〜60万円</a></li>
  494.                             <li><a href="javascript:void(0);" onclick="applyPrice(600000, 1000000)">60万円〜100万円</a></li>
  495.                             <li><a href="javascript:void(0);" onclick="applyPrice(1000000, 5000000)">100万円〜</a></li>
  496.                         </ul>
  497.                     </div>
  498.                 </div>
  499.                 {# ----- メーカー(ブランド)----- #}
  500.                 <div class="ec-filterSidebar__section">
  501.                     <h3 class="ec-filterSidebar__heading">ブランド</h3>
  502.                     <div class="ec-filterSidebar__body is-open">
  503.                         <ul class="ec-filterSidebar__makerList">
  504.                             {% if Makers is defined %}
  505.                                 {% for MakerItem in Makers %}
  506.                                     <li class="{{ Maker is not null and Maker.id == MakerItem.id ? 'is-active' : '' }}">
  507.                                         <a href="{{ url('product_list') }}?maker_id={{ MakerItem.id }}{% if app.request.query.get('category_id') %}&category_id={{ app.request.query.get('category_id') }}{% endif %}">
  508.                                             {{ MakerItem.name }}
  509.                                         </a>
  510.                                     </li>
  511.                                 {% endfor %}
  512.                             {% else %}
  513.                                 {# Makersがコントローラから渡されない場合は静的リストで対応 #}
  514.                                 {% set makerList = [
  515.                                     {id: 1,  name: 'LIXIL'},
  516.                                     {id: 2,  name: 'YKK AP'},
  517.                                     {id: 3,  name: '三協アルミ'},
  518.                                     {id: 4,  name: '四国化成'},
  519.                                     {id: 5,  name: 'タカショー'},
  520.                                     {id: 6,  name: 'ユニソン'},
  521.                                     {id: 7,  name: 'エスビック'},
  522.                                     {id: 8,  name: 'パナソニック'},
  523.                                     {id: 9,  name: 'イナバ'},
  524.                                     {id: 10, name: 'ヨドコウ'},
  525.                                     {id: 13, name: 'タクボ'}
  526.                                 ] %}
  527.                                 {% for maker in makerList %}
  528.                                     <li class="{{ Maker is not null and Maker.id == maker.id ? 'is-active' : '' }}">
  529.                                         <a href="{{ url('product_list') }}?maker_id={{ maker.id }}{% if app.request.query.get('category_id') %}&category_id={{ app.request.query.get('category_id') }}{% endif %}">
  530.                                             {{ maker.name }}
  531.                                         </a>
  532.                                     </li>
  533.                                 {% endfor %}
  534.                             {% endif %}
  535.                         </ul>
  536.                     </div>
  537.                 </div>
  538.                 {# ----- カテゴリ ----- #}
  539.                 <div class="ec-filterSidebar__section ec-filterSidebar__section--category">
  540.                     <h3 class="ec-filterSidebar__heading">カテゴリ</h3>
  541.                     <div class="ec-filterSidebar__body is-open">
  542.                         {% set categoryList = [
  543.                             {id: 7,  name: 'カーポート・車庫',         children: []},
  544.                             {id: 16, name: 'ガレージ・倉庫',           children: []},
  545.                             {id: 8,  name: 'サイクルポート・駐輪場',   children: []},
  546.                             {id: 9,  name: 'ゲート',                   children: [
  547.                                 {id: 30, name: '跳ね上げ式ゲート'},
  548.                                 {id: 29, name: '伸縮ゲート'},
  549.                                 {id: 31, name: 'ガレージシャッター'}
  550.                             ]},
  551.                             {id: 12, name: 'テラス',                   children: [
  552.                                 {id: 33, name: 'テラス囲い'},
  553.                                 {id: 32, name: 'テラス屋根'}
  554.                             ]},
  555.                             {id: 19, name: 'ベランダ・バルコニー',     children: []},
  556.                             {id: 35, name: 'バルコニー屋根',           children: []},
  557.                             {id: 18, name: 'オーニング・日よけ',       children: []},
  558.                             {id: 11, name: 'ウッドデッキ',             children: [
  559.                                 {id: 37, name: 'タイルデッキ'},
  560.                                 {id: 36, name: 'ウッドデッキ'}
  561.                             ]},
  562.                             {id: 14, name: 'フェンス・柵',             children: []},
  563.                             {id: 25, name: '門扉',                     children: []},
  564.                             {id: 13, name: 'ポスト・門柱宅配ボックス', children: []},
  565.                             {id: 15, name: '物置・収納・屋外倉庫',     children: []},
  566.                             {id: 20, name: 'ガーデンファニチャー',     children: []},
  567.                             {id: 22, name: '人工芝',                   children: []},
  568.                             {id: 17, name: '内窓・二重窓',             children: []},
  569.                             {id: 26, name: 'その他',                   children: [
  570.                                 {id: 38, name: 'パーゴラ'},
  571.                                 {id: 39, name: '立水栓・ガーデンシンク'},
  572.                                 {id: 40, name: '手すり'},
  573.                                 {id: 10, name: 'ストックヤード'},
  574.                                 {id: 27, name: 'ゴミステーション'},
  575.                                 {id: 42, name: '面格子・窓格子'},
  576.                                 {id: 41, name: '窓シャッター'},
  577.                                 {id: 43, name: '玄関ドア'}
  578.                             ]}
  579.                         ] %}
  580.                         <ul class="ec-filterSidebar__categoryList">
  581.                             <li class="{{ app.request.query.get('category_id') is empty and Maker is null ? 'is-active' : '' }}">
  582.                                 <a href="{{ url('product_list') }}">すべて</a>
  583.                             </li>
  584.                             {% for cat in categoryList %}
  585.                                 <li class="{{ app.request.query.get('category_id') == cat.id ? 'is-active' : '' }}">
  586.                                     <a href="{{ url('product_list') }}?category_id={{ cat.id }}{% if app.request.query.get('maker_id') %}&maker_id={{ app.request.query.get('maker_id') }}{% endif %}">
  587.                                         {{ cat.name }}
  588.                                     </a>
  589.                                     {% if cat.children|length > 0 %}
  590.                                         <ul>
  591.                                             {% for child in cat.children %}
  592.                                                 <li class="{{ app.request.query.get('category_id') == child.id ? 'is-active' : '' }}">
  593.                                                     <a href="{{ url('product_list') }}?category_id={{ child.id }}{% if app.request.query.get('maker_id') %}&maker_id={{ app.request.query.get('maker_id') }}{% endif %}">
  594.                                                         {{ child.name }}
  595.                                                     </a>
  596.                                                 </li>
  597.                                             {% endfor %}
  598.                                         </ul>
  599.                                     {% endif %}
  600.                                 </li>
  601.                             {% endfor %}
  602.                         </ul>
  603.                     </div>
  604.                 </div>
  605.                 {# ----- リセット ----- #}
  606.                 {% if hasFilter %}
  607.                     <div class="ec-filterSidebar__reset">
  608.                         <a href="{{ url('product_list') }}">絞り込みをすべてリセット</a>
  609.                     </div>
  610.                 {% endif %}
  611.             </aside>
  612.             {# / サイドバー #}
  613.             {# ===== 商品一覧(右カラム)===== #}
  614.             <div class="ec-productListMain">
  615.     {% if search_form.category_id.vars.errors|length > 0 %}
  616.         <div class="ec-searchnavRole">
  617.             <p class="errormsg text-danger">{{ 'ご指定のカテゴリは存在しません'|trans }}</p>
  618.         </div>
  619.     {% else %}
  620.         {# 検索フォーム(hidden) #}
  621.         <div class="ec-searchnavRole">
  622.             <form name="form1" id="form1" method="get" action="?">
  623.                 {% for item in search_form %}
  624.                     <input type="hidden" id="{{ item.vars.id }}"
  625.                            name="{{ item.vars.full_name }}"
  626.                            {% if item.vars.value is not empty %}value="{{ item.vars.value }}" {% endif %}/>
  627.                 {% endfor %}
  628.             </form>
  629.             {# パンくずリスト #}
  630.             <div class="ec-searchnavRole__topicpath">
  631.                 <ol class="ec-topicpath">
  632.                     <li class="ec-topicpath__item"><a href="{{ url('product_list') }}">{{ '全て'|trans }}</a></li>
  633.                     {% if Category is not null %}
  634.                         {% for Path in Category.path %}
  635.                             <li class="ec-topicpath__divider">|</li>
  636.                             <li class="ec-topicpath__item{% if loop.last %}--active{% endif %}">
  637.                                 <a href="{{ url('product_list') }}?category_id={{ Path.id }}">{{ Path.name }}</a>
  638.                             </li>
  639.                         {% endfor %}
  640.                     {% endif %}
  641.                     {% if Maker is not null %}
  642.                         <li class="ec-topicpath__divider">|</li>
  643.                         <li class="ec-topicpath__item--active">
  644.                             <a href="{{ url('product_list') }}?maker_id={{ Maker.id }}">{{ Maker.name }}</a>
  645.                         </li>
  646.                     {% endif %}
  647.                     {% if search_form.vars.value and search_form.vars.value.name %}
  648.                         <li class="ec-topicpath__divider">|</li>
  649.                         <li class="ec-topicpath__item">{{ '「%name%」の検索結果'|trans({ '%name%': search_form.vars.value.name }) }}</li>
  650.                     {% endif %}
  651.                 </ol>
  652.             </div>
  653.         {# ==============================
  654.            現在の絞り込み条件バッジ
  655.         ============================== #}
  656.         {% if hasFilter %}
  657.             <div class="ec-filterActive">
  658.                 <span class="ec-filterActive__label">絞り込み中:</span>
  659.                 {% if Category is not null %}
  660.                     {% set removeCategoryParam = 'category_id=' ~ app.request.query.get('category_id') %}
  661.                     <span class="ec-filterActive__tag">
  662.                         {{ Category.name }}
  663.                         <a href="{{ app.request.uri|replace({(removeCategoryParam): ''}) }}" title="解除">✕</a>
  664.                     </span>
  665.                 {% endif %}
  666.                 {% if Maker is not null %}
  667.                     <span class="ec-filterActive__tag">
  668.                         {{ Maker.name }}
  669.                         <a href="{{ url('product_list') }}{% if app.request.query.get('category_id') %}?category_id={{ app.request.query.get('category_id') }}{% endif %}" title="解除">✕</a>
  670.                     </span>
  671.                 {% endif %}
  672.                 {% if app.request.query.get('price_min') or app.request.query.get('price_max') %}
  673.                     <span class="ec-filterActive__tag">
  674.                         {% if app.request.query.get('price_min') %}{{ app.request.query.get('price_min')|number_format }}円{% else %}0円{% endif %}
  675.                         〜
  676.                         {% if app.request.query.get('price_max') %}{{ app.request.query.get('price_max')|number_format }}円{% else %}上限なし{% endif %}
  677.                         <a href="javascript:void(0);" onclick="
  678.                             var url = new URL(window.location.href);
  679.                             url.searchParams.delete('price_min');
  680.                             url.searchParams.delete('price_max');
  681.                             window.location.href = url.toString();
  682.                         " title="解除">✕</a>
  683.                     </span>
  684.                 {% endif %}
  685.             </div>
  686.         {% endif %}
  687.             {# 件数・並び順 #}
  688.             <div class="ec-searchnavRole__infos">
  689.                 <div class="ec-searchnavRole__counter">
  690.                     {% if pagination.totalItemCount > 0 %}
  691.                         {{ '<span class="ec-font-bold">%count%件</span><span>の商品が見つかりました</span>'|trans({ '%count%': pagination.totalItemCount })|raw }}
  692.                     {% else %}
  693.                         <span>{{ 'お探しの商品は見つかりませんでした'|trans }}</span>
  694.                     {% endif %}
  695.                 </div>
  696.                 {% if pagination.totalItemCount > 0 %}
  697.                     <div class="ec-searchnavRole__actions">
  698.                         <div class="ec-select">
  699.                             {{ form_widget(search_form.disp_number, {'id': '', 'attr': {'class': 'disp-number'}}) }}
  700.                             {{ form_widget(search_form.orderby, {'id': '', 'attr': {'class': 'order-by'}}) }}
  701.                         </div>
  702.                     </div>
  703.                 {% endif %}
  704.             </div>
  705.         </div>
  706.                 {% if pagination.totalItemCount > 0 %}
  707.                     <div class="ec-shelfRole">
  708.                         <ul class="ec-shelfGrid">
  709.                             {% for Product in pagination %}
  710.                                 <li class="ec-shelfGrid__item" style="position:relative;">
  711.                                     {% if BaseInfo.option_favorite_product %}
  712.                                         <div style="position:absolute;top:8px;right:20px;z-index:10;">
  713.                                             <form action="{{ url('product_add_favorite', {id:Product.id}) }}" method="post">
  714.                                                 <button type="submit" id="favorite" class="favorite">&#9825;</button>
  715.                                             </form>
  716.                                         </div>
  717.                                     {% endif %}
  718.                                     <a href="{{ url('product_detail', {'id': Product.id}) }}">
  719.                                         <p class="ec-shelfGrid__item-image">
  720.                                             <img src="{{ asset(Product.main_list_image|no_image_product, 'save_image') }}" alt="{{ Product.name }}" width="180" height="180"{% if loop.index > 5 %} loading="lazy"{% endif %}>
  721.                                         </p>
  722.                                         {% if Product.Maker and Product.Maker.name %}
  723.                                             <p class="ec-shelfGrid__item-maker" style="font-size:12px;color:#666;margin:2px 0 4px;">{{ Product.Maker.name }}</p>
  724.                                         {% endif %}
  725.                                         <h5><strong>{{ getProduct_field(Product.id,"related_keyword") }}</strong></h5>
  726.                                         <p class="price02-default">
  727.                                             {% if Product.getPrice02Max is null or Product.getPrice02Max == 0 %}
  728.                                                 現地調査見積
  729.                                             {% elseif Product.hasProductClass %}
  730.                                                 {% if Product.getPrice02Min == Product.getPrice02Max %}
  731.                                                     {{ Product.getPrice02IncTaxMin|number_format }}円
  732.                                                 {% else %}
  733.                                                     {{ Product.getPrice02IncTaxMin|number_format }}円  ~ {{ Product.getPrice02IncTaxMax|number_format }}円
  734.                                                 {% endif %}
  735.                                             {% else %}
  736.                                                 {{ Product.getPrice02IncTaxMin|number_format }}円 ~
  737.                                             {% endif %}
  738.                                         </p>
  739.                                     </a>
  740.                                     {% if Product.stock_find %}
  741.                                         {% set form = forms[Product.id] %}
  742.                                         <form name="form{{ Product.id }}" id="productForm{{ Product.id }}" action="{{ url('product_detail', {id:Product.id}) }}" method="get">
  743.                                             <div class="ec-productRole__actions">
  744.                                                 {% if form.classcategory_id1 is defined %}
  745.                                                     <div class="ec-select">
  746.                                                         {{ form_widget(form.classcategory_id1) }}
  747.                                                         {{ form_errors(form.classcategory_id1) }}
  748.                                                     </div>
  749.                                                     {% if form.classcategory_id2 is defined %}
  750.                                                         <div class="ec-select">
  751.                                                             {{ form_widget(form.classcategory_id2) }}
  752.                                                             {{ form_errors(form.classcategory_id2) }}
  753.                                                         </div>
  754.                                                     {% endif %}
  755.                                                 {% endif %}
  756.                                                 <div class="ec-numberInput" style="display:none;"><span>{{ '数量'|trans }}</span>
  757.                                                     {{ form_errors(form.quantity) }}
  758.                                                 </div>
  759.                                             </div>
  760.                                             <div class="ec-productRole__btn">
  761.                                                 <button class="ec-blockBtn--action">
  762.                                                     {{ 'お見積もりはこちら'|trans }}
  763.                                                 </button>
  764.                                             </div>
  765.                                         </form>
  766.                                     {% else %}
  767.                                         <div class="ec-productRole__btn">
  768.                                             <button type="button" class="ec-blockBtn--action" disabled="disabled">
  769.                                                 {{ 'ただいま品切れ中です。'|trans }}
  770.                                             </button>
  771.                                         </div>
  772.                                     {% endif %}
  773.                                 </li>
  774.                             {% endfor %}
  775.                         </ul>
  776.                     </div>
  777.                     <div class="ec-pagerRole">
  778.                         {% include "pager.twig" with {'pages': pagination.paginationData} %}
  779.                     </div>
  780.                 {% endif %}
  781.             </div>
  782.             {# / 商品一覧 #}
  783.         </div>
  784.         {# / ec-productListLayout #}
  785.     {% endif %}
  786. {% endblock %}