src/Eccube/Repository/ProductRepository.php line 107

Open in your IDE?
  1. <?php
  2. namespace Eccube\Repository;
  3. use Doctrine\Common\Collections\ArrayCollection;
  4. use Doctrine\Persistence\ManagerRegistry as RegistryInterface;
  5. use Eccube\Common\EccubeConfig;
  6. use Eccube\Doctrine\Query\Queries;
  7. use Eccube\Entity\Category;
  8. use Eccube\Entity\Master\ProductListMax;
  9. use Eccube\Entity\Master\ProductListOrderBy;
  10. use Eccube\Entity\Master\ProductStatus;
  11. use Eccube\Entity\Product;
  12. use Eccube\Entity\ProductStock;
  13. use Eccube\Entity\Tag;
  14. use Eccube\Util\StringUtil;
  15. use Plugin\Recommend42\Entity\RecommendProduct;
  16. /**
  17.  * ProductRepository
  18.  *
  19.  * This class was generated by the Doctrine ORM. Add your own custom
  20.  * repository methods below.
  21.  */
  22. class ProductRepository extends AbstractRepository
  23. {
  24.   /**
  25.    * @var Queries
  26.    */
  27.   protected $queries;
  28.   /**
  29.    * @var EccubeConfig
  30.    */
  31.   protected $eccubeConfig;
  32.   public const COLUMNS = [
  33.     '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''price' => 'pc.price02',
  34.     // 'stock_sum' は在庫合計での並び替え専用キー(getQueryBuilderBySearchDataForAdmin内で個別処理)。
  35.     // ここでの値自体は使われないが、ProductController::index()のwrap-queries判定(COLUMNS[$sortKey]が空でないか)を満たすために定義が必要。
  36.     'stock_sum' => 'pc.stock',
  37.   ];
  38.   /**
  39.    * ProductRepository constructor.
  40.    *
  41.    * @param RegistryInterface $registry
  42.    * @param Queries $queries
  43.    * @param EccubeConfig $eccubeConfig
  44.    */
  45.   public function __construct(
  46.     RegistryInterface $registry,
  47.     Queries           $queries,
  48.     EccubeConfig      $eccubeConfig
  49.   )
  50.   {
  51.     parent::__construct($registryProduct::class);
  52.     $this->queries $queries;
  53.     $this->eccubeConfig $eccubeConfig;
  54.   }
  55.   /**
  56.    * 子カテゴリー一覧をハッシュで取得
  57.    * @param integer $p_id
  58.    * @return hash
  59.    */
  60.   public function getHashStatus()
  61.   {
  62.     $conn $this->getEntityManager()->getConnection();
  63.     $sql "SELECT id,product_status_id FROM dtb_product";
  64.     $stmt $conn->prepare($sql);
  65.     $resultSet $stmt->executeQuery();
  66.     $rows $resultSet->fetchAllAssociative();
  67.     $hash = array();
  68.     foreach ($rows as $row) {
  69.       $hash[$row["id"]] = $row["product_status_id"];
  70.     }
  71.     return $hash;
  72.   }
  73.   /**
  74.    * Find the Product with sorted ClassCategories.
  75.    *
  76.    * @param integer $productId
  77.    *
  78.    * @return Product
  79.    */
  80.   public function findWithSortedClassCategories($productId)
  81.   {
  82.     $qb $this->createQueryBuilder('p');
  83.     $qb->addSelect(['pc''cc1''cc2''pi''pt'])
  84.       ->innerJoin('p.ProductClasses''pc')
  85.       ->leftJoin('pc.ClassCategory1''cc1')
  86.       ->leftJoin('pc.ClassCategory2''cc2')
  87.       ->leftJoin('p.ProductImage''pi')
  88.       ->leftJoin('p.ProductTag''pt')
  89.       ->where('p.id = :id')
  90.       ->andWhere('pc.visible = :visible')
  91.       ->setParameter('id'$productId)
  92.       ->setParameter('visible'true)
  93.       ->orderBy('cc1.sort_no''DESC')
  94.       ->addOrderBy('cc2.sort_no''DESC');
  95.     $product $qb
  96.       ->getQuery()
  97.       ->getSingleResult();
  98.     return $product;
  99.   }
  100.   /**
  101.    * Find the Products with sorted ClassCategories.
  102.    *
  103.    * @param array $ids Product in ids
  104.    * @param string $indexBy The index for the from.
  105.    *
  106.    * @return ArrayCollection|array
  107.    */
  108.   public function findProductsWithSortedClassCategories(array $ids$indexBy null)
  109.   {
  110.     if (count($ids) < 1) {
  111.       return [];
  112.     }
  113.     $qb $this->createQueryBuilder('p'$indexBy);
  114.     $qb->addSelect(['pc''cc1''cc2''pi''pt''tr''ps'])
  115.       ->innerJoin('p.ProductClasses''pc')
  116.       // XXX Joined 'TaxRule' and 'ProductStock' to prevent lazy loading
  117.       ->leftJoin('pc.TaxRule''tr')
  118.       ->innerJoin('pc.ProductStock''ps')
  119.       ->leftJoin('pc.ClassCategory1''cc1')
  120.       ->leftJoin('pc.ClassCategory2''cc2')
  121.       ->leftJoin('p.ProductImage''pi')
  122.       ->leftJoin('p.ProductTag''pt')
  123.       ->where($qb->expr()->in('p.id'$ids))
  124.       ->andWhere('pc.visible = :visible')
  125.       ->setParameter('visible'true)
  126.       ->orderBy('cc1.sort_no''DESC')
  127.       ->addOrderBy('cc2.sort_no''DESC');
  128.     $products $qb
  129.       ->getQuery()
  130.       ->useResultCache(true$this->eccubeConfig['eccube_result_cache_lifetime_short'])
  131.       ->getResult();
  132.     return $products;
  133.   }
  134.   /**
  135.    * get query builder.
  136.    *
  137.    * @param array{
  138.    *         category_id?:Category,
  139.    *         name?:string,
  140.    *         pageno?:string,
  141.    *         disp_number?:ProductListMax,
  142.    *         orderby?:ProductListOrderBy
  143.    *     } $searchData
  144.    *
  145.    * @return \Doctrine\ORM\QueryBuilder
  146.    */
  147.   public function getQueryBuilderBySearchData($searchData)
  148.   {
  149.     $qb $this->createQueryBuilder('p')
  150.       ->andWhere('((p.Status = 1) OR (p.Status = 6) OR (p.Status = 8))')
  151.       ->andWhere('p.del_flg = 0');
  152.     // category
  153.     $categoryJoin false;
  154.     if (!empty($searchData['category_id']) && $searchData['category_id']) {
  155.       $Categories $searchData['category_id']->getSelfAndDescendants();
  156.       if ($Categories) {
  157.         $qb
  158.           ->innerJoin('p.ProductCategories''pct')
  159.           ->innerJoin('pct.Category''c')
  160.           ->andWhere($qb->expr()->in('pct.Category'':Categories'))
  161.           ->setParameter('Categories'$Categories);
  162.         $categoryJoin true;
  163.       }
  164.     }
  165.     // 複数のカテゴリーに対応
  166.     if (!empty($searchData['c_id']) and is_array($searchData['c_id']) and count($searchData['c_id']) > 0) {
  167.       dump($searchData);exit;
  168.     }
  169.     // name
  170.     if (isset($searchData['name']) && StringUtil::isNotBlank($searchData['name'])) {
  171.       $keywords preg_split('/[\s ]+/u'str_replace(['%''_'], ['\\%''\\_'], $searchData['name']), -1PREG_SPLIT_NO_EMPTY);
  172.       foreach ($keywords as $index => $keyword) {
  173.         $key sprintf('keyword%s'$index);
  174.         $qb
  175.           ->andWhere(sprintf('NORMALIZE(p.name) LIKE NORMALIZE(:%s) OR
  176.                         NORMALIZE(p.search_word) LIKE NORMALIZE(:%s) OR
  177.                         EXISTS (SELECT wpc%d FROM \Eccube\Entity\ProductClass wpc%d WHERE p = wpc%d.Product AND NORMALIZE(wpc%d.code) LIKE NORMALIZE(:%s))',
  178.             $key$key$index$index$index$index$key))
  179.           ->setParameter($key'%' $keyword '%');
  180.       }
  181.     }
  182.     // ============================================================
  183.     // 限定商品の除外処理
  184.     // ============================================================
  185.     // 1. カテゴリーベースの限定商品除外(カテゴリーID: 218)
  186.     // 限定カテゴリーの子カテゴリーに属する商品は除外
  187.     // ただし、限定カテゴリーを直接指定した場合は表示
  188.     $genteiCategoryId 218;
  189.     $isGenteiCategory false;
  190.     if (!empty($searchData['category_id'])) {
  191.       // 指定カテゴリーが限定カテゴリー(218)またはその子孫かチェック
  192.       $category $searchData['category_id'];
  193.       if ($category->getId() == $genteiCategoryId) {
  194.         $isGenteiCategory true;
  195.       } else {
  196.         // 親をたどって限定カテゴリーかチェック
  197.         $parent $category->getParent();
  198.         while ($parent) {
  199.           if ($parent->getId() == $genteiCategoryId) {
  200.             $isGenteiCategory true;
  201.             break;
  202.           }
  203.           $parent $parent->getParent();
  204.         }
  205.       }
  206.     }
  207.     if (!$isGenteiCategory) {
  208.       // 限定カテゴリー以外の場合、限定商品を除外
  209.       $genteiProductIds $this->getGenteiProductIds($genteiCategoryId);
  210.       if (!empty($genteiProductIds)) {
  211.         $qb->andWhere($qb->expr()->notIn('p.id'':genteiProductIds'))
  212.            ->setParameter('genteiProductIds'$genteiProductIds);
  213.       }
  214.     }
  215.     // 2. 拡張項目ベースの限定公開除外(column_id: 66)
  216.     // ProductPlusの限定公開URLに値がある商品は除外
  217.     $genteiUrlProductIds $this->getGenteiUrlProductIds();
  218.     if (!empty($genteiUrlProductIds)) {
  219.       $qb->andWhere($qb->expr()->notIn('p.id'':genteiUrlProductIds'))
  220.          ->setParameter('genteiUrlProductIds'$genteiUrlProductIds);
  221.     }
  222.     // Order By
  223.     // 価格低い順
  224.     $config $this->eccubeConfig;
  225.     if (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_price_lower']) {
  226.       // @see http://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html
  227.       $qb->addSelect('MIN(pc.price02) as HIDDEN price02_min');
  228.       $qb->innerJoin('p.ProductClasses''pc');
  229.       $qb->andWhere('pc.visible = true');
  230.       $qb->groupBy('p.id');
  231.       $qb->orderBy('price02_min''ASC');
  232.       $qb->addOrderBy('p.id''DESC');
  233.       // 価格高い順
  234.     } elseif (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_price_higher']) {
  235.       $qb->addSelect('MAX(pc.price02) as HIDDEN price02_max');
  236.       $qb->innerJoin('p.ProductClasses''pc');
  237.       $qb->andWhere('pc.visible = true');
  238.       $qb->groupBy('p.id');
  239.       $qb->orderBy('price02_max''DESC');
  240.       $qb->addOrderBy('p.id''DESC');
  241.       // 新着順
  242.     } elseif (!empty($searchData['orderby']) && $searchData['orderby']->getId() == $config['eccube_product_order_newer']) {
  243.       // 在庫切れ商品非表示の設定が有効時対応
  244.       // @see https://github.com/EC-CUBE/ec-cube/issues/1998
  245.       if ($this->getEntityManager()->getFilters()->isEnabled('option_nostock_hidden') == true) {
  246.         $qb->innerJoin('p.ProductClasses''pc');
  247.         $qb->andWhere('pc.visible = true');
  248.       }
  249.       $qb->orderBy('p.create_date''DESC');
  250.       $qb->addOrderBy('p.id''DESC');
  251.     } else {
  252.       if ($categoryJoin === false) {
  253.         $qb
  254.           ->leftJoin('p.ProductCategories''pct')
  255.           ->leftJoin('pct.Category''c');
  256.       }
  257.       $qb
  258.         ->addOrderBy('p.id''DESC');
  259.     }
  260.     //dump($qb->getQuery()->getSQL());exit;
  261.     //$all = $qb->getQuery()->getArrayResult();
  262.     //dump($all);exit;
  263.     return $this->queries->customize(QueryKey::PRODUCT_SEARCH$qb$searchData);
  264.   }
  265.   /**
  266.    * get query builder.
  267.    *
  268.    * @param array{
  269.    *         id?:string|int|null,
  270.    *         category_id?:Category,
  271.    *         status?:ProductStatus[],
  272.    *         link_status?:ProductStatus[],
  273.    *         stock_status?:int,
  274.    *         stock?:1|2|3|4,
  275.    *         tag_id?:Tag,
  276.    *         create_datetime_start?:\DateTime,
  277.    *         create_datetime_end?:\DateTime,
  278.    *         create_date_start?:\DateTime,
  279.    *         create_date_end?:\DateTime,
  280.    *         update_datetime_start?:\DateTime,
  281.    *         update_datetime_end?:\DateTime,
  282.    *         update_date_start?:\DateTime,
  283.    *         update_date_end?:\DateTime,
  284.    *         sortkey?:string,
  285.    *         sorttype?:string
  286.    *     } $searchData
  287.    *
  288.    * @return \Doctrine\ORM\QueryBuilder
  289.    */
  290.   public function getQueryBuilderBySearchDataForAdmin($searchData)
  291.   {
  292.     $qb $this->createQueryBuilder('p')
  293.       ->addSelect('pc''pi''tr''ps')
  294.       ->innerJoin('p.ProductClasses''pc')
  295.       ->leftJoin('p.ProductImage''pi')
  296.       ->leftJoin('pc.TaxRule''tr')
  297.       ->leftJoin('pc.ProductStock''ps')
  298.       ->andWhere('pc.visible = :visible')
  299.       ->setParameter('visible'true)
  300.       ->andWhere('p.del_flg = 0');
  301.     // id(商品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 OR p.search_word LIKE :likeid OR EXISTS (SELECT ppd.id FROM Plugin\ProductPlus42\Entity\ProductData ppd INNER JOIN Plugin\ProductPlus42\Entity\ProductDataDetail ppdd WITH ppdd.ProductData = ppd WHERE ppd.Product = p AND ppd.ProductItem = 2 AND ppdd.value LIKE :likeid)')
  309.         ->setParameter('id'$id)
  310.         ->setParameter('likeid''%' str_replace(['%''_'], ['\\%''\\_'], $searchData['id']) . '%');
  311.     }
  312.     // 在庫・お預かり・地金型コイン
  313.     // 1. 在庫    = コインパレスの在庫
  314.     // 2. お預かり = お客さまから預かっているコイン
  315.     // 3. 地金型コイン = カテゴリー地金型を含むコイン
  316.     if (!empty($searchData["custody_flg"]) and count($searchData["custody_flg"]) == 1) {
  317.       $custody_flg $searchData["custody_flg"][0];
  318.       if ($custody_flg == 1) {
  319.         $ids $this->getStorageCpIds();
  320.         $qb->andWhere($qb->expr()->in('p.id'$ids));
  321.       } elseif ($custody_flg == 2) {
  322.         $ids $this->getStorageCustIds();
  323.         $qb->andWhere($qb->expr()->in('p.id'$ids));
  324.       }elseif($custody_flg == 3){
  325.         $Categories = array();
  326.         $categoryRepository $this->getEntityManager()->getRepository(Category::class);
  327.         $Categories[] = $categoryRepository->find("5");
  328.         $qb->innerJoin('p.ProductCategories''pct2')
  329.           ->innerJoin('pct2.Category''c2')
  330.           ->andWhere($qb->expr()->in('pct2.Category'':Categories2'))
  331.           ->setParameter('Categories2'$Categories);
  332.       }
  333.     }
  334.     // category
  335.     if (!empty($searchData['category_id']) && $searchData['category_id']) {
  336.       $Categories $searchData['category_id']->getSelfAndDescendants();
  337.       if ($Categories) {
  338.         $qb
  339.           ->innerJoin('p.ProductCategories''pct')
  340.           ->innerJoin('pct.Category''c')
  341.           ->andWhere($qb->expr()->in('pct.Category'':Categories'))
  342.           ->setParameter('Categories'$Categories);
  343.       }
  344.     }
  345.     // status
  346.     if (!empty($searchData['status']) && $searchData['status']) {
  347.       $qb
  348.         ->andWhere($qb->expr()->in('p.Status'':Status'))
  349.         ->setParameter('Status'$searchData['status']);
  350.     }
  351.     // link_status
  352.     if (isset($searchData['link_status']) && !empty($searchData['link_status'])) {
  353.       $qb
  354.         ->andWhere($qb->expr()->in('p.Status'':Status'))
  355.         ->setParameter('Status'$searchData['link_status']);
  356.     }
  357.     // stock status
  358.     if (isset($searchData['stock_status'])) {
  359.       $qb
  360.         ->andWhere('pc.stock_unlimited = :StockUnlimited AND pc.stock = 0')
  361.         ->setParameter('StockUnlimited'$searchData['stock_status']);
  362.     }
  363.     // stock status
  364.     // 1:在庫あり(無制限含む) 2:在庫なし 3:在庫あり(無制限以外) 4:無制限
  365.     if (isset($searchData['stock']) && !empty($searchData['stock'])) {
  366.       $stockFlg is_array($searchData['stock']) ? reset($searchData['stock']) : $searchData['stock'];
  367.       switch ((int)$stockFlg) {
  368.         case ProductStock::IN_STOCK:
  369.           $qb->andWhere('pc.stock_unlimited = true OR pc.stock > 0');
  370.           break;
  371.         case ProductStock::OUT_OF_STOCK:
  372.           $qb->andWhere('pc.stock_unlimited = false AND pc.stock <= 0');
  373.           break;
  374.         case 3:
  375.           $qb->andWhere('pc.stock_unlimited = false AND pc.stock > 0');
  376.           break;
  377.         case 4:
  378.           $qb->andWhere('pc.stock_unlimited = true');
  379.           break;
  380.         default:
  381.           // 未定義の値は検索条件に含めない
  382.       }
  383.     }
  384.     // tag
  385.     if (!empty($searchData['tag_id']) && $searchData['tag_id']) {
  386.       $qb
  387.         ->innerJoin('p.ProductTag''pt')
  388.         ->andWhere('pt.Tag = :tag_id')
  389.         ->setParameter('tag_id'$searchData['tag_id']);
  390.     }
  391.     // crate_date
  392.     if (!empty($searchData['create_datetime_start']) && $searchData['create_datetime_start']) {
  393.       $date $searchData['create_datetime_start'];
  394.       $qb
  395.         ->andWhere('p.create_date >= :create_date_start')
  396.         ->setParameter('create_date_start'$date);
  397.     } elseif (!empty($searchData['create_date_start']) && $searchData['create_date_start']) {
  398.       $date $searchData['create_date_start'];
  399.       $qb
  400.         ->andWhere('p.create_date >= :create_date_start')
  401.         ->setParameter('create_date_start'$date);
  402.     }
  403.     if (!empty($searchData['create_datetime_end']) && $searchData['create_datetime_end']) {
  404.       $date $searchData['create_datetime_end'];
  405.       $qb
  406.         ->andWhere('p.create_date < :create_date_end')
  407.         ->setParameter('create_date_end'$date);
  408.     } elseif (!empty($searchData['create_date_end']) && $searchData['create_date_end']) {
  409.       $date = clone $searchData['create_date_end'];
  410.       $date $date
  411.         ->modify('+1 days');
  412.       $qb
  413.         ->andWhere('p.create_date < :create_date_end')
  414.         ->setParameter('create_date_end'$date);
  415.     }
  416.     // update_date
  417.     if (!empty($searchData['update_datetime_start']) && $searchData['update_datetime_start']) {
  418.       $date $searchData['update_datetime_start'];
  419.       $qb
  420.         ->andWhere('p.update_date >= :update_date_start')
  421.         ->setParameter('update_date_start'$date);
  422.     } elseif (!empty($searchData['update_date_start']) && $searchData['update_date_start']) {
  423.       $date $searchData['update_date_start'];
  424.       $qb
  425.         ->andWhere('p.update_date >= :update_date_start')
  426.         ->setParameter('update_date_start'$date);
  427.     }
  428.     if (!empty($searchData['update_datetime_end']) && $searchData['update_datetime_end']) {
  429.       $date $searchData['update_datetime_end'];
  430.       $qb
  431.         ->andWhere('p.update_date < :update_date_end')
  432.         ->setParameter('update_date_end'$date);
  433.     } elseif (!empty($searchData['update_date_end']) && $searchData['update_date_end']) {
  434.       $date = clone $searchData['update_date_end'];
  435.       $date $date
  436.         ->modify('+1 days');
  437.       $qb
  438.         ->andWhere('p.update_date < :update_date_end')
  439.         ->setParameter('update_date_end'$date);
  440.     }
  441.     // Order By
  442.     if (isset($searchData['sortkey']) && !empty($searchData['sortkey'])) {
  443.       $sortOrder = (isset($searchData['sorttype']) && $searchData['sorttype'] == 'a') ? 'ASC' 'DESC';
  444.       if ($searchData['sortkey'] === 'stock_sum') {
  445.         // 在庫が多い順・少ない順(無制限は並び順に関わらず必ず最後にまとめる)
  446.         // 商品配下のいずれか1つの規格でも無制限であれば、商品全体を無制限グループとして扱う
  447.         // 商品配下の全規格(可視のみ)の在庫数を合計する
  448.         //
  449.         // 注意: ここで groupBy('p') は使わない。
  450.         // このクエリは addSelect('pc','pi','tr','ps') で商品規格・画像等をto-many joinしており、
  451.         // KnpPaginatorの内部実装(Doctrine\ORM\Tools\Pagination\Paginator)はページ内容の最終取得時も
  452.         // 同じクエリ(GROUP BYを含む)をそのまま使い回すため、groupBy('p')を付けると
  453.         // 商品規格が複数ある商品でProductClassesコレクションが1件に潰れてしまい、
  454.         // 価格表示(price02_min/max)等が壊れる。そのため相関サブクエリで集計する。
  455.         $qb->addSelect('(SELECT SUM(pc_stock_sum.stock) FROM Eccube\Entity\ProductClass pc_stock_sum WHERE pc_stock_sum.Product = p AND pc_stock_sum.visible = true) as HIDDEN stock_sum_value');
  456.         $qb->addSelect('(SELECT MAX(pc_stock_unlimited.stock_unlimited) FROM Eccube\Entity\ProductClass pc_stock_unlimited WHERE pc_stock_unlimited.Product = p AND pc_stock_unlimited.visible = true) as HIDDEN stock_unlimited_flag');
  457.         $qb->addOrderBy('stock_unlimited_flag''ASC');
  458.         $qb->addOrderBy('stock_sum_value'$sortOrder);
  459.         $qb->addOrderBy('p.id''DESC');
  460.       } else {
  461.         $qb->orderBy(self::COLUMNS[$searchData['sortkey']], $sortOrder);
  462.         $qb->addOrderBy('p.update_date''DESC');
  463.         $qb->addOrderBy('p.id''DESC');
  464.       }
  465.     } else {
  466.       $qb->orderBy('p.id''DESC');
  467.       //$qb->addOrderBy('p.update_date', 'DESC');
  468.     }
  469.     //dump($qb->getQuery()->getSQL());exit;
  470.     //$all = $qb->getQuery()->getArrayResult();
  471.     //dump($all);exit;
  472.     return $this->queries->customize(QueryKey::PRODUCT_SEARCH_ADMIN$qb$searchData);
  473.   }
  474.   /**
  475.    * 商品種別(クレジット可否)をハッシュで取得
  476.    * sale_type_id = 3 がクレジット不可商品
  477.    *
  478.    * @return array
  479.    */
  480.   public function getHashType()
  481.   {
  482.     $conn $this->getEntityManager()->getConnection();
  483.     $sql = <<<SQL
  484. SELECT
  485.     product_id,
  486.     sale_type_id
  487. FROM
  488.     dtb_product_class
  489. WHERE
  490.     visible = 1
  491. SQL;
  492.     $stmt $conn->prepare($sql);
  493.     $resultSet $stmt->executeQuery();
  494.     $rows $resultSet->fetchAllAssociative();
  495.     $hash = array();
  496.     foreach ($rows as $row) {
  497.       $hash[$row['product_id']] = $row['sale_type_id'];
  498.     }
  499.     return $hash;
  500.   }
  501.   /**
  502.    * 地金型コインカテゴリーに属する商品をハッシュで取得
  503.    *
  504.    * @return array
  505.    */
  506.   public function getHashJigane()
  507.   {
  508.     $conn $this->getEntityManager()->getConnection();
  509.     $sql = <<<SQL
  510. SELECT
  511.     product_id,
  512.     category_id
  513. FROM
  514.     dtb_product_category
  515. WHERE
  516.     category_id = 5
  517. SQL;
  518.     $stmt $conn->prepare($sql);
  519.     $resultSet $stmt->executeQuery();
  520.     $rows $resultSet->fetchAllAssociative();
  521.     $hash = array();
  522.     foreach ($rows as $row) {
  523.       $hash[$row['product_id']] = $row['category_id'];
  524.     }
  525.     return $hash;
  526.   }
  527.   /**
  528.    * コインパレスが販売中のコインID一覧を取得
  529.    * @return array
  530.    * @throws \Doctrine\DBAL\Exception
  531.    */
  532.   public function getStorageCpIds()
  533.   {
  534.     $sql = <<<SQL
  535. SELECT
  536.   A.id
  537. FROM
  538.   dtb_product AS A
  539.   INNER JOIN plg_productplus_dtb_product_data AS B ON A.id = B.product_id
  540.   INNER JOIN plg_productplus_dtb_product_data_detail AS C ON B.id = C.product_data_id
  541. WHERE
  542.   B.product_item_id = 6
  543.   AND (C.value = 1424 or C.value = 1427 or C.value = '')
  544. SQL;
  545.     return $this->getIdsFromSQL($sql);
  546.   }
  547.   /**
  548.    * お客さまからの預りコインID一覧を取得
  549.    * @return array
  550.    * @throws \Doctrine\DBAL\Exception
  551.    */
  552.   public function getStorageCustIds()
  553.   {
  554.     $sql = <<<SQL
  555. SELECT
  556.   A.id
  557. FROM
  558.   dtb_product AS A
  559.   INNER JOIN plg_productplus_dtb_product_data AS B ON A.id = B.product_id
  560.   INNER JOIN plg_productplus_dtb_product_data_detail AS C ON B.id = C.product_data_id
  561. WHERE
  562.   B.product_item_id = 6
  563.   AND (C.value != 1424 AND C.value != 1427 AND C.value != '')
  564. SQL;
  565.     return $this->getIdsFromSQL($sql);
  566.   }
  567.   /**
  568.    * SQL文からID一覧を取得
  569.    * @param $sql
  570.    * @return array
  571.    * @throws \Doctrine\DBAL\Exception
  572.    */
  573.   public function getIdsFromSQL($sql){
  574.     $conn $this->getEntityManager()->getConnection();
  575.     $stmt $conn->prepare($sql);
  576.     $resultSet $stmt->executeQuery();
  577.     $rows $resultSet->fetchAllAssociative();
  578.     $ids = array();
  579.     foreach ($rows as $row) {
  580.       $ids[] = $row['id'];
  581.     }
  582.     return $ids;
  583.   }
  584.   /**
  585.    * 限定カテゴリーに属する商品IDを取得
  586.    * @param int $genteiCategoryId 限定親カテゴリーID(デフォルト: 218)
  587.    * @return array 商品IDの配列
  588.    */
  589.   public function getGenteiProductIds(int $genteiCategoryId 218): array
  590.   {
  591.     // 限定カテゴリー(218)とその子カテゴリーのIDを取得
  592.     $categoryIds $this->getGenteiCategoryIds($genteiCategoryId);
  593.     if (empty($categoryIds)) {
  594.       return [];
  595.     }
  596.     // 限定カテゴリーに属する商品IDを取得
  597.     $sql "SELECT DISTINCT product_id AS id FROM dtb_product_category WHERE category_id IN (" implode(','$categoryIds) . ")";
  598.     return $this->getIdsFromSQL($sql);
  599.   }
  600.   /**
  601.    * 限定カテゴリーとその子カテゴリーのIDを取得
  602.    * @param int $parentCategoryId 親カテゴリーID
  603.    * @return array カテゴリーIDの配列
  604.    */
  605.   public function getGenteiCategoryIds(int $parentCategoryId): array
  606.   {
  607.     $conn $this->getEntityManager()->getConnection();
  608.     // 親カテゴリーと全ての子孫カテゴリーを取得(再帰的に)
  609.     $sql = <<<SQL
  610. SELECT id FROM dtb_category
  611. WHERE id = :parent_id
  612.    OR parent_category_id = :parent_id
  613.    OR parent_category_id IN (SELECT id FROM dtb_category WHERE parent_category_id = :parent_id)
  614.    OR parent_category_id IN (SELECT id FROM dtb_category WHERE parent_category_id IN (SELECT id FROM dtb_category WHERE parent_category_id = :parent_id))
  615. SQL;
  616.     $stmt $conn->prepare($sql);
  617.     $resultSet $stmt->executeQuery(['parent_id' => $parentCategoryId]);
  618.     $rows $resultSet->fetchAllAssociative();
  619.     $ids = [];
  620.     foreach ($rows as $row) {
  621.       $ids[] = $row['id'];
  622.     }
  623.     return $ids;
  624.   }
  625.   /**
  626.    * 限定公開URL(column_id=66)に値がある商品IDを取得
  627.    * @return array 商品IDの配列
  628.    */
  629.   public function getGenteiUrlProductIds(): array
  630.   {
  631.     $sql = <<<SQL
  632. SELECT DISTINCT
  633.   ppd.product_id AS id
  634. FROM
  635.   plg_productplus_dtb_product_data AS ppd
  636.   INNER JOIN plg_productplus_dtb_product_data_detail AS ppdd ON ppd.id = ppdd.product_data_id
  637. WHERE
  638.   ppd.product_item_id = 66
  639.   AND ppdd.value IS NOT NULL
  640.   AND ppdd.value != ''
  641. SQL;
  642.     return $this->getIdsFromSQL($sql);
  643.   }
  644. }