custom/plugins/Spf/src/Storefront/Controller/SpfController.php line 355

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Spf\Storefront\Controller;
  3. use Shopware\Core\Checkout\Customer\CustomerEntity;
  4. use Shopware\Core\Content\Cms\SalesChannel\SalesChannelCmsPageLoader;
  5. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\AndFilter;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\ContainsFilter;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
  10. use Shopware\Core\Framework\Routing\Annotation\RouteScope;
  11. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  12. use Shopware\Storefront\Controller\StorefrontController;
  13. use Shopware\Storefront\Page\Account\Overview\AccountOverviewPageLoader;
  14. use Shopware\Core\Framework\Context;
  15. use Shopware\Core\Framework\Routing\Annotation\LoginRequired;
  16. use Shopware\Storefront\Page\Navigation\NavigationPageLoader;
  17. use Symfony\Component\HttpFoundation\Response;
  18. use Symfony\Component\Routing\Annotation\Route;
  19. use Symfony\Component\HttpFoundation\Request;
  20. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  21. use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
  22. use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepository;
  23. use Doctrine\DBAL\Connection;
  24. /**
  25.  * @RouteScope(scopes={"storefront"})
  26.  */
  27. class SpfController extends StorefrontController
  28. {
  29.     private AccountOverviewPageLoader $overviewPageLoader;
  30.     private SalesChannelRepository $productRepo;
  31.     private EntityRepositoryInterface $fullProductRepo;
  32.     private EntityRepositoryInterface $orderRepo;
  33.     private EntityRepositoryInterface $ruleRepo;
  34.     private EntityRepositoryInterface $priceRepo;
  35.     private EntityRepositoryInterface $ruleConditionRepo;
  36.     public function __construct(
  37.         AccountOverviewPageLoader $overviewPageLoader,
  38.         NavigationPageLoader      $pageLoader,
  39.         EntityRepositoryInterface $orderRepo,
  40.         SalesChannelRepository    $productRepo,
  41.         EntityRepositoryInterface $fullProductRepo,
  42.         EntityRepositoryInterface $ruleRepo,
  43.         EntityRepositoryInterface $ruleConditionRepo,
  44.         EntityRepositoryInterface $priceRepo,
  45.         Connection                $connection)
  46.     {
  47.         $this->overviewPageLoader $overviewPageLoader;
  48.         $this->pageLoader         $pageLoader;
  49.         $this->productRepo        $productRepo;
  50.         $this->fullProductRepo    $fullProductRepo;
  51.         $this->orderRepo          $orderRepo;
  52.         $this->connection         $connection;
  53.         $this->ruleRepo           $ruleRepo;
  54.         $this->priceRepo          $priceRepo;
  55.         $this->ruleConditionRepo  $ruleConditionRepo;
  56.     }
  57.     /**
  58.      * @RouteScope(scopes={"storefront"})
  59.      * @Route("/ersatzteilfinder/{id}", name="frontend.spf.spf", methods={"GET"})
  60.      */
  61.     public function showSpf(string $idRequest $requestSalesChannelContext $context): Response
  62.     {
  63.         $page          $this->pageLoader->load($request$context);
  64.         $articles      = [];
  65.         $allGroups     = [];
  66.         $groups        = [];
  67.         $pointsGrouped = [];
  68.         $shopProducts  = [];
  69.         $title         '';
  70.         $categories    = [];
  71.         $noResult true;
  72.         $model self::getIdFromSlug($id);
  73.         if (!empty($model))
  74.         {
  75.             $connection \Shopware\Core\Kernel::getConnection();
  76.             $result     $connection->executeQuery("SELECT s.*, c.name, n.name  as article_description, IF (p.active = 1, lower(hex(p.id)), null) as product_id  FROM hobaIT_sparepartfinder AS s 
  77.             LEFT JOIN hobaIT_csb_article_data AS c ON c.article_number = s.article_number 
  78.             LEFT JOIN hobaIT_sparepartfinder_article_names n ON n.article_number = s.article_number
  79.             LEFT JOIN product AS p ON p.product_number = s.article_number
  80.             WHERE model_number = ? ORDER BY `group`, sequence_number ASC", [$model]);
  81.             foreach ($result as $res)
  82.             {
  83.                 if (empty($groups[$res['group']]))
  84.                 {
  85.                     $groups[$res['group']] = [];
  86.                 }
  87.                 $groups[$res['group']][]          = $res;
  88.                 $articles[$res['article_number']] = 1;
  89.                 $shopProducts[$res['product_id']] = 1;
  90.             }
  91.             array_walk($articles, function (&$value$key) {
  92.                 $value '"' $key '"';
  93.             });
  94.             if (!empty($groups))
  95.             {
  96.                 $noResult   false;
  97.                 $allGroups  $connection->executeQuery("SELECT * FROM hobaIT_sparepartfinder_groups WHERE id IN (" implode(','array_keys($groups)) . ") ORDER BY number, part_type ASC")->fetchAllAssociative();
  98.                 $points     $connection->executeQuery("SELECT * FROM hobaIT_sparepartfinder_images WHERE group_id IN (" implode(','array_keys($groups)) . ") AND article_number IN (" implode(','$articles) . ")")->fetchAllAssociative();
  99.                 $model      $connection->executeQuery("SELECT * FROM hobaIT_sparepartfinder_models WHERE model_number =  ?", [$model])->fetchAssociative();
  100.                 $powerParts $connection->executeQuery("SELECT s.product_number as product_number, lower(hex(p.id)) as id FROM hobaIT_sparepartfinder_powerparts as s LEFT JOIN product as p on p.product_number = s.product_number WHERE model_number =  ?", [$model['model_number']])->fetchAllAssociative();
  101.                 foreach ($points as $point)
  102.                 {
  103.                     if (empty($pointsGrouped[$point['group_id']][$point['article_number']]))
  104.                     {
  105.                         $pointsGrouped[$point['group_id']][$point['article_number']] = [];
  106.                     }
  107.                     $pointsGrouped[$point['group_id']][$point['article_number']][] = [
  108.                         'x' => $point['point_x'],
  109.                         'y' => $point['point_y']
  110.                     ];
  111.                 }
  112.                 $model['has_frame']  = false;
  113.                 $model['has_engine'] = false;
  114.                 foreach ($allGroups as &$g)
  115.                 {
  116.                     if ($g['part_type'] == 0)
  117.                     {
  118.                         $model['has_frame'] = true;
  119.                     }
  120.                     if ($g['part_type'] == 1)
  121.                     {
  122.                         $model['has_engine'] = true;
  123.                     }
  124.                     $g['articles'] = $groups[$g['id']];
  125.                 }
  126.                 $title $page->getMetaInformation()->getMetaTitle();
  127.                 $page->getMetaInformation()->setMetaTitle($title ' - Ersatzteile ' $model['model_name']);
  128.                 $shopProducts $this->getProductsByIds(array_keys($shopProducts), $context);
  129.             }
  130.             if (!empty($powerParts))
  131.             {
  132.                 $ppId       = (array_column($powerParts'id'));
  133.                 $powerParts self::getProductsByIds($ppId$context);
  134.                 foreach ($powerParts as $item)
  135.                 {
  136.                     if (empty($categories[$item->getCategories()->first()->getName()]))
  137.                     {
  138.                         $categories[$item->getCategories()->first()->getName()] = [];
  139.                     }
  140.                     $categories[$item->getCategories()->first()->getName()][] = $item;
  141.                 }
  142.                 ksort($categories);
  143.             }
  144.         }
  145.         if ($noResult)
  146.         {
  147.             $model               = [];
  148.             $model['model_name'] = 'Kein Ergebnis';
  149.             $model['has_frame']  = false;
  150.             $model['has_engine'] = false;
  151.             $page->getMetaInformation()->setMetaTitle($title ' Ersatzteilfinder - Fehler: Keine Resultate');
  152.         }
  153.         return $this->renderStorefront('@Spf/storefront/page/spf.html.twig', [
  154.                 'page'         => $page,
  155.                 'groups'       => $allGroups,
  156.                 'model'        => $model,
  157.                 'points'       => json_encode($pointsGrouped),
  158.                 'shopProducts' => $shopProducts,
  159.                 'powerparts'   => $categories
  160.             ]
  161.         );
  162.     }
  163.     /**
  164.      * @RouteScope(scopes={"storefront"})
  165.      * @Route("/ersatzteilfinder", name="frontend.spf.models", methods={"GET"})
  166.      */
  167.     public function showModelList(Request $requestSalesChannelContext $context): Response
  168.     {
  169.         $page       $this->pageLoader->load($request$context);
  170.         $connection \Shopware\Core\Kernel::getConnection();
  171.         $title      $page->getMetaInformation()->getMetaTitle();
  172.         $page->getMetaInformation()->setMetaTitle($title ' - Ersatzteilfinder Modellübersicht');
  173.         $results      $connection->executeQuery('SELECT DISTINCT m.model_number AS id , m.model_name as `name` , m.model_code AS `hex`, count(s.group) AS results FROM hobaIT_sparepartfinder_models AS m LEFT JOIN hobaIT_sparepartfinder AS s ON m.model_number=s.model_number  WHERE manufacturer = "KTM"   GROUP BY m.model_number order BY m.model_name ASC');
  174.         $ktmModels    self::processModelList($results);
  175.         $results      $connection->executeQuery('SELECT DISTINCT m.model_number AS id , m.model_name as `name` , m.model_code AS `hex`, count(s.group) AS results FROM hobaIT_sparepartfinder_models AS m LEFT JOIN hobaIT_sparepartfinder AS s ON m.model_number=s.model_number  WHERE manufacturer = "Husqvarna"   GROUP BY m.model_number order BY m.model_name ASC');
  176.         $huskyModels  self::processModelList($results);
  177.         $results      $connection->executeQuery('SELECT DISTINCT m.model_number AS id , m.model_name as `name` , m.model_code AS `hex`, count(s.group) AS results FROM hobaIT_sparepartfinder_models AS m LEFT JOIN hobaIT_sparepartfinder AS s ON m.model_number=s.model_number  WHERE manufacturer = "GASGAS"   GROUP BY m.model_number order BY m.model_name ASC');
  178.         $gasgasModels self::processModelList($results);
  179.         return $this->renderStorefront('@Spf/storefront/page/models.html.twig', [
  180.                 'page'         => $page,
  181.                 'ktmModels'    => $ktmModels,
  182.                 'huskyModels'  => $huskyModels,
  183.                 'gasgasModels' => $gasgasModels,
  184.             ]
  185.         );
  186.     }
  187.     private static function processModelList(\Doctrine\DBAL\ForwardCompatibility\Result $results)
  188.     {
  189.         $models = [];
  190.         foreach ($results as $result)
  191.         {
  192.             $result['slug'] = self::slugify($result['name']);
  193.             $models[]       = $result;
  194.         }
  195.         return $models;
  196.     }
  197.     /**
  198.      * @param array  $cats
  199.      * @param string $articlesFile
  200.      *
  201.      * @return void
  202.      */
  203.     public static function getCachedNewsPosts(array $cats = [5714], string $articlesFile 'article-data.json')
  204.     {
  205.         $forceReload $_GET['forceReload'] ?? 0;
  206.         if ($forceReload || time() - filemtime($articlesFile) >= 3600)
  207.         {
  208.             $cats = [5714];
  209.             $host 'https://newsblog.ktm-sturm.de/wp-json/wp/v2/posts?categories=' implode(','$cats) . '&per_page=100';
  210.             $ch   curl_init($host);
  211.             curl_setopt($chCURLOPT_TIMEOUT30);
  212.             curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
  213.             $return json_decode(curl_exec($ch));
  214.             curl_close($ch);
  215.             $posts = [];
  216.             foreach ($return as $item)
  217.             {
  218.                 $content                          self::truncate($item->content->rendered);
  219.                 $posts[$item->id]['title']        = $item->title->rendered;
  220.                 $posts[$item->id]['content']      = $content;
  221.                 $posts[$item->id]['content_full'] = $item->content->rendered;
  222.                 $posts[$item->id]['date']         = $item->date;
  223.                 $posts[$item->id]['id']           = $item->id;
  224.                 $posts[$item->id]['slug']         = self::slugify($item->title->rendered) . '-' $item->id;
  225.                 $posts[$item->id]['cat']          = $item->categories;
  226.                 if (!empty($item->featured_image_url))
  227.                 {
  228.                     $posts[$item->id]['featured_image_url'] = $item->featured_image_url;
  229.                 }
  230.                 if (!empty($item->featured_image_url))
  231.                 {
  232.                     $posts[$item->id]['thumb_image_url'] = $item->thumb_image_url;
  233.                 }
  234.             }
  235.             file_put_contents($articlesFilejson_encode($posts));
  236.         }
  237.     }
  238.     /**
  239.      * @param array  $cats
  240.      * @param string $articlesFile
  241.      *
  242.      * @return array
  243.      */
  244.     public static function getNewsPostsFromCache(array $catsstring $articlesFile 'article-data.json')
  245.     {
  246.         $matching = [];
  247.         $posts    json_decode(file_get_contents($articlesFile), true);
  248.         foreach ($posts as $post)
  249.         {
  250.             if (count(array_intersect($cats$post['cat'])) > 0)
  251.             {
  252.                 $matching[] = $post;
  253.             }
  254.         }
  255.         return $matching;
  256.     }
  257.     /**
  258.      * @param array $cats
  259.      * @param int   $perPage
  260.      *
  261.      * @return array
  262.      */
  263.     public static function getNewsPosts(array $cats = [5714], int $perPage 9int $limit 0)
  264.     {
  265.         $articlesFile 'article-data.json';
  266.         $pagination   '';
  267.         self::getCachedNewsPosts($cats$articlesFile);
  268.         $articles self::getNewsPostsFromCache($cats$articlesFile);
  269.         if ($limit)
  270.         {
  271.             $articles array_slice($articles0$limit);
  272.         }
  273.         if ($perPage)
  274.         {
  275.             $page      $_GET['page'] ?? 1;
  276.             $total     count($articles);
  277.             $posts     array_slice($articles, ($page 1) * $perPage$perPage);
  278.             $pageCount = ((int) ($total $perPage) + 1);
  279.             $pagination '<div class="pagination">';
  280.             if ($page 1)
  281.             {
  282.                 $pagination .= '<a class="page prev" href="/news?page=' . ($page 1) . '">«</a>';
  283.             }
  284.             for ($i 1$i <= $pageCount$i++)
  285.             {
  286.                 if ($i == $page)
  287.                 {
  288.                     $pagination .= '<span class="page active">[' $i ']</span>';
  289.                 }
  290.                 else
  291.                 {
  292.                     $pagination .= '<a class="page" href="/news?page=' $i '"> ' $i ' </a>';
  293.                 }
  294.             }
  295.             if ($page $pageCount)
  296.             {
  297.                 $pagination .= '<a class="page next" href="/news?page=' . ($page 1) . '">»</a>';
  298.             }
  299.             $pagination .= '</div>';
  300.         }
  301.         else
  302.         {
  303.             $posts $articles;
  304.         }
  305.         return [$posts$pagination];
  306.     }
  307.     /**
  308.      * @RouteScope(scopes={"storefront"})
  309.      * @Route("/news", name="frontend.news.article", methods={"GET"})
  310.      */
  311.     public function showNews(Request $requestSalesChannelContext $context): Response
  312.     {
  313.         list($posts$pagination) = self::getNewsPosts([5],9);
  314.         $page $this->pageLoader->load($request$context);
  315.         return $this->renderStorefront('@Spf/storefront/page/news.html.twig', [
  316.                 'page'       => $page,
  317.                 'posts'      => $posts,
  318.                 'pagination' => $pagination
  319.             ]
  320.         );
  321.     }
  322.     /**
  323.      * @RouteScope(scopes={"storefront"})
  324.      * @Route("/blog", name="frontend.blog.article", methods={"GET"})
  325.      */
  326.     public function showBlog(Request $requestSalesChannelContext $context): Response
  327.     {
  328.         list($posts$pagination) = self::getNewsPosts([14], 9);
  329.         $page $this->pageLoader->load($request$context);
  330.         return $this->renderStorefront('@Spf/storefront/page/blog.html.twig', [
  331.                 'page'       => $page,
  332.                 'posts'      => $posts,
  333.                 'pagination' => $pagination
  334.             ]
  335.         );
  336.     }
  337.     /**
  338.      * @RouteScope(scopes={"storefront"})
  339.      * @Route("/news/{id}", name="frontend.news.news", methods={"GET"})
  340.      */
  341.     public function showArticle(string $idRequest $requestSalesChannelContext $context): Response
  342.     {
  343.         $page     $this->pageLoader->load($request$context);
  344.         $articles json_decode(file_get_contents('article-data.json'));
  345.         $id       self::getIdFromSlug($id);
  346.         return $this->renderStorefront('@Spf/storefront/page/article.html.twig', [
  347.                 'page' => $page,
  348.                 'item' => $articles->{$id}
  349.             ]
  350.         );
  351.     }
  352.     /**
  353.      * Get id from a slug
  354.      *
  355.      * @param string $slug
  356.      *
  357.      * @return string
  358.      */
  359.     private static function getIdFromSlug(string $slug): int
  360.     {
  361.         $exp explode('-'$slug);
  362.         $id  $exp[count($exp) - 1];
  363.         if ('modelcode' == $exp[count($exp) - 2])
  364.         {
  365.             $connection \Shopware\Core\Kernel::getConnection();
  366.             $id         $connection->executeQuery("SELECT model_number FROM hobaIT_sparepartfinder_models WHERE model_code LIKE ?", ['%' $id '%'])->fetchFirstColumn()[0];
  367.         }
  368.         return (int) $id;
  369.     }
  370.     /**
  371.      * Truncate text
  372.      *
  373.      * @param string $str
  374.      * @param int    $width
  375.      *
  376.      * @return string
  377.      */
  378.     private
  379.     static function truncate(string $strint $width 300)
  380.     {
  381.         if (strlen($str) <= $width)
  382.         {
  383.             return $str;
  384.         }
  385.         $str str_replace("\n"''strip_tags($str));
  386.         return substr($str0strpos(wordwrap($str$width), "\n")) . '...';
  387.     }
  388.     /**
  389.      * Create a slug
  390.      *
  391.      * @param        $text
  392.      * @param string $divider
  393.      *
  394.      * @return string
  395.      */
  396.     private
  397.     static function slugify($textstring $divider '-')
  398.     {
  399.         // replace non letter or digits by divider
  400.         $text preg_replace('~[^\pL\d]+~u'$divider$text);
  401.         // transliterate
  402.         $text iconv('utf-8''ISO-8859-1//TRANSLIT'$text);
  403.         // remove unwanted characters
  404.         $text preg_replace('~[^-\w]+~'''$text);
  405.         // trim
  406.         $text trim($text$divider);
  407.         // remove duplicate divider
  408.         $text preg_replace('~-+~'$divider$text);
  409.         // lowercase
  410.         $text strtolower($text);
  411.         if (empty($text))
  412.         {
  413.             return 'n-a';
  414.         }
  415.         return $text;
  416.     }
  417.     /**
  418.      * @param array $productIds
  419.      * @param       $context
  420.      *
  421.      * @return object
  422.      */
  423.     public
  424.     function getProductsByIds(array $productIds$context): object
  425.     {
  426.         $productsCriteria = (new Criteria($productIds))
  427.             ->addAssociation('prices')
  428.             ->addAssociation('categories')
  429.             ->addAssociation('media');
  430.         return $this->productRepo->search($productsCriteria$context)->getEntities();
  431.     }
  432. }