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