app/Plugin/NZCategoryLayoutManager42/EventSubscriber/TemplateEventSubscriber.php line 26

Open in your IDE?
  1. <?php
  2. namespace Plugin\NZCategoryLayoutManager42\EventSubscriber;
  3. use Eccube\Event\TemplateEvent;
  4. use Plugin\NZCategoryLayoutManager42\Repository\CategoryLayoutRepository;
  5. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  6. use Symfony\Component\HttpFoundation\RequestStack;
  7. class TemplateEventSubscriber implements EventSubscriberInterface
  8. {
  9.     private $categoryLayoutRepository;
  10.     private $requestStack;
  11.     public function __construct(CategoryLayoutRepository $categoryLayoutRepositoryRequestStack $requestStack)
  12.     {
  13.         $this->categoryLayoutRepository $categoryLayoutRepository;
  14.         $this->requestStack $requestStack;
  15.     }
  16.     public static function getSubscribedEvents()
  17.     {
  18.         return ['Product/list.twig' => 'onProductList'];
  19.     }
  20.     public function onProductList(TemplateEvent $event)
  21.     {
  22.         $request $this->requestStack->getCurrentRequest();
  23.         if (!$request) {
  24.             return;
  25.         }
  26.         $categoryId $request->query->get('category_id');
  27.         if (!$categoryId) {
  28.             return;
  29.         }
  30.         try {
  31.             $layout $this->categoryLayoutRepository->findActiveByParentCategoryId((int)$categoryId);
  32.             if (!$layout) {
  33.                 return;
  34.             }
  35.             
  36.             $items $layout->getCategoryLayoutItems();
  37.             if (!$items || $items->count() === 0) {
  38.                 return;
  39.             }
  40.             // 子カテゴリ情報を構築
  41.             $gameCategories = [];
  42.             foreach ($items as $item) {
  43.                 $gameCategories[] = [
  44.                     'id' => (int)$item->getChildCategoryId(), 
  45.                     'name' => $item->getDisplayName(), 
  46.                     'icon' => $item->getIconPath() ?: ''
  47.                 ];
  48.             }
  49.             $gameCategoriesJson json_encode($gameCategoriesJSON_UNESCAPED_UNICODE);
  50.             // CSS
  51.             $event->addSnippet('<style>
  52. .game-category-parent-layout{width:100%;padding:20px 0}
  53. .game-section{margin-bottom:60px;padding-bottom:40px;border-bottom:2px solid #e0e0e0}
  54. .game-section:last-child{border-bottom:none}
  55. .game-section-title{font-size:1.8rem;margin-bottom:20px;padding-left:10px;border-left:5px solid #333}
  56. .game-section-title a{color:#333;text-decoration:none}
  57. .game-section-title a:hover{color:#007bff}
  58. .game-section-layout{display:flex!important;gap:20px;align-items:flex-start}
  59. .game-icon-section{flex:0 0 240px!important;width:240px!important;display:block;position:relative;transition:all .3s;border-radius:12px;overflow:hidden;box-shadow:0 8px 20px rgba(0,0,0,.2)}
  60. .game-icon-section:hover{transform:translateY(-8px) scale(1.02);box-shadow:0 12px 30px rgba(0,0,0,.3)}
  61. .game-icon-image{width:100%!important;height:auto!important;display:block!important;border-radius:12px!important}
  62. .game-products-section{flex:1!important;min-width:0!important;overflow:visible!important;position:relative}
  63. .game-products-slider{position:relative;width:100%;padding:0 30px}
  64. .game-products-slider .slick-list{overflow:hidden!important}
  65. .game-products-slider .slick-track{display:flex!important}
  66. .game-products-slider .slick-slide{height:auto!important;float:none!important;margin:0 10px}
  67. .game-products-slider .slick-prev,.game-products-slider .slick-next{width:50px!important;height:50px!important;background:rgba(0,0,0,.7)!important;border-radius:50%!important;z-index:10!important;border:2px solid white!important;box-shadow:0 2px 8px rgba(0,0,0,.3)!important;top:50%!important;transform:translateY(-50%)!important;position:absolute!important}
  68. .game-products-slider .slick-prev:hover,.game-products-slider .slick-next:hover{background:rgba(0,0,0,.9)!important}
  69. .game-products-slider .slick-prev{left:0!important}
  70. .game-products-slider .slick-next{right:0!important}
  71. .game-products-slider .slick-prev:before,.game-products-slider .slick-next:before{content:""!important}
  72. .game-products-slider .slick-disabled{opacity:.3!important}
  73. .no-products{text-align:center;padding:60px 20px;color:#999}
  74. @media(max-width:768px){
  75.     .game-section-layout{gap:15px}
  76.     .game-icon-section{flex:0 0 120px!important;width:120px!important}
  77.     .game-products-slider .slick-prev,.game-products-slider .slick-next{display:none!important}
  78.     .game-products-slider{padding:0}
  79.     .game-section-title{font-size:1.4rem}
  80. }
  81. @media(max-width:480px){
  82.     .game-icon-section{flex:0 0 100px!important;width:100px!important}
  83. }
  84. </style>'false);
  85.             // JS
  86.             $event->addSnippet('<script>
  87. (function(){
  88.     if(typeof eccube==="undefined"||!eccube.productsClassCategories) return;
  89.     
  90.     var gameCategories = ' $gameCategoriesJson ';
  91.     var $shelfGrid = $(".ec-shelfGrid");
  92.     var $items = $shelfGrid.find(".ec-shelfGrid__item");
  93.     
  94.     if($items.length === 0) return;
  95.     
  96.     var productsByCategory = {};
  97.     
  98.     $items.each(function(){
  99.         var $item = $(this);
  100.         var categoryIds = $item.attr("data-category-ids");
  101.         if(!categoryIds) return;
  102.         
  103.         var categories = categoryIds.split(",").map(function(id){ return parseInt(id.trim()); });
  104.         
  105.         gameCategories.forEach(function(game){
  106.             if(categories.indexOf(game.id) !== -1){
  107.                 if(!productsByCategory[game.id]) productsByCategory[game.id] = [];
  108.                 productsByCategory[game.id].push($item.clone());
  109.             }
  110.         });
  111.     });
  112.     
  113.     var $newLayout = $("<div class=\"game-category-parent-layout\"></div>");
  114.     
  115.     gameCategories.forEach(function(game){
  116.         var products = productsByCategory[game.id] || [];
  117.         var $section = $("<div class=\"game-section\"></div>");
  118.         var $title = $("<h2 class=\"game-section-title\"><a href=\"?category_id=" + game.id + "\">" + game.name + "</a></h2>");
  119.         var $layout = $("<div class=\"game-section-layout\"></div>");
  120.         
  121.         var iconHtml = game.icon ? "<img src=\"" + game.icon + "\" alt=\"" + game.name + "\" class=\"game-icon-image\">" : "<div style=\"width:100%;height:200px;background:#ccc;display:flex;align-items:center;justify-content:center;\">" + game.name + "</div>";
  122.         var $iconSection = $("<a href=\"?category_id=" + game.id + "\" class=\"game-icon-section\">" + iconHtml + "</a>");
  123.         
  124.         var $productsSection = $("<div class=\"game-products-section\"></div>");
  125.         var $slider = $("<div class=\"game-products-slider slider-" + game.id + "\"></div>");
  126.         
  127.         if(products.length > 0){
  128.             products.forEach(function($product){ $slider.append($product); });
  129.         } else {
  130.             $slider.html("<div class=\"no-products\"><p>商品準備中です</p></div>");
  131.         }
  132.         
  133.         $productsSection.append($slider);
  134.         $layout.append($iconSection);
  135.         $layout.append($productsSection);
  136.         $section.append($title);
  137.         $section.append($layout);
  138.         $newLayout.append($section);
  139.     });
  140.     
  141.     $shelfGrid.replaceWith($newLayout);
  142.     
  143.     // Slick初期化
  144.     setTimeout(function(){
  145.         gameCategories.forEach(function(game){
  146.             var $slider = $(".slider-" + game.id);
  147.             if($slider.find(".ec-shelfGrid__item").length > 0 && typeof $.fn.slick !== "undefined"){
  148.                 $slider.slick({
  149.                     slidesToShow: 4,
  150.                     slidesToScroll: 1,
  151.                     infinite: false,
  152.                     arrows: true,
  153.                     prevArrow: "<button type=\"button\" class=\"slick-prev\"><svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\"><path d=\"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z\"/></svg></button>",
  154.                     nextArrow: "<button type=\"button\" class=\"slick-next\"><svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\"><path d=\"M8.59 16.59L10 18l6-6-6-6-1.41 1.41L13.17 12z\"/></svg></button>",
  155.                     responsive: [
  156.                         {breakpoint: 1200, settings: {slidesToShow: 3}},
  157.                         {breakpoint: 768, settings: {slidesToShow: 1, arrows: false}},
  158.                         {breakpoint: 480, settings: {slidesToShow: 1, arrows: false}}
  159.                     ]
  160.                 });
  161.             }
  162.         });
  163.     }, 300);
  164. })();
  165. </script>'false);
  166.         } catch (\Exception $e) {
  167.             error_log('NZCategoryLayoutManager42: ' $e->getMessage());
  168.         }
  169.     }
  170. }