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