app/Customize/Repository/ProductRepository.php line 150

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 Customize\Repository;
  13. use Doctrine\Common\Collections\ArrayCollection;
  14. use Doctrine\Persistence\ManagerRegistry as RegistryInterface;
  15. use Eccube\Repository\AbstractRepository;
  16. use Eccube\Common\EccubeConfig;
  17. use Eccube\Doctrine\Query\Queries;
  18. use Eccube\Entity\Category;
  19. use Eccube\Entity\Master\ProductListMax;
  20. use Eccube\Entity\Master\ProductListOrderBy;
  21. use Eccube\Entity\Master\ProductStatus;
  22. use Eccube\Entity\Product;
  23. use Eccube\Entity\ProductStock;
  24. use Eccube\Entity\Tag;
  25. use Eccube\Util\StringUtil;
  26. use Eccube\Repository\QueryKey;
  27. /**
  28.  * ProductRepository
  29.  *
  30.  * This class was generated by the Doctrine ORM. Add your own custom
  31.  * repository methods below.
  32.  */
  33. class ProductRepository extends AbstractRepository
  34. {
  35.     /**
  36.      * @var Queries
  37.      */
  38.     protected $queries;
  39.     /**
  40.      * @var EccubeConfig
  41.      */
  42.     protected $eccubeConfig;
  43.     public const COLUMNS = [
  44.         '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',
  45.     ];
  46.     /**
  47.      * ProductRepository constructor.
  48.      *
  49.      * @param RegistryInterface $registry
  50.      * @param Queries $queries
  51.      * @param EccubeConfig $eccubeConfig
  52.      */
  53.     public function __construct(
  54.         RegistryInterface $registry,
  55.         Queries $queries,
  56.         EccubeConfig $eccubeConfig
  57.     ) {
  58.         parent::__construct($registryProduct::class);
  59.         $this->queries $queries;
  60.         $this->eccubeConfig $eccubeConfig;
  61.     }
  62.     /**
  63.      * Find the Product with sorted ClassCategories.
  64.      *
  65.      * @param integer $productId
  66.      *
  67.      * @return Product
  68.      */
  69.     public function findWithSortedClassCategories($productId)
  70.     {
  71.         $qb $this->createQueryBuilder('p');
  72.         $qb->addSelect(['pc''cc1''cc2''pi''pt'])
  73.             ->innerJoin('p.ProductClasses''pc')
  74.             ->leftJoin('pc.ClassCategory1''cc1')
  75.             ->leftJoin('pc.ClassCategory2''cc2')
  76.             ->leftJoin('p.ProductImage''pi')
  77.             ->leftJoin('p.ProductTag''pt')
  78.             ->where('p.id = :id')
  79.             ->andWhere('pc.visible = :visible')
  80.             ->setParameter('id'$productId)
  81.             ->setParameter('visible'true)
  82.             ->orderBy('cc1.sort_no''DESC')
  83.             ->addOrderBy('cc2.sort_no''DESC');
  84.         $product $qb
  85.             ->getQuery()
  86.             ->getSingleResult();
  87.         return $product;
  88.     }
  89.     /**
  90.      * Find the Products with sorted ClassCategories.
  91.      *
  92.      * @param array $ids Product in ids
  93.      * @param string $indexBy The index for the from.
  94.      *
  95.      * @return ArrayCollection|array
  96.      */
  97.     public function findProductsWithSortedClassCategories(array $ids$indexBy null)
  98.     {
  99.         if (count($ids) < 1) {
  100.             return [];
  101.         }
  102.         $qb $this->createQueryBuilder('p'$indexBy);
  103.         $qb->addSelect(['pc''cc1''cc2''pi''pt''tr''ps'])
  104.             ->innerJoin('p.ProductClasses''pc')
  105.             // XXX Joined 'TaxRule' and 'ProductStock' to prevent lazy loading
  106.             ->leftJoin('pc.TaxRule''tr')
  107.             ->innerJoin('pc.ProductStock''ps')
  108.             ->leftJoin('pc.ClassCategory1''cc1')
  109.             ->leftJoin('pc.ClassCategory2''cc2')
  110.             ->leftJoin('p.ProductImage''pi')
  111.             ->leftJoin('p.ProductTag''pt')
  112.             ->where($qb->expr()->in('p.id'$ids))
  113.             ->andWhere('pc.visible = :visible')
  114.             ->setParameter('visible'true)
  115.             ->orderBy('cc1.sort_no''DESC')
  116.             ->addOrderBy('cc2.sort_no''DESC');
  117.         $products $qb
  118.             ->getQuery()
  119.             ->useResultCache(true$this->eccubeConfig['eccube_result_cache_lifetime_short'])
  120.             ->getResult();
  121.         return $products;
  122.     }
  123.     /**
  124.      * get query builder.
  125.      *
  126.      * @param array{
  127.      *         category_id?:Category,
  128.      *         name?:string,
  129.      *         pageno?:string,
  130.      *         disp_number?:ProductListMax,
  131.      *         orderby?:ProductListOrderBy
  132.      *     } $searchData
  133.      *
  134.      * @return \Doctrine\ORM\QueryBuilder
  135.      */
  136.     public function getQueryBuilderBySearchData($searchData)
  137.     {
  138.         $productFieldContentClass \Plugin\ProductField\Entity\ProductFieldContent::class;
  139.         $productClassClass \Eccube\Entity\ProductClass::class;
  140.         // ===== Phase 1: カテゴリ・名前・メーカーに合致する公開商品IDを収集 =====
  141.         $preQb $this->createQueryBuilder('p_pre')
  142.             ->select('DISTINCT p_pre.id')
  143.             ->andWhere('p_pre.Status = 1');
  144.         if (!empty($searchData['category_id']) && $searchData['category_id']) {
  145.             $Categories $searchData['category_id']->getSelfAndDescendants();
  146.             if ($Categories) {
  147.                 $preQb
  148.                     ->innerJoin('p_pre.ProductCategories''pct_pre')
  149.                     ->andWhere($preQb->expr()->in('pct_pre.Category'':cat_pre'))
  150.                     ->setParameter('cat_pre'$Categories);
  151.             }
  152.         }
  153.         if (!empty($searchData['maker_id']) && $searchData['maker_id']) {
  154.             $preQb
  155.                 ->innerJoin('p_pre.Maker''pm_pre')
  156.                 ->andWhere('pm_pre.id = :maker_id_pre')
  157.                 ->setParameter('maker_id_pre'$searchData['maker_id']);
  158.         }
  159.         if (isset($searchData['name']) && StringUtil::isNotBlank($searchData['name'])) {
  160.             $keywords preg_split('/[\s ]+/u'str_replace(['%''_'], ['\\%''\\_'], $searchData['name']), -1PREG_SPLIT_NO_EMPTY);
  161.             foreach ($keywords as $index => $keyword) {
  162.                 $key sprintf('kw_pre_%s'$index);
  163.                 $preQb
  164.                     ->andWhere(sprintf('NORMALIZE(p_pre.name) LIKE NORMALIZE(:%s) OR NORMALIZE(p_pre.search_word) LIKE NORMALIZE(:%s) OR EXISTS (SELECT wpc_pre%d FROM \Eccube\Entity\ProductClass wpc_pre%d WHERE p_pre = wpc_pre%d.Product AND NORMALIZE(wpc_pre%d.code) LIKE NORMALIZE(:%s))',
  165.                         $key$key$index$index$index$index$key))
  166.                     ->setParameter($key'%'.$keyword.'%');
  167.             }
  168.         }
  169.         $preRows $preQb->getQuery()->getArrayResult();
  170.         $matchingIds array_column($preRows'id');
  171.         if (empty($matchingIds)) {
  172.             $qb $this->createQueryBuilder('p')
  173.                 ->andWhere('1 = 0');
  174.             return $this->queries->customize(QueryKey::PRODUCT_SEARCH$qb$searchData);
  175.         }
  176.         // ===== Phase 2: matchingIds の中で related_keyword グループの代表を選ぶ =====
  177.         // matchingIds 内の各商品の related_keyword を取得
  178.         $kwQb $this->getEntityManager()->createQueryBuilder();
  179.         $kwQb
  180.             ->select('pfc_m.product_id, pfc_m.meta_content AS keyword')
  181.             ->from($productFieldContentClass'pfc_m')
  182.             ->where('pfc_m.meta_key = :mk_m')
  183.             ->andWhere($kwQb->expr()->in('pfc_m.product_id'':matching_ids'))
  184.             ->setParameter('mk_m''related_keyword')
  185.             ->setParameter('matching_ids'$matchingIds);
  186.         $kwRows $kwQb->getQuery()->getArrayResult();
  187.         // product_id => keyword のマップ
  188.         $productKeywordMap = [];
  189.         foreach ($kwRows as $row) {
  190.             $productKeywordMap[$row['product_id']] = $row['keyword'];
  191.         }
  192.         $idsWithKeyword array_keys($productKeywordMap);
  193.         $idsWithoutKeyword array_values(array_diff($matchingIds$idsWithKeyword));
  194.         // keyword => [product_ids] のグループ
  195.         $keywordGroups = [];
  196.         foreach ($productKeywordMap as $pid => $kw) {
  197.             $keywordGroups[$kw][] = $pid;
  198.         }
  199.         // 各グループから最安値・最小IDの代表を選ぶ
  200.         $representativeIds = [];
  201.         if (!empty($idsWithKeyword)) {
  202.             $priceQb $this->getEntityManager()->createQueryBuilder();
  203.             $priceQb
  204.                 ->select('IDENTITY(pc_p.Product) as product_id, MIN(pc_p.price02) as min_price')
  205.                 ->from($productClassClass'pc_p')
  206.                 ->where($priceQb->expr()->in('pc_p.Product'':ids_kw'))
  207.                 ->andWhere('pc_p.visible = true')
  208.                 ->groupBy('pc_p.Product')
  209.                 ->setParameter('ids_kw'$idsWithKeyword);
  210.             $priceRows $priceQb->getQuery()->getArrayResult();
  211.             $productPriceMap = [];
  212.             foreach ($priceRows as $row) {
  213.                 $productPriceMap[$row['product_id']] = (int) $row['min_price'];
  214.             }
  215.             foreach ($keywordGroups as $kw => $pids) {
  216.                 $minPrice PHP_INT_MAX;
  217.                 $repId null;
  218.                 foreach ($pids as $pid) {
  219.                     $price $productPriceMap[$pid] ?? PHP_INT_MAX;
  220.                     if ($price $minPrice || ($price === $minPrice && ($repId === null || $pid $repId))) {
  221.                         $minPrice $price;
  222.                         $repId $pid;
  223.                     }
  224.                 }
  225.                 if ($repId !== null) {
  226.                     $representativeIds[] = $repId;
  227.                 }
  228.             }
  229.         }
  230.         // グループ代表 + グループ未所属 = 表示対象
  231.         $visibleIds array_merge($representativeIds$idsWithoutKeyword);
  232.         if (empty($visibleIds)) {
  233.             $qb $this->createQueryBuilder('p')
  234.                 ->andWhere('1 = 0');
  235.             return $this->queries->customize(QueryKey::PRODUCT_SEARCH$qb$searchData);
  236.         }
  237.         // ===== Phase 3: 最終クエリを構築(visibleIds + 並び順 + 価格帯) =====
  238.         $qb $this->createQueryBuilder('p')
  239.             ->andWhere('p.Status = 1');
  240.         $qb->andWhere($qb->expr()->in('p.id'':visible_ids'))
  241.            ->setParameter('visible_ids'$visibleIds);
  242.         // category(並び順のJOIN用)
  243.         $categoryJoin false;
  244.         if (!empty($searchData['category_id']) && $searchData['category_id']) {
  245.             $Categories $searchData['category_id']->getSelfAndDescendants();
  246.             if ($Categories) {
  247.                 $qb
  248.                     ->innerJoin('p.ProductCategories''pct')
  249.                     ->innerJoin('pct.Category''c')
  250.                     ->andWhere($qb->expr()->in('pct.Category'':Categories'))
  251.                     ->setParameter('Categories'$Categories);
  252.                 $categoryJoin true;
  253.             }
  254.         }
  255.         // maker
  256.         if (!empty($searchData['maker_id']) && $searchData['maker_id']) {
  257.             $qb
  258.                 ->innerJoin('p.Maker''pm')
  259.                 ->andWhere('pm.id = :maker_id')
  260.                 ->setParameter('maker_id'$searchData['maker_id']);
  261.         }
  262.         // name
  263.         if (isset($searchData['name']) && StringUtil::isNotBlank($searchData['name'])) {
  264.             $keywords preg_split('/[\s ]+/u'str_replace(['%''_'], ['\\%''\\_'], $searchData['name']), -1PREG_SPLIT_NO_EMPTY);
  265.             foreach ($keywords as $index => $keyword) {
  266.                 $key sprintf('keyword%s'$index);
  267.                 $qb
  268.                     ->andWhere(sprintf('NORMALIZE(p.name) LIKE NORMALIZE(:%s) OR
  269.                         NORMALIZE(p.search_word) LIKE NORMALIZE(:%s) OR
  270.                         EXISTS (SELECT wpc%d FROM \Eccube\Entity\ProductClass wpc%d WHERE p = wpc%d.Product AND NORMALIZE(wpc%d.code) LIKE NORMALIZE(:%s))',
  271.                         $key$key$index$index$index$index$key))
  272.                     ->setParameter($key'%'.$keyword.'%');
  273.             }
  274.         }
  275.         // 価格低い順
  276.         $config $this->eccubeConfig;
  277.         if (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_price_lower']) {
  278.             // @see http://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html
  279.             $qb->addSelect('MIN(pc.price02) as HIDDEN price02_min');
  280.             $qb->innerJoin('p.ProductClasses''pc');
  281.             $qb->andWhere('pc.visible = true');
  282.             $qb->groupBy('p.id');
  283.             $qb->orderBy('price02_min''ASC');
  284.             $qb->addOrderBy('p.id''DESC');
  285.         // 価格高い順
  286.         } elseif (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_price_higher']) {
  287.             $qb->addSelect('MAX(pc.price02) as HIDDEN price02_max');
  288.             $qb->innerJoin('p.ProductClasses''pc');
  289.             $qb->andWhere('pc.visible = true');
  290.             $qb->groupBy('p.id');
  291.             $qb->orderBy('price02_max''DESC');
  292.             $qb->addOrderBy('p.id''DESC');
  293.         // 新着順
  294.         } elseif (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_newer']) {
  295.             // 在庫切れ商品非表示の設定が有効時対応
  296.             // @see https://github.com/EC-CUBE/ec-cube/issues/1998
  297.             if ($this->getEntityManager()->getFilters()->isEnabled('option_nostock_hidden') == true) {
  298.                 $qb->innerJoin('p.ProductClasses''pc');
  299.                 $qb->andWhere('pc.visible = true');
  300.             }
  301.             $qb->orderBy('p.create_date''DESC');
  302.             $qb->addOrderBy('p.id''DESC');
  303.         } else {
  304.             if ($categoryJoin === false) {
  305.                 $qb
  306.                     ->leftJoin('p.ProductCategories''pct')
  307.                     ->leftJoin('pct.Category''c');
  308.             }
  309.             $qb
  310.                 ->addOrderBy('p.id''ASC');
  311.         }
  312.         // 価格帯の絞り込み
  313.         // price_min / price_max はコントローラから $searchData に追加される
  314.         // pc(ProductClasses)がまだJOINされていない場合はここでJOINする
  315.         $pcJoined false;
  316.         foreach ($qb->getDQLPart('join') as $joins) {
  317.             foreach ($joins as $join) {
  318.                 if ($join->getAlias() === 'pc') {
  319.                     $pcJoined true;
  320.                     break 2;
  321.                 }
  322.             }
  323.         }
  324.         if (!empty($searchData['price_min']) || !empty($searchData['price_max'])) {
  325.             if (!$pcJoined) {
  326.                 $qb->innerJoin('p.ProductClasses''pc')
  327.                    ->andWhere('pc.visible = true');
  328.             }
  329.             // price02 は税抜価格カラム(DBの実カラム名)
  330.             // 税込換算: price02 * (1 + tax_rate/100)
  331.             // ただし税率はTaxRuleに依存するため、サイトの消費税率(10%)を固定で使用
  332.             // 税込入力値 → 税抜換算して比較(÷1.1)
  333.             if (!empty($searchData['price_min'])) {
  334.                 $priceMinExTax = (int) round((int) $searchData['price_min'] / 1.1);
  335.                 $qb->andWhere('pc.price02 >= :price_min')
  336.                    ->setParameter('price_min'$priceMinExTax);
  337.             }
  338.             if (!empty($searchData['price_max'])) {
  339.                 $priceMaxExTax = (int) round((int) $searchData['price_max'] / 1.1);
  340.                 $qb->andWhere('pc.price02 <= :price_max')
  341.                    ->setParameter('price_max'$priceMaxExTax);
  342.             }
  343.             // GROUP BYが未設定の場合は追加
  344.             $groupBy $qb->getDQLPart('groupBy');
  345.             if (empty($groupBy)) {
  346.                 $qb->groupBy('p.id');
  347.             }
  348.         }
  349.         return $this->queries->customize(QueryKey::PRODUCT_SEARCH$qb$searchData);
  350.     }
  351.     /**
  352.      * get query builder.
  353.      *
  354.      * @param array{
  355.      *         id?:string|int|null,
  356.      *         category_id?:Category,
  357.      *         status?:ProductStatus[],
  358.      *         link_status?:ProductStatus[],
  359.      *         stock_status?:int,
  360.      *         stock?:ProductStock::IN_STOCK|ProductStock::OUT_OF_STOCK,
  361.      *         tag_id?:Tag,
  362.      *         create_datetime_start?:\DateTime,
  363.      *         create_datetime_end?:\DateTime,
  364.      *         create_date_start?:\DateTime,
  365.      *         create_date_end?:\DateTime,
  366.      *         update_datetime_start?:\DateTime,
  367.      *         update_datetime_end?:\DateTime,
  368.      *         update_date_start?:\DateTime,
  369.      *         update_date_end?:\DateTime,
  370.      *         sortkey?:string,
  371.      *         sorttype?:string
  372.      *     } $searchData
  373.      *
  374.      * @return \Doctrine\ORM\QueryBuilder
  375.      */
  376.     public function getQueryBuilderBySearchDataForAdmin($searchData)
  377.     {
  378.         $qb $this->createQueryBuilder('p')
  379.             ->addSelect('pc''pi''tr''ps')
  380.             ->innerJoin('p.ProductClasses''pc')
  381.             ->leftJoin('p.ProductImage''pi')
  382.             ->leftJoin('pc.TaxRule''tr')
  383.             ->leftJoin('pc.ProductStock''ps')
  384.             ->andWhere('pc.visible = :visible')
  385.             ->setParameter('visible'true);
  386.         // id
  387.         if (isset($searchData['id']) && StringUtil::isNotBlank($searchData['id'])) {
  388.             $id preg_match('/^\d{0,10}$/'$searchData['id']) ? $searchData['id'] : null;
  389.             if ($id && $id '2147483647' && $this->isPostgreSQL()) {
  390.                 $id null;
  391.             }
  392.             $qb
  393.                 ->andWhere('p.id = :id OR p.name LIKE :likeid OR p.note LIKE :likeid OR pc.code LIKE :likeid')
  394.                 ->setParameter('id'$id)
  395.                 ->setParameter('likeid''%'.str_replace(['%''_'], ['\\%''\\_'], $searchData['id']).'%');
  396.         }
  397.         // code
  398.         /*
  399.         if (!empty($searchData['code']) && $searchData['code']) {
  400.             $qb
  401.                 ->innerJoin('p.ProductClasses', 'pc')
  402.                 ->andWhere('pc.code LIKE :code')
  403.                 ->setParameter('code', '%' . $searchData['code'] . '%');
  404.         }
  405.         // name
  406.         if (!empty($searchData['name']) && $searchData['name']) {
  407.             $keywords = preg_split('/[\s ]+/u', $searchData['name'], -1, PREG_SPLIT_NO_EMPTY);
  408.             foreach ($keywords as $keyword) {
  409.                 $qb
  410.                     ->andWhere('p.name LIKE :name')
  411.                     ->setParameter('name', '%' . $keyword . '%');
  412.             }
  413.         }
  414.        */
  415.         // category
  416.         if (!empty($searchData['category_id']) && $searchData['category_id']) {
  417.             $Categories $searchData['category_id']->getSelfAndDescendants();
  418.             if ($Categories) {
  419.                 $qb
  420.                     ->innerJoin('p.ProductCategories''pct')
  421.                     ->innerJoin('pct.Category''c')
  422.                     ->andWhere($qb->expr()->in('pct.Category'':Categories'))
  423.                     ->setParameter('Categories'$Categories);
  424.             }
  425.         }
  426.         // status
  427.         if (!empty($searchData['status']) && $searchData['status']) {
  428.             $qb
  429.                 ->andWhere($qb->expr()->in('p.Status'':Status'))
  430.                 ->setParameter('Status'$searchData['status']);
  431.         }
  432.         // link_status
  433.         if (isset($searchData['link_status']) && !empty($searchData['link_status'])) {
  434.             $qb
  435.                 ->andWhere($qb->expr()->in('p.Status'':Status'))
  436.                 ->setParameter('Status'$searchData['link_status']);
  437.         }
  438.         // stock status
  439.         if (isset($searchData['stock_status'])) {
  440.             $qb
  441.                 ->andWhere('pc.stock_unlimited = :StockUnlimited AND pc.stock = 0')
  442.                 ->setParameter('StockUnlimited'$searchData['stock_status']);
  443.         }
  444.         // stock status
  445.         if (isset($searchData['stock']) && !empty($searchData['stock'])) {
  446.             switch ($searchData['stock']) {
  447.                 case [ProductStock::IN_STOCK]:
  448.                     $qb->andWhere('pc.stock_unlimited = true OR pc.stock > 0');
  449.                     break;
  450.                 case [ProductStock::OUT_OF_STOCK]:
  451.                     $qb->andWhere('pc.stock_unlimited = false AND pc.stock <= 0');
  452.                     break;
  453.                 default:
  454.                     // 共に選択された場合は全権該当するので検索条件に含めない
  455.             }
  456.         }
  457.         // tag
  458.         if (!empty($searchData['tag_id']) && $searchData['tag_id']) {
  459.             $qb
  460.                 ->innerJoin('p.ProductTag''pt')
  461.                 ->andWhere('pt.Tag = :tag_id')
  462.                 ->setParameter('tag_id'$searchData['tag_id']);
  463.         }
  464.         // crate_date
  465.         if (!empty($searchData['create_datetime_start']) && $searchData['create_datetime_start']) {
  466.             $date $searchData['create_datetime_start'];
  467.             $qb
  468.                 ->andWhere('p.create_date >= :create_date_start')
  469.                 ->setParameter('create_date_start'$date);
  470.         } elseif (!empty($searchData['create_date_start']) && $searchData['create_date_start']) {
  471.             $date $searchData['create_date_start'];
  472.             $qb
  473.                 ->andWhere('p.create_date >= :create_date_start')
  474.                 ->setParameter('create_date_start'$date);
  475.         }
  476.         if (!empty($searchData['create_datetime_end']) && $searchData['create_datetime_end']) {
  477.             $date $searchData['create_datetime_end'];
  478.             $qb
  479.                 ->andWhere('p.create_date < :create_date_end')
  480.                 ->setParameter('create_date_end'$date);
  481.         } elseif (!empty($searchData['create_date_end']) && $searchData['create_date_end']) {
  482.             $date = clone $searchData['create_date_end'];
  483.             $date $date
  484.                 ->modify('+1 days');
  485.             $qb
  486.                 ->andWhere('p.create_date < :create_date_end')
  487.                 ->setParameter('create_date_end'$date);
  488.         }
  489.         // update_date
  490.         if (!empty($searchData['update_datetime_start']) && $searchData['update_datetime_start']) {
  491.             $date $searchData['update_datetime_start'];
  492.             $qb
  493.                 ->andWhere('p.update_date >= :update_date_start')
  494.                 ->setParameter('update_date_start'$date);
  495.         } elseif (!empty($searchData['update_date_start']) && $searchData['update_date_start']) {
  496.             $date $searchData['update_date_start'];
  497.             $qb
  498.                 ->andWhere('p.update_date >= :update_date_start')
  499.                 ->setParameter('update_date_start'$date);
  500.         }
  501.         if (!empty($searchData['update_datetime_end']) && $searchData['update_datetime_end']) {
  502.             $date $searchData['update_datetime_end'];
  503.             $qb
  504.                 ->andWhere('p.update_date < :update_date_end')
  505.                 ->setParameter('update_date_end'$date);
  506.         } elseif (!empty($searchData['update_date_end']) && $searchData['update_date_end']) {
  507.             $date = clone $searchData['update_date_end'];
  508.             $date $date
  509.                 ->modify('+1 days');
  510.             $qb
  511.                 ->andWhere('p.update_date < :update_date_end')
  512.                 ->setParameter('update_date_end'$date);
  513.         }
  514.         // Order By
  515.         if (isset($searchData['sortkey']) && !empty($searchData['sortkey'])) {
  516.             $sortOrder = (isset($searchData['sorttype']) && $searchData['sorttype'] == 'a') ? 'ASC' 'DESC';
  517.             $qb->orderBy(self::COLUMNS[$searchData['sortkey']], $sortOrder);
  518.             $qb->addOrderBy('p.update_date''DESC');
  519.             $qb->addOrderBy('p.id''DESC');
  520.         } else {
  521.             $qb->orderBy('p.update_date''DESC');
  522.             $qb->addOrderBy('p.id''DESC');
  523.         }
  524.         return $this->queries->customize(QueryKey::PRODUCT_SEARCH_ADMIN$qb$searchData);
  525.     }
  526.     public function findByLikeName($value$categories)
  527.     {
  528.         $builder $this->createQueryBuilder('p');
  529.         
  530.         return $builder
  531.             ->innerJoin('p.ProductCategories''pct')
  532.             ->innerJoin('pct.Category''c')
  533.             ->where($builder->expr()->like('p.name'':name'))  // LIKE検索条件
  534.             ->andWhere($builder->expr()->in('pct.Category'':Categories'))
  535.             ->setParameter('name''%' $value '%')          // ワイルドカードを含める
  536.             ->setParameter('Categories'$categories)
  537.             ->getQuery()
  538.             ->getResult();
  539.     }
  540. }