src/Eccube/Repository/ProductRepository.php line 63

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of EC-CUBE
  4.  *
  5.  * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
  6.  *
  7.  * http://www.ec-cube.co.jp/
  8.  *
  9.  * For the full copyright and license information, please view the LICENSE
  10.  * file that was distributed with this source code.
  11.  */
  12. namespace Eccube\Repository;
  13. use Doctrine\Common\Collections\ArrayCollection;
  14. use Doctrine\Persistence\ManagerRegistry as RegistryInterface;
  15. use Eccube\Common\EccubeConfig;
  16. use Eccube\Doctrine\Query\Queries;
  17. use Eccube\Entity\Category;
  18. use Eccube\Entity\Master\ProductListMax;
  19. use Eccube\Entity\Master\ProductListOrderBy;
  20. use Eccube\Entity\Master\ProductStatus;
  21. use Eccube\Entity\Product;
  22. use Eccube\Entity\ProductStock;
  23. use Eccube\Entity\Tag;
  24. use Eccube\Util\StringUtil;
  25. /**
  26.  * ProductRepository
  27.  *
  28.  * This class was generated by the Doctrine ORM. Add your own custom
  29.  * repository methods below.
  30.  */
  31. class ProductRepository extends AbstractRepository
  32. {
  33.     /**
  34.      * @var Queries
  35.      */
  36.     protected $queries;
  37.     /**
  38.      * @var EccubeConfig
  39.      */
  40.     protected $eccubeConfig;
  41.     public const COLUMNS = [
  42.         'product_id' => 'p.id''name' => 'p.name''product_code' => 'pc.code''stock' => 'pc.stock''status' => 'p.Status''create_date' => 'p.create_date''update_date' => 'p.update_date',
  43.     ];
  44.     /**
  45.      * ProductRepository constructor.
  46.      *
  47.      * @param RegistryInterface $registry
  48.      * @param Queries $queries
  49.      * @param EccubeConfig $eccubeConfig
  50.      */
  51.     public function __construct(
  52.         RegistryInterface $registry,
  53.         Queries $queries,
  54.         EccubeConfig $eccubeConfig
  55.     ) {
  56.         parent::__construct($registryProduct::class);
  57.         $this->queries $queries;
  58.         $this->eccubeConfig $eccubeConfig;
  59.     }
  60.     /**
  61.      * Find the Product with sorted ClassCategories.
  62.      *
  63.      * @param integer $productId
  64.      *
  65.      * @return Product
  66.      */
  67.     public function findWithSortedClassCategories($productId)
  68.     {
  69.         $qb $this->createQueryBuilder('p');
  70.         $qb->addSelect(['pc''cc1''cc2''pi''pt'])
  71.             ->innerJoin('p.ProductClasses''pc')
  72.             ->leftJoin('pc.ClassCategory1''cc1')
  73.             ->leftJoin('pc.ClassCategory2''cc2')
  74.             ->leftJoin('p.ProductImage''pi')
  75.             ->leftJoin('p.ProductTag''pt')
  76.             ->where('p.id = :id')
  77.             ->andWhere('pc.visible = :visible')
  78.             ->setParameter('id'$productId)
  79.             ->setParameter('visible'true)
  80.             ->orderBy('cc1.sort_no''DESC')
  81.             ->addOrderBy('cc2.sort_no''DESC');
  82.         $product $qb
  83.             ->getQuery()
  84.             ->getSingleResult();
  85.         return $product;
  86.     }
  87.     /**
  88.      * Find the Products with sorted ClassCategories.
  89.      *
  90.      * @param array $ids Product in ids
  91.      * @param string $indexBy The index for the from.
  92.      *
  93.      * @return ArrayCollection|array
  94.      */
  95.     public function findProductsWithSortedClassCategories(array $ids$indexBy null)
  96.     {
  97.         if (count($ids) < 1) {
  98.             return [];
  99.         }
  100.         $qb $this->createQueryBuilder('p'$indexBy);
  101.         $qb->addSelect(['pc''cc1''cc2''pi''pt''tr''ps'])
  102.             ->innerJoin('p.ProductClasses''pc')
  103.             // XXX Joined 'TaxRule' and 'ProductStock' to prevent lazy loading
  104.             ->leftJoin('pc.TaxRule''tr')
  105.             ->innerJoin('pc.ProductStock''ps')
  106.             ->leftJoin('pc.ClassCategory1''cc1')
  107.             ->leftJoin('pc.ClassCategory2''cc2')
  108.             ->leftJoin('p.ProductImage''pi')
  109.             ->leftJoin('p.ProductTag''pt')
  110.             ->where($qb->expr()->in('p.id'$ids))
  111.             ->andWhere('pc.visible = :visible')
  112.             ->setParameter('visible'true)
  113.             ->orderBy('cc1.sort_no''DESC')
  114.             ->addOrderBy('cc2.sort_no''DESC');
  115.         $products $qb
  116.             ->getQuery()
  117.             ->useResultCache(true$this->eccubeConfig['eccube_result_cache_lifetime_short'])
  118.             ->getResult();
  119.         return $products;
  120.     }
  121.     /**
  122.      * get query builder.
  123.      *
  124.      * @param array{
  125.      *         category_id?:Category,
  126.      *         name?:string,
  127.      *         pageno?:string,
  128.      *         disp_number?:ProductListMax,
  129.      *         orderby?:ProductListOrderBy
  130.      *     } $searchData
  131.      *
  132.      * @return \Doctrine\ORM\QueryBuilder
  133.      */
  134.     public function getQueryBuilderBySearchData($searchData)
  135.     {
  136.         $qb $this->createQueryBuilder('p')
  137.             ->andWhere('p.Status = 1');
  138.         // category
  139.         $categoryJoin false;
  140.         if (!empty($searchData['category_id']) && $searchData['category_id']) {
  141.             $Categories $searchData['category_id']->getSelfAndDescendants();
  142.             if ($Categories) {
  143.                 $qb
  144.                     ->innerJoin('p.ProductCategories''pct')
  145.                     ->innerJoin('pct.Category''c')
  146.                     ->andWhere($qb->expr()->in('pct.Category'':Categories'))
  147.                     ->setParameter('Categories'$Categories);
  148.                 $categoryJoin true;
  149.             }
  150.         }
  151.         // name
  152.         if (isset($searchData['name']) && StringUtil::isNotBlank($searchData['name'])) {
  153.             $keywords preg_split('/[\s ]+/u'str_replace(['%''_'], ['\\%''\\_'], $searchData['name']), -1PREG_SPLIT_NO_EMPTY);
  154.             foreach ($keywords as $index => $keyword) {
  155.                 $key sprintf('keyword%s'$index);
  156.                 $qb
  157.                     ->andWhere(sprintf('NORMALIZE(p.name) LIKE NORMALIZE(:%s) OR
  158.                         NORMALIZE(p.search_word) LIKE NORMALIZE(:%s) OR
  159.                         EXISTS (SELECT wpc%d FROM \Eccube\Entity\ProductClass wpc%d WHERE p = wpc%d.Product AND NORMALIZE(wpc%d.code) LIKE NORMALIZE(:%s))',
  160.                         $key$key$index$index$index$index$key))
  161.                     ->setParameter($key'%'.$keyword.'%');
  162.             }
  163.         }
  164.         // Order By
  165.         // 価格低い順
  166.         $config $this->eccubeConfig;
  167.         if (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_price_lower']) {
  168.             // @see http://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html
  169.             $qb->addSelect('MIN(pc.price02) as HIDDEN price02_min');
  170.             $qb->innerJoin('p.ProductClasses''pc');
  171.             $qb->andWhere('pc.visible = true');
  172.             $qb->groupBy('p.id');
  173.             $qb->orderBy('price02_min''ASC');
  174.             $qb->addOrderBy('p.id''DESC');
  175.         // 価格高い順
  176.         } elseif (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_price_higher']) {
  177.             $qb->addSelect('MAX(pc.price02) as HIDDEN price02_max');
  178.             $qb->innerJoin('p.ProductClasses''pc');
  179.             $qb->andWhere('pc.visible = true');
  180.             $qb->groupBy('p.id');
  181.             $qb->orderBy('price02_max''DESC');
  182.             $qb->addOrderBy('p.id''DESC');
  183.         // 新着順
  184.         } elseif (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_newer']) {
  185.             // 在庫切れ商品非表示の設定が有効時対応
  186.             // @see https://github.com/EC-CUBE/ec-cube/issues/1998
  187.             if ($this->getEntityManager()->getFilters()->isEnabled('option_nostock_hidden') == true) {
  188.                 $qb->innerJoin('p.ProductClasses''pc');
  189.                 $qb->andWhere('pc.visible = true');
  190.             }
  191.             $qb->orderBy('p.create_date''DESC');
  192.             $qb->addOrderBy('p.id''DESC');
  193.         } else {
  194. // 2025.02.27 start
  195. // ↓元のコード
  196. //            if ($categoryJoin === false) {
  197. //                $qb
  198. //                    ->leftJoin('p.ProductCategories', 'pct')
  199. //                    ->leftJoin('pct.Category', 'c');
  200. //            }
  201. //
  202. //            $qb->addSelect("CASE WHEN pct.recommend_rank IS NULL THEN 0 ELSE pct.recommend_rank END AS HIDDEN recommend_rank")
  203. //               ->orderBy('recommend_rank', 'DESC')
  204. //               ->addOrderBy('p.id', 'DESC');
  205. // ↑元のコード
  206.             $in_category1 false;
  207.             $in_category2 false;
  208.             $in_category3 false;
  209.             $in_category4 false;
  210.             $in_category5 false;
  211.             if (!empty($searchData['category_id']) && $searchData['category_id']) {
  212.                 $qb->innerJoin('p.ProductCategories''pct1')
  213.                    ->addSelect("CASE WHEN pct1.recommend_rank IS NULL THEN 0 ELSE pct1.recommend_rank END AS HIDDEN recommend_rank1")
  214.                    ->andWhere('pct1.Category = :pct1_id')
  215.                    ->setParameter('pct1_id'$searchData['category_id']->getId());
  216.                 $in_category1 true;
  217.             }
  218.             if (!empty($searchData['category_id2']) && $searchData['category_id2']) {
  219.                 $qb->innerJoin('p.ProductCategories''pct11')
  220.                    ->addSelect("CASE WHEN pct11.recommend_rank IS NULL THEN 0 ELSE pct11.recommend_rank END AS HIDDEN recommend_rank2")
  221.                    ->andWhere('pct11.Category = :pct2_id')
  222.                    ->setParameter('pct2_id'$searchData['category_id2']);
  223.                 $in_category2 true;
  224.             }
  225.             if (!empty($searchData['category_id3']) && $searchData['category_id3']) {
  226.                 $qb->innerJoin('p.ProductCategories''pct12')
  227.                    ->addSelect("CASE WHEN pct12.recommend_rank IS NULL THEN 0 ELSE pct12.recommend_rank END AS HIDDEN recommend_rank3")
  228.                    ->andWhere('pct12.Category = :pct3_id')
  229.                    ->setParameter('pct3_id'$searchData['category_id3']);
  230.                 $in_category3 true;
  231.             }
  232.             if (!empty($searchData['category_id4']) && $searchData['category_id4']) {
  233.                 $qb->innerJoin('p.ProductCategories''pct9')
  234.                    ->addSelect("CASE WHEN pct9.recommend_rank IS NULL THEN 0 ELSE pct9.recommend_rank END AS HIDDEN recommend_rank4")
  235.                    ->andWhere('pct9.Category = :pct4_id')
  236.                    ->setParameter('pct4_id'$searchData['category_id4']);
  237.                 $in_category4 true;
  238.             }
  239.             if (!empty($searchData['category_id5']) && $searchData['category_id5']) {
  240.                 $qb->innerJoin('p.ProductCategories''pct10')
  241.                    ->addSelect("CASE WHEN pct10.recommend_rank IS NULL THEN 0 ELSE pct10.recommend_rank END AS HIDDEN recommend_rank5")
  242.                    ->andWhere('pct10.Category = :pct5_id')
  243.                    ->setParameter('pct5_id'$searchData['category_id5']);
  244.                 $in_category5 true;
  245.             }
  246.             if ($in_category1) {
  247.                 $qb->orderBy('recommend_rank1''DESC');
  248.             }
  249.             if ($in_category2) {
  250.                 $qb->addOrderBy('recommend_rank2''DESC');
  251.             }
  252.             if ($in_category3) {
  253.                 $qb->addOrderBy('recommend_rank3''DESC');
  254.             }
  255.             if ($in_category4) {
  256.                 $qb->addOrderBy('recommend_rank4''DESC');
  257.             }
  258.             if ($in_category5) {
  259.                $qb->addOrderBy('recommend_rank5''DESC');
  260.             }
  261.             $qb->addOrderBy('p.id''DESC');
  262. // 2025.02.27 end
  263.         }
  264.         return $this->queries->customize(QueryKey::PRODUCT_SEARCH$qb$searchData);
  265.     }
  266.     /**
  267.      * get query builder.
  268.      *
  269.      * @param array{
  270.      *         id?:string|int|null,
  271.      *         category_id?:Category,
  272.      *         status?:ProductStatus[],
  273.      *         link_status?:ProductStatus[],
  274.      *         stock_status?:int,
  275.      *         stock?:ProductStock::IN_STOCK|ProductStock::OUT_OF_STOCK,
  276.      *         tag_id?:Tag,
  277.      *         create_datetime_start?:\DateTime,
  278.      *         create_datetime_end?:\DateTime,
  279.      *         create_date_start?:\DateTime,
  280.      *         create_date_end?:\DateTime,
  281.      *         update_datetime_start?:\DateTime,
  282.      *         update_datetime_end?:\DateTime,
  283.      *         update_date_start?:\DateTime,
  284.      *         update_date_end?:\DateTime,
  285.      *         sortkey?:string,
  286.      *         sorttype?:string
  287.      *     } $searchData
  288.      *
  289.      * @return \Doctrine\ORM\QueryBuilder
  290.      */
  291.     public function getQueryBuilderBySearchDataForAdmin($searchData)
  292.     {
  293.         $qb $this->createQueryBuilder('p')
  294.             ->addSelect('pc''pi''tr''ps')
  295.             ->innerJoin('p.ProductClasses''pc')
  296.             ->leftJoin('p.ProductImage''pi')
  297.             ->leftJoin('pc.TaxRule''tr')
  298.             ->leftJoin('pc.ProductStock''ps')
  299.             ->andWhere('pc.visible = :visible')
  300.             ->setParameter('visible'true);
  301.         // id
  302.         if (isset($searchData['id']) && StringUtil::isNotBlank($searchData['id'])) {
  303.             $id preg_match('/^\d{0,10}$/'$searchData['id']) ? $searchData['id'] : null;
  304.             if ($id && $id '2147483647' && $this->isPostgreSQL()) {
  305.                 $id null;
  306.             }
  307.             $qb
  308.                 ->andWhere('p.id = :id OR p.name LIKE :likeid OR pc.code LIKE :likeid')
  309.                 ->setParameter('id'$id)
  310.                 ->setParameter('likeid''%'.str_replace(['%''_'], ['\\%''\\_'], $searchData['id']).'%');
  311.         }
  312.         // code
  313.         /*
  314.         if (!empty($searchData['code']) && $searchData['code']) {
  315.             $qb
  316.                 ->innerJoin('p.ProductClasses', 'pc')
  317.                 ->andWhere('pc.code LIKE :code')
  318.                 ->setParameter('code', '%' . $searchData['code'] . '%');
  319.         }
  320.         // name
  321.         if (!empty($searchData['name']) && $searchData['name']) {
  322.             $keywords = preg_split('/[\s ]+/u', $searchData['name'], -1, PREG_SPLIT_NO_EMPTY);
  323.             foreach ($keywords as $keyword) {
  324.                 $qb
  325.                     ->andWhere('p.name LIKE :name')
  326.                     ->setParameter('name', '%' . $keyword . '%');
  327.             }
  328.         }
  329.        */
  330.         // category
  331.         if (!empty($searchData['category_id']) && $searchData['category_id']) {
  332.             $Categories $searchData['category_id']->getSelfAndDescendants();
  333.             if ($Categories) {
  334.                 $qb
  335.                     ->innerJoin('p.ProductCategories''pct')
  336.                     ->innerJoin('pct.Category''c')
  337.                     ->andWhere($qb->expr()->in('pct.Category'':Categories'))
  338.                     ->setParameter('Categories'$Categories);
  339.             }
  340.         }
  341.         // status
  342.         if (!empty($searchData['status']) && $searchData['status']) {
  343.             $qb
  344.                 ->andWhere($qb->expr()->in('p.Status'':Status'))
  345.                 ->setParameter('Status'$searchData['status']);
  346.         }
  347.         // link_status
  348.         if (isset($searchData['link_status']) && !empty($searchData['link_status'])) {
  349.             $qb
  350.                 ->andWhere($qb->expr()->in('p.Status'':Status'))
  351.                 ->setParameter('Status'$searchData['link_status']);
  352.         }
  353.         // stock status
  354.         if (isset($searchData['stock_status'])) {
  355.             $qb
  356.                 ->andWhere('pc.stock_unlimited = :StockUnlimited AND pc.stock = 0')
  357.                 ->setParameter('StockUnlimited'$searchData['stock_status']);
  358.         }
  359.         // stock status
  360.         if (isset($searchData['stock']) && !empty($searchData['stock'])) {
  361.             switch ($searchData['stock']) {
  362.                 case [ProductStock::IN_STOCK]:
  363.                     $qb->andWhere('pc.stock_unlimited = true OR pc.stock > 0');
  364.                     break;
  365.                 case [ProductStock::OUT_OF_STOCK]:
  366.                     $qb->andWhere('pc.stock_unlimited = false AND pc.stock <= 0');
  367.                     break;
  368.                 default:
  369.                     // 共に選択された場合は全権該当するので検索条件に含めない
  370.             }
  371.         }
  372.         // tag
  373.         if (!empty($searchData['tag_id']) && $searchData['tag_id']) {
  374.             $qb
  375.                 ->innerJoin('p.ProductTag''pt')
  376.                 ->andWhere('pt.Tag = :tag_id')
  377.                 ->setParameter('tag_id'$searchData['tag_id']);
  378.         }
  379.         // crate_date
  380.         if (!empty($searchData['create_datetime_start']) && $searchData['create_datetime_start']) {
  381.             $date $searchData['create_datetime_start'];
  382.             $qb
  383.                 ->andWhere('p.create_date >= :create_date_start')
  384.                 ->setParameter('create_date_start'$date);
  385.         } elseif (!empty($searchData['create_date_start']) && $searchData['create_date_start']) {
  386.             $date $searchData['create_date_start'];
  387.             $qb
  388.                 ->andWhere('p.create_date >= :create_date_start')
  389.                 ->setParameter('create_date_start'$date);
  390.         }
  391.         if (!empty($searchData['create_datetime_end']) && $searchData['create_datetime_end']) {
  392.             $date $searchData['create_datetime_end'];
  393.             $qb
  394.                 ->andWhere('p.create_date < :create_date_end')
  395.                 ->setParameter('create_date_end'$date);
  396.         } elseif (!empty($searchData['create_date_end']) && $searchData['create_date_end']) {
  397.             $date = clone $searchData['create_date_end'];
  398.             $date $date
  399.                 ->modify('+1 days');
  400.             $qb
  401.                 ->andWhere('p.create_date < :create_date_end')
  402.                 ->setParameter('create_date_end'$date);
  403.         }
  404.         // update_date
  405.         if (!empty($searchData['update_datetime_start']) && $searchData['update_datetime_start']) {
  406.             $date $searchData['update_datetime_start'];
  407.             $qb
  408.                 ->andWhere('p.update_date >= :update_date_start')
  409.                 ->setParameter('update_date_start'$date);
  410.         } elseif (!empty($searchData['update_date_start']) && $searchData['update_date_start']) {
  411.             $date $searchData['update_date_start'];
  412.             $qb
  413.                 ->andWhere('p.update_date >= :update_date_start')
  414.                 ->setParameter('update_date_start'$date);
  415.         }
  416.         if (!empty($searchData['update_datetime_end']) && $searchData['update_datetime_end']) {
  417.             $date $searchData['update_datetime_end'];
  418.             $qb
  419.                 ->andWhere('p.update_date < :update_date_end')
  420.                 ->setParameter('update_date_end'$date);
  421.         } elseif (!empty($searchData['update_date_end']) && $searchData['update_date_end']) {
  422.             $date = clone $searchData['update_date_end'];
  423.             $date $date
  424.                 ->modify('+1 days');
  425.             $qb
  426.                 ->andWhere('p.update_date < :update_date_end')
  427.                 ->setParameter('update_date_end'$date);
  428.         }
  429.         // Order By
  430.         if (isset($searchData['sortkey']) && !empty($searchData['sortkey'])) {
  431.             $sortOrder = (isset($searchData['sorttype']) && $searchData['sorttype'] == 'a') ? 'ASC' 'DESC';
  432.             $qb->orderBy(self::COLUMNS[$searchData['sortkey']], $sortOrder);
  433.             $qb->addOrderBy('p.update_date''DESC');
  434.             $qb->addOrderBy('p.id''DESC');
  435.         } else {
  436.             $qb->orderBy('p.update_date''DESC');
  437.             $qb->addOrderBy('p.id''DESC');
  438.         }
  439.         return $this->queries->customize(QueryKey::PRODUCT_SEARCH_ADMIN$qb$searchData);
  440.     }
  441. }