vendor/lm/engine-ec/src/Entity/GoodsWithRelated.php line 798

Open in your IDE?
  1. <?php
  2. namespace Lm\Engine\EC\Entity;
  3. use Lm\Engine\EC\Entity\Goods\ColorNumberLabel;
  4. use Lm\Engine\EC\Entity\Goods\Label;
  5. use Lm\Engine\EC\Entity\Item\Item;
  6. use Lm\Engine\EC\UT;
  7. use Lm\Engine\SokujitsuHassou\SokujitsuHassou;
  8. use Lm\Engine\Zaiko\Entity\SkuExtended;
  9. use Lm\Entity\Category;
  10. use Lm\Engine\EC\Entity\Goods\CategoryListIconLabel;
  11. use Lm\Engine\EC\Entity\Goods\TargetGenderLabel;
  12. use Lm\Engine\EC\Entity\Goods\VendingStatusLabel;
  13. use Lm\Entity\GoodsSetPurchase;
  14. use Lm\Service\Cache\CacheService;
  15. use Lm\Service\Db\SqlService;
  16. use Lm\Entity\Goods;
  17. class GoodsWithRelated extends GoodsExtended
  18. {
  19.     const REVIEW_GET_MAX 50;
  20.     /**
  21.      * @var CategoryWithRelated
  22.      */
  23.     protected $mainCategory;
  24.     /**
  25.      * @var CategoryWithRelated
  26.      */
  27.     protected $canonicalCategory;
  28.     /**
  29.      * @var array
  30.      */
  31.     protected $schemaOrgProduct;
  32.     /**
  33.      * @var array
  34.      */
  35.     protected $schemaOrgAggregateOffer;
  36.     /**
  37.      * @var array
  38.      */
  39.     protected $schemaOrgAggregateRating;
  40.     /**
  41.      * @var array
  42.      */
  43.     protected $schemaOrgReviewList;
  44.     /**
  45.      * @var array
  46.      */
  47.     protected $reviewList;
  48.     /**
  49.      * @var array
  50.      */
  51.     protected $reviewSummary;
  52.     /**
  53.      * @var Item
  54.      */
  55.     protected $item;
  56.     /**
  57.      * @var TargetGenderLabel
  58.      */
  59.     protected $targetGenderLabel;
  60.     /**
  61.      * @var CategoryListIconLabel
  62.      */
  63.     protected $categoryListIconLabel;
  64.     /**
  65.      * @var VendingStatusLabel
  66.      */
  67.     protected $vendingStatusLabel;
  68.     /**
  69.      * @var ColorNumberLabel
  70.      */
  71.     protected $colorNumberLabel;
  72.     /**
  73.      * @var Label[]
  74.      */
  75.     protected $labelList;
  76.     /**
  77.      * @var bool
  78.      */
  79.     protected $isSokujitsuHassouAvailable;
  80.     /**
  81.      * @return string
  82.      *
  83.      */
  84.     public function getItemUrl()
  85.     {
  86.         //
  87.         $ut UT::getBaseInfo();
  88.         //
  89.         return sprintf("%s/item/%s.html"$ut->getShopUrl(), $this->getItemId());
  90.     }
  91.     /**
  92.      * @return CategoryWithRelated|null
  93.      */
  94.     public function getMainCategory()
  95.     {
  96.         return self::compute($this->canonicalCategory, function () {
  97.             //
  98.             return self::getMainCategoryById($this->goodsId);
  99.         });
  100.     }
  101.     /**
  102.      * @param $goodsId
  103.      * @return CategoryWithRelated|null
  104.      */
  105.     public static function getMainCategoryById($goodsId)
  106.     {
  107.         //
  108.         $mainCategory = (new SqlService())
  109.             ->Sql("SELECT
  110.                             main_category_main_category_webname
  111.                         ,    main_category_webname
  112.                         ,    COUNT(DISTINCT category_id) AS `ctCount`
  113.                         FROM (
  114.                             SELECT     main_category_id
  115.                             ,    main_category_main_category_webname
  116.                             ,    main_category_webname
  117.                             ,    category_id
  118.                             FROM goods_table
  119.                             INNER JOIN goods_category_table ON goods_id = gc_goods
  120.                             INNER JOIN category_table ON category_id = gc_category AND category_status = :category_status
  121.                             INNER JOIN main_category_table ON main_category_id = category_main_category AND main_category_status = :main_category_status
  122.                             WHERE goods_id = :goods_id
  123.                             UNION ALL
  124.                             SELECT    main_category_id
  125.                             ,    main_category_main_category_webname
  126.                             ,    main_category_webname
  127.                             ,    0 AS category_id
  128.                             FROM goods_table
  129.                             INNER JOIN goods_main_category_table ON goods_id = gmc_goods
  130.                             INNER JOIN main_category_table ON main_category_id = gmc_main_category AND main_category_status = :main_category_status
  131.                             WHERE goods_id = :goods_id
  132.                         ) AS `mc`
  133.                         GROUP BY
  134.                             main_category_id
  135.                         ORDER BY
  136.                             `ctCount` DESC
  137.                         ,    main_category_webname
  138.                         LIMIT 1")
  139.             ->Params([
  140.                 'goods_id' => $goodsId,
  141.                 'category_status' => Category::STATUS_AVAILABLE,
  142.                 'main_category_status' => Category::STATUS_AVAILABLE,
  143.             ])
  144.             ->Fetch()
  145.         ;
  146.         //
  147.         if (!empty($mainCategory)) {
  148.             //
  149.             return new CategoryWithRelated($mainCategory['main_category_webname'], null$mainCategory['main_category_main_category_webname']);
  150.         } else {
  151.             //
  152.             return null;
  153.         }
  154.     }
  155.     /**
  156.      * @return CategoryWithRelated|null
  157.      */
  158.     public function getCanonicalCategory()
  159.     {
  160.         return self::compute($this->canonicalCategory, function () {
  161.             //
  162.             if ($mainCategory $this->getMainCategory()) {
  163.                 //
  164.                 if ($mainCategory->hasMainCategories()) {
  165.                     //
  166.                     return $mainCategory;
  167.                 }
  168.             }
  169.         });
  170.     }
  171.     /**
  172.      * @return array
  173.      * @throws \Exception
  174.      */
  175.     public function getStockListForMatrix() {
  176.         //
  177.         $result = [
  178.             'stockList' => [],
  179.             'direct' => '',
  180.             'janList' => [],
  181.         ];
  182.         //
  183.         $goodsId $this->getGoodsId();
  184.         //
  185.         $skuList SkuExtended::getInstanceList($goodsId);
  186.         //
  187.         foreach($skuList as $sku){
  188.             //
  189.             $gclId $sku->getGclId();
  190.             $gpId $sku->getGpId();
  191.             //
  192.             if (!isset($result['stockList'][$goodsId])) {
  193.                 //
  194.                 $result['stockList'][$goodsId] = [];
  195.             }
  196.             //
  197.             if (!isset($result['stockList'][$goodsId][$gclId])) {
  198.                 //
  199.                 $result['stockList'][$goodsId][$gclId] = [];
  200.             }
  201.             //
  202.             $nyukaDateComment $sku->getNyukaDateComment();
  203.             $nyukaDate $sku->getNyukaDate();
  204.             $comment '';
  205.             if (strstr(trim($nyukaDateComment), "次回生産予定無し")) {
  206.                 $comment '<span style="color: #138a19;">入荷予定日<br />未定</span>';
  207.             } elseif (strstr(trim($nyukaDateComment), "生産中止")) {
  208.                 $comment '<span style="color: #696969;">生産中止</span>';
  209.             } elseif (strstr(trim($nyukaDateComment), "生産中")) {
  210.                 $comment '<span style="color: #138a19;">生産中</span>';
  211.             } elseif ($nyukaDate != null) {
  212.                 $comment '<span style="color: #138a19;">入荷予定日<br />' date("n月j日"strtotime("{$nyukaDate}")) . '</span>';
  213.             }
  214.             //
  215.             $result['stockList'][$goodsId][$gclId][$gpId] = [
  216.                 'stock_other' => $sku->getStockOther(),
  217.                 'stock' => $sku->getStockTotal(),
  218.             ];
  219.             //
  220.             if ($stock3 $sku->getStockBichiku()) {
  221.                 //
  222.                 if ($bichikuNyukaDate $sku->getBichikuNyukaDate()) {
  223.                     $result['stockList'][$goodsId][$gclId][$gpId]['stock3'] = sprintf('<div class="nyuka-yotei" style="font-size:12px;">入荷予定日<br />%s</div>'date("n月j日"strtotime("{$bichikuNyukaDate} +1 day")));
  224.                 } else {
  225.                     $result['stockList'][$goodsId][$gclId][$gpId]['stock3'] = sprintf("<span style=\"color:red;\">(即日発送 %s)</span>"$stock3);
  226.                 }
  227.             }
  228.             //
  229.             if (true) {
  230.                 $result['stockList'][$goodsId][$gclId][$gpId]['nodisp'] = $sku->getNoDisp();
  231.             }
  232.             //
  233.             if (!empty($nyukaDate)) {
  234.                 $result['stockList'][$goodsId][$gclId][$gpId]['zdate'] = (new \DateTime($nyukaDate))->format('n月j日');
  235.                 $result['stockList'][$goodsId][$gclId][$gpId]['nyuuka_yoteibi'] = (new \DateTime($nyukaDate))->format('Y-m-d H:i:s');
  236.             }
  237.             //
  238.             if (!empty($comment)) {
  239.                 $result['stockList'][$goodsId][$gclId][$gpId]['comment'] = $comment;
  240.             }
  241.             //
  242.             if (!isset($result['janList'][$gclId])) {
  243.                 //
  244.                 $result['janList'][$gclId] = [];
  245.             }
  246.             $result['janList'][$gclId][$gpId] = $sku->getBichikuNyukaDate() ?: $sku->getNyukaDate();
  247.         }
  248.         //
  249.         return $result;
  250.     }
  251.     /**
  252.      * @param int $goodsId
  253.      * @return array
  254.      * @throws Exception
  255.      */
  256.     function LM_chokusouCheckAll2($goodsId) {
  257.         return (array)(new \Lm\Engine\SokujitsuHassou\SokujitsuHassou())
  258.             ->check($goodsIdtrue);
  259.     }
  260.     protected function getStockList() {
  261.         //
  262.         $result = array();
  263.         //
  264.         $goodsId $this->getGoodsId();
  265.         // 84: チトセの場合は、在庫数 + 外部倉庫
  266.         // $is_chitose = false;
  267.         // $strSql = sprintf("SELECT DISTINCT gp_kataban FROM jancode_table INNER JOIN goods_price_table ON jan_price = gp_id WHERE jan_goods = %d", $goodsId);
  268.         // if ($res = $model->getDatas($db, $strSql)) {
  269.         //     $tmp = explode('-', $res[0]['gp_kataban']);
  270.         //     if ($tmp[0] == '84') {
  271.         //         $is_chitose = true;
  272.         //     }
  273.         // }
  274.         $is_chitose $this->isChitose();
  275.         // 在庫マトリクス高速化対応
  276.         // TODO: 直送チェック用のリストを取得
  277.         $sokujitsuFlgList $this->LM_chokusouCheckAll2($goodsId);
  278.         // 在庫情報を取得
  279.         // $strSql = $this->getCheckStockSelectByIdSql($goodsId);
  280.         // $stockList = $model->getDatas($db, $strSql);
  281.         $stockList = (array)$this->getCheckStockList();
  282.         foreach ($stockList as $stock) {
  283.             if (is_numeric($stock['stock'])) {
  284.                 $result[$goodsId][$stock['jan_color']][$stock['jan_price']]['stock_other'] = intval($stock['jan_stock4']) + intval($stock['jan_stock5']) + intval($stock['jan_stock6']) + intval($stock['jan_stock7']) + intval($stock['jan_stock8']) + intval($stock['jan_stock3']);
  285.                 $result[$goodsId][$stock['jan_color']][$stock['jan_price']]['stock'] = $stock['stock'] + $result[$goodsId][$stock['jan_color']][$stock['jan_price']]['stock_other'];
  286.             } else {
  287.                 $result[$goodsId][$stock['jan_color']][$stock['jan_price']]['stock'] = $stock['stock'];
  288.             }
  289.             if ($is_chitose && $stock['jan_stock9']) {
  290.                 $result[$goodsId][$stock['jan_color']][$stock['jan_price']]['stock'] += $stock['jan_stock9'];
  291.             }
  292.             if($stock['jan_stock3']!="" and $stock['jan_stock3']>and $stock['jan_stock3_nyuka_date']==""){
  293.                 $result[$goodsId][$stock['jan_color']][$stock['jan_price']]['stock3'] = "<span style='color:red;'>(即日発送 ".$stock['jan_stock3'].")</span>";
  294.             }elseif($stock['jan_stock3']!="" and $stock['jan_stock3']>and $stock['jan_stock3_nyuka_date']!=""){
  295.                 $result[$goodsId][$stock['jan_color']][$stock['jan_price']]['stock3'] = '<div class="nyuka-yotei" style="font-size:12px;">入荷予定日<br />' date("n月j日"strtotime($stock['jan_stock3_nyuka_date']." +1 day")) . '</div>';
  296.             }elseif(isset($sokujitsuFlgList[$stock['jan_color']][$stock['jan_price']]) && $sokujitsuFlgList[$stock['jan_color']][$stock['jan_price']]){
  297.                 $result[$goodsId][$stock['jan_color']][$stock['jan_price']]['stock3'] = "<span style='color:red;'>(即日発送 ".$result[$goodsId][$stock['jan_color']][$stock['jan_price']]['stock'].")</span>";
  298.             }
  299.             $result[$goodsId][$stock['jan_color']][$stock['jan_price']]['nodisp'] = $stock['nodisp'];
  300.         }
  301.         // 入荷予定情報を取得
  302.         // $strSql = $model->getGoodsArrivalDateSelectSql($goodsId);
  303.         // $zeroList = $model->getDatas($db, $strSql);
  304.         $zeroList = (array)$this->getGoodsArrivalDate($goodsId);
  305.         foreach ($zeroList as $zero) {
  306.             // // $result[$goodsId][$zero['zero_gcl']][$zero['zero_gp']]['stock'] = 0;
  307.             if ($result[$goodsId][$zero['zero_gcl']][$zero['zero_gp']]['stock'] === '*') {
  308.                 // 在庫「*」の場合、「在庫0指定」(=予約注文)を優先
  309.                 $result[$goodsId][$zero['zero_gcl']][$zero['zero_gp']]['stock'] = 0;
  310.             } else if (empty($result[$goodsId][$zero['zero_gcl']][$zero['zero_gp']]['stock_other'])) {
  311.                 // 仕入先在庫のみ存在するの場合、「在庫0指定」(=予約注文)を優先
  312.                 $result[$goodsId][$zero['zero_gcl']][$zero['zero_gp']]['stock'] = 0;
  313.             }
  314.             //
  315.             $result[$goodsId][$zero['zero_gcl']][$zero['zero_gp']]['zdate'] = $zero['zdate'];
  316.             $result[$goodsId][$zero['zero_gcl']][$zero['zero_gp']]['nyuuka_yoteibi'] = $zero['nyuuka_yoteibi'];
  317.         }
  318.         // 欠品情報を取得(入荷未定、入荷予定日あり、生産中、生産中止)
  319.         // $strSql = $model->getGoodsStockoutSelectSql($goodsId);
  320.         // $keppinList = $model->getDatas($db, $strSql);
  321.         $keppinList = (array)$this->getGoodsStockout();
  322.         foreach ((array)$keppinList as $keppin) {
  323.             // // $result[$goodsId][$keppin['ki_gcl']][$keppin['ki_gp']]['stock'] = 0;
  324.             if ($result[$goodsId][$keppin['ki_gcl']][$keppin['ki_gp']]['stock'] === '*') {
  325.                 // 在庫「*」の場合、「欠品登録」(=予約注文)を優先
  326.                 $result[$goodsId][$keppin['ki_gcl']][$keppin['ki_gp']]['stock'] = 0;
  327.             } else if (empty($result[$goodsId][$keppin['ki_gcl']][$keppin['ki_gp']]['stock_other'])) {
  328.                 // 仕入先在庫のみ存在するの場合、「欠品登録」(=予約注文)を優先
  329.                 $result[$goodsId][$keppin['ki_gcl']][$keppin['ki_gp']]['stock'] = 0;
  330.             }
  331.             //
  332.             $result[$goodsId][$keppin['ki_gcl']][$keppin['ki_gp']]['zdate'] = date('n月j日'strtotime($keppin['ki_date']));
  333.             $result[$goodsId][$keppin['ki_gcl']][$keppin['ki_gp']]['nyuuka_yoteibi'] = $keppin['ki_date'];
  334.             if (strstr(trim($keppin['ki_comment']), "次回生産予定無し")) {
  335.                 $result[$goodsId][$keppin['ki_gcl']][$keppin['ki_gp']]['comment'] = '<span style="color: #138a19;">入荷予定日<br />未定</span>';
  336.             } elseif (strstr(trim($keppin['ki_comment']), "生産中止")) {
  337.                 $result[$goodsId][$keppin['ki_gcl']][$keppin['ki_gp']]['comment'] = '<span style="color: #696969;">生産中止</span>';
  338.             } elseif (strstr(trim($keppin['ki_comment']), "生産中")) {
  339.                 $result[$goodsId][$keppin['ki_gcl']][$keppin['ki_gp']]['comment'] = '<span style="color: #138a19;">生産中</span>';
  340.             } elseif ($keppin['ki_date'] != null) {
  341.                 $result[$goodsId][$keppin['ki_gcl']][$keppin['ki_gp']]['comment'] = '<span style="color: #138a19;">入荷予定日<br />' date("n月j日"strtotime("{$keppin['ki_date']} +1 day")) . '</span>';
  342.             } else {
  343.                 $result[$goodsId][$keppin['ki_gcl']][$keppin['ki_gp']]['comment'] = "";
  344.             }
  345.         }
  346.         return $result;
  347.     }
  348.     /**
  349.      * @return array|null
  350.      */
  351.     public function getCheckStockList ()
  352.     {
  353.         return self::getCheckStockListById($this->getGoodsId());
  354.     }
  355.     /**
  356.      * @link https://bitbucket.org/lm91/front/src/e4bda25c7a8fce8f116c87adb8646ee8f9635f6f/app/html/application/modules/default/models/commons/BaseLibraryModel.php#lines-523:529
  357.      * @param $goodsId
  358.      * @return array|null
  359.      */
  360.     public static function getCheckStockListById($goodsId)
  361.     {
  362.         //
  363.         return (new SqlService())
  364.             ->Select("
  365.                     jan_color
  366.                 ,    jan_price
  367.                 ,    coalesce( jan_stock, '*' ) AS stock
  368.                 ,    coalesce( jan_stock3, 0 ) AS jan_stock3
  369.                 ,    jan_stock3_nyuka_date
  370.                 ,    coalesce( jan_stock4, 0 ) AS jan_stock4
  371.                 ,    coalesce( jan_stock5, 0 ) AS jan_stock5
  372.                 ,    coalesce( jan_stock6, 0 ) AS jan_stock6
  373.                 ,    coalesce( jan_stock7, 0 ) AS jan_stock7
  374.                 ,    coalesce( jan_stock8, 0 ) AS jan_stock8
  375.                 ,    coalesce( jan_stock9, 0 ) AS jan_stock9
  376.                 ,    coalesce( jan_stock9, 0 ) AS jan_stock9
  377.                 ,    jan_nodisplay AS nodisp
  378.                 ,    MIN(COALESCE(jan_stock3_nyuka_date, T2.zero_period, T1.ki_date)) AS `nyuuka_yoteibi`
  379.             ")
  380.             ->Table("jancode_table")
  381.             ->Join("keppin_item_table""T.jan_goods = T1.ki_goods AND T.jan_color = T1.ki_gcl AND T.jan_price = T1.ki_gp AND T1.ki_date > NOW()""LEFT")
  382.             ->Join("zero_stock_table""T.jan_goods = T2.zero_goods AND T.jan_color = T2.zero_gcl AND T.jan_price = T2.zero_gp AND T2.zero_period > NOW()""LEFT")
  383.             ->Set("jan_goods"$goodsId)
  384.             ->GroupBy('T.jan_id')
  385.             ->FindAll()
  386.         ;
  387.     }
  388.     /**
  389.      * 在庫入荷予定日
  390.      * @return array|null
  391.      */
  392.     public function getGoodsArrivalDate ()
  393.     {
  394.         return self::getGoodsArrivalDateById($this->getGoodsId());
  395.     }
  396.     /**
  397.      * 在庫入荷予定日
  398.      * @link https://bitbucket.org/lm91/front/src/e4bda25c7a8fce8f116c87adb8646ee8f9635f6f/app/html/application/modules/default/models/commons/BaseLibraryModel.php#lines-1281:1304
  399.      * @param int $goodsId
  400.      * @return array|null
  401.      */
  402.     public static function getGoodsArrivalDateById ($goodsId)
  403.     {
  404.         return (new SqlService())
  405.             ->Sql("SELECT *, date_format( date_add( zero_period, interval 1 day ), '%c月%e日' ) AS zdate,(CASE WHEN cp_price IS NOT NULL AND cp_price > 0 THEN cp_price ELSE ROUND(gp_price2 * (1+ 10/100)) END) as gp_price, date_add( zero_period, interval 1 day ) AS nyuuka_yoteibi 
  406.                     FROM zero_stock_table
  407.                     INNER JOIN goods_table ON zero_goods = goods_id
  408.                     LEFT JOIN goods_price_table ON zero_gp = gp_id
  409.                     LEFT JOIN size_table ON gp_size_id = size_id
  410.                     LEFT JOIN goods_color_table ON zero_gcl = gcl_id
  411.                     LEFT JOIN color_table ON gcl_color_id = color_id
  412.                     LEFT JOIN campaign_price_table ON cp_gp = gp_id AND `cp_end_datetime` >= NOW() and `cp_start_datetime` <= NOW()
  413.                     LEFT JOIN jancode_table ON jan_goods = goods_id AND jan_color = gcl_id AND jan_price = gp_id
  414.                     WHERE zero_goods = :goods_id
  415.                     AND zero_period >= NOW()
  416.                     AND ( jan_stock5 IS NULL OR jan_stock5 = 0 )
  417.                     AND ( jan_stock6 IS NULL OR jan_stock6 = 0 )
  418.                     AND ( jan_stock7 IS NULL OR jan_stock7 = 0 )
  419.                     AND ( jan_stock8 IS NULL OR jan_stock8 = 0 )
  420.                     AND ( jan_stock9 IS NULL OR jan_stock9 = 0 )
  421.                     AND ( gp_display IS NULL OR ( gp_display IS NOT NULL AND gp_display != 99 ) )
  422.                     AND gcl_display_status = 1
  423.                     ORDER BY gcl_display, color_display,gp_display,size_display")
  424.             ->Params([
  425.                 'goods_id' => $goodsId,
  426.             ])
  427.             ->FetchAll()
  428.         ;
  429.     }
  430.     /**
  431.      * 欠品情報
  432.      * @param string|null $fromDatetime
  433.      * @return array|null
  434.      */
  435.     public function getGoodsStockout ($fromDatetime null)
  436.     {
  437.         return self::getGoodsStockoutById($this->getGoodsId(), $fromDatetime);
  438.     }
  439.     //
  440.     /**
  441.      * 欠品情報
  442.      * @link https://bitbucket.org/lm91/front/src/e4bda25c7a8fce8f116c87adb8646ee8f9635f6f/app/html/application/modules/default/models/commons/BaseLibraryModel.php#lines-1306:1330
  443.      * @param int $goodsId
  444.      * @param string|null $fromDatetime
  445.      * @return array|null
  446.      */
  447.     public static function getGoodsStockoutById ($goodsId$fromDatetime null)
  448.     {
  449.         //
  450.         if ($fromDatetime === null) {
  451.             $fromDatetime date('Y-m-d 00:00:00');
  452.         }
  453.         //
  454.         return (new SqlService())
  455.             ->Sql("SELECT *, date_format( ki_date, '%Y/%m/%d' ) AS kdate
  456.                     FROM keppin_item_table AS a
  457.                     INNER JOIN goods_table ON ki_goods = goods_id
  458.                     LEFT JOIN goods_price_table ON ki_gp = gp_id
  459.                     LEFT JOIN size_table ON gp_size_id = size_id
  460.                     LEFT JOIN goods_color_table ON ki_gcl = gcl_id
  461.                     LEFT JOIN color_table ON gcl_color_id = color_id
  462.                     WHERE NOT EXISTS (
  463.                         SELECT * FROM keppin_item_table AS b
  464.                         WHERE a.ki_id < b.ki_id
  465.                         AND a.ki_goods = b.ki_goods
  466.                         AND ( a.ki_gp = b.ki_gp OR b.ki_gp IS NULL )
  467.                         AND ( a.ki_gcl = b.ki_gcl OR b.ki_gcl IS NULL )
  468.                     )
  469.                     AND a.ki_goods = :goods_id
  470.                     AND ( a.ki_date >= :from_datetime OR a.ki_date IS NULL )
  471.                     AND a.ki_comment IS NOT NULL
  472.                     AND ( gp_display IS NULL OR ( gp_display IS NOT NULL AND gp_display != 99 ) )
  473.                     AND gcl_display_status = 1
  474.                     ORDER BY gcl_display, color_display,gp_display,size_display")
  475.             ->Params([
  476.                 'goods_id' => $goodsId,
  477.                 'from_datetime' => $fromDatetime,
  478.             ])
  479.             ->FetchAll()
  480.         ;
  481.     }
  482.     public function getSchemaOrgProduct()
  483.     {
  484.         return self::compute($this->schemaOrgProduct, function () {
  485.             //
  486.             if ($category $this->getMainCategory()) {
  487.                 //
  488.                 $category $category->getMainCategoryName();
  489.             }
  490.             //
  491.             $result = [
  492.                 '@context' => 'http://schema.org',
  493.                 '@type' => 'Product',
  494.                 'productID' => $this->getGoodsId(),
  495.                 'name' => $this->getGoodsName(),
  496.                 'sku' => $this->getGoodsMainKataban(),
  497.                 'mpn' => $this->getGoodsMainKataban(),
  498.                 'category' => $category,
  499.             ];
  500.             //
  501.             if ($aggregateRating $this->getSchemaOrgAggregateRating()) {
  502.                 //
  503.                 $result['aggregateRating'] = $aggregateRating;
  504.             }
  505.             //
  506.             if ($review $this->getSchemaOrgReviewList()) {
  507.                 //
  508.                 $result['review'] = $review;
  509.             }
  510.             //
  511.             if ($offers $this->getSchemaOrgAggregateOffer()) {
  512.                 //
  513.                 $result['offers'] = $offers;
  514.             }
  515.             //
  516.             return $result;
  517.         });
  518.     }
  519.     public function getSchemaOrgAggregateOffer()
  520.     {
  521.         return self::compute($this->schemaOrgAggregateOffer, function () {
  522.             //
  523.             $result = [];
  524.             //
  525.             $goodsPrice $this->getPrice();
  526.             $availability $this->isStockAvailable() ? 'InStock' 'OutOfStock';
  527.             //
  528.             $result = [
  529.                 '@type' => 'AggregateOffer',
  530.                 'availability' => "http://schema.org/{$availability}",
  531.                 'price' => $goodsPrice['min_price_in_tax'],
  532.                 'lowPrice' => $goodsPrice['min_price_in_tax'],
  533.                 'highPrice' => $goodsPrice['max_price_in_tax'],
  534.                 'priceCurrency' => 'JPY',
  535.                 'url' => $this->getItemUrl(),
  536.             ];
  537.             //
  538.             if ($priceValidUntil $goodsPrice['max_cp_end_datetime']) {
  539.                 $result['priceValidUntil'] = date('Y-m-d'strtotime($priceValidUntil));
  540.             }
  541.             //
  542.             return $result;
  543.         });
  544.     }
  545.     public function getSchemaOrgAggregateRating()
  546.     {
  547.         return self::compute($this->schemaOrgAggregateRating, function () {
  548.             //
  549.             $result = [];
  550.             //
  551.             if ($reviewList $this->getReviewList($reviewCount)) {
  552.                 $cr_points array_map(function ($v) {
  553.                     return $v["cr_points"];
  554.                 }, $reviewList);
  555.                 $arg = empty($cr_points) ? : (array_sum($cr_points) / count($cr_points));
  556.                 $ratingValue = (floor($arg 10)) / 10;
  557.                 //
  558.                 $result = [
  559.                     '@type' => 'AggregateRating',
  560.                     'ratingValue' => $ratingValue,
  561.                     'reviewCount' => $reviewCount,
  562.                 ];
  563.             }
  564.             //
  565.             return $result;
  566.         });
  567.     }
  568.     public function getSchemaOrgReviewList()
  569.     {
  570.         return self::compute($this->schemaOrgReviewList, function () {
  571.             //
  572.             $reviewList $this->getReviewList();
  573.             //
  574.             $result = [];
  575.             //
  576.             foreach ($reviewList as $review) {
  577.                 //
  578.                 $result[] = [
  579.                     '@type' => 'Review',
  580.                     'datePublished' => date('Y-m-d'strtotime($review['app_datetime'])),
  581.                     'reviewBody' => !empty($review['cr_staff_comment']) ? $review['cr_staff_comment'] : $review['cr_comment'],
  582.                     'author' => [
  583.                         '@type' => 'Person',
  584.                         'name' => empty($review['cr_name']) ? '匿名希望' $review['cr_name'],
  585.                     ],
  586.                     'reviewRating' => [
  587.                         '@type' => 'Rating',
  588.                         'bestRating' => 5,
  589.                         'ratingValue' => $review['cr_points'],
  590.                         'worstRating' => 1,
  591.                     ],
  592.                 ];
  593.             }
  594.             //
  595.             return $result;
  596.         });
  597.     }
  598.     public function getReviewSummary()
  599.     {
  600.         return self::compute($this->reviewSummary, function () {
  601.             //
  602.             $result = [
  603.                 'ratingValue' => 0,
  604.                 'reviewCount' => 0,
  605.                 'imgStars' => '',
  606.             ];
  607.             $image_name 0;
  608.             $arg 0;
  609.             //
  610.             if ($reviewList $this->getReviewList($reviewCount)) {
  611.                 $cr_points array_map(function ($v) {
  612.                     return $v["cr_points"];
  613.                 }, $reviewList);
  614.                 $arg = empty($cr_points) ? : (array_sum($cr_points) / count($cr_points));
  615.                 $ratingValue = (floor($arg 10)) / 10;
  616.                 //
  617.                 $image_name = (int)($arg 2) / 10;
  618.                 //
  619.                 $result['ratingValue'] = $ratingValue;
  620.                 $result['reviewCount'] = $reviewCount;
  621.             }
  622.             //
  623.             $result['imgStars'] = "<img class=\"history-average-img\" alt=\"お客様からの口コミレビュー評価の星の数{$arg}\" src=\"/images/review/star_m{$image_name}.gif\">";
  624.             //
  625.             return $result;
  626.         });
  627.     }
  628.     public function getReviewList(&$count null$limit self::REVIEW_GET_MAX$offset 0)
  629.     {
  630.         return self::compute($this->reviewList, function () use ($limit$offset, &$count) {
  631.             //
  632.             $id $this->getGoodsId();
  633.             //
  634.             return self::getReviewListById($id$limit$offset$count);
  635.         });
  636.     }
  637.     public static function getReviewListById($id$limit null$offset 0, &$count null)
  638.     {
  639.         return CacheService::getCached(function () use ($id$limit$offset, &$count) {
  640.             $sql = (new SqlService())
  641.                 ->Table('customer_review_table')
  642.                 ->Set('cr_approval'1)
  643.                 ->Set('cr_site_id'0)
  644.                 ->Set('cr_main_category_id'0)
  645.                 ->Set('cr_goods_id'$id)
  646.                 ->Order('app_datetime''DESC');
  647.             //
  648.             $count call_user_func(function ($sql) {
  649.                 return (int)$sql->Count();
  650.             }, clone $sql);
  651.             //
  652.             if (!empty($limit)) $sql
  653.                 ->Limit($offset$limit);
  654.             //
  655.             $result = (array)$sql
  656.                 ->FindAll();
  657.             //
  658.             return $result;
  659.         });
  660.     }
  661.     public function getPrice()
  662.     {
  663.         return self::getPriceByGoodsId($this->getGoodsId());
  664.     }
  665.     /**
  666.      * TODO: 疑似バッチ
  667.      * @param int[]|int $goodsId
  668.      * @param $datetime
  669.      * @return mixed
  670.      * @throws \ReflectionException
  671.      */
  672.     public static function getPriceByGoodsId($goodsId$datetime null)
  673.     {
  674.         //
  675.         if ($datetime === null) {
  676.             //
  677.             $datetime date("Y-m-d H:i:s");
  678.         }
  679.         //
  680.         $hash md5(serialize($goodsId));
  681.         $label "goods-price-{$hash}-{$datetime}";
  682.         //
  683.         return CacheService::getCached(function () use ($goodsId$datetime) {
  684.             //
  685.             $goodsIdList = [];
  686.             foreach ((array)$goodsId as $i => $_goodsId) {
  687.                 $goodsIdList["goodsId{$i}"] = $_goodsId;
  688.             }
  689.             $goodsIdListPlaceHolder implode(','array_map(function ($key) {
  690.                 return ":{$key}";
  691.             }, array_keys($goodsIdList)));
  692.             //
  693.             // TODO: 税率は動的に取得
  694.             $sql "SELECT
  695.                         goods_id
  696.                     ,    MIN(CASE WHEN cp_price IS NOT NULL AND cp_price > 0 THEN (truncate((cp_price/1.10) + .5,0)) ELSE (gp_price2) END ) AS `min_price_ex_tax`
  697.                     ,    MIN(CASE WHEN cp_price IS NOT NULL AND cp_price > 0 THEN (cp_price) ELSE (ROUND(gp_price2 * (1+ 10/100))) END) AS `min_price_in_tax`
  698.                     ,    MAX(CASE WHEN cp_price IS NOT NULL AND cp_price > 0 THEN (truncate((cp_price/1.10) + .5,0)) ELSE (gp_price2) END ) AS `max_price_ex_tax`
  699.                     ,    MAX(CASE WHEN cp_price IS NOT NULL AND cp_price > 0 THEN (cp_price) ELSE (ROUND(gp_price2 * (1+ 10/100))) END) AS `max_price_in_tax`
  700.                     ,    MIN(`cp_start_datetime`) AS `min_cp_start_datetime`
  701.                     ,    MAX(`cp_end_datetime`) AS `max_cp_end_datetime`
  702.                     ,    (:datetime BETWEEN MIN(`cp_start_datetime`) AND MAX(`cp_end_datetime`)) AS is_campaign
  703.                     FROM
  704.                         goods_table
  705.                     INNER JOIN
  706.                         goods_price_table
  707.                     ON
  708.                         goods_id = gp_goods
  709.                     AND
  710.                         IfNull(gp_display, 0) != 99
  711.                     LEFT JOIN
  712.                         campaign_price_table
  713.                     ON
  714.                         gp_id = cp_gp
  715.                     AND
  716.                         cp_start_datetime <= :datetime
  717.                     AND
  718.                         cp_end_datetime >= :datetime
  719.                     AND
  720.                         cp_del_flg = 0
  721.                     WHERE
  722.                         goods_id IN ({$goodsIdListPlaceHolder})
  723.                     GROUP BY
  724.                         goods_id";
  725.             $params array_merge([
  726.                 'datetime' => $datetime,
  727.             ], $goodsIdList);
  728.             //
  729.             $sql = (new SqlService())
  730.                 // ->Select('goods_id, MIN(CASE WHEN cp_price IS NOT NULL AND cp_price > 0 THEN (truncate((cp_price/1.10) + .5,0)) ELSE (gp_price2) END ), MAX(CASE WHEN cp_price IS NOT NULL AND cp_price > 0 THEN (cp_price) ELSE (ROUND(gp_price2 * (1+ 10/100))) END)')
  731.                 // ->Table('goods_table')
  732.                 // ->Join('goods_price_table', 'T.goods_id = T1.gp_goods', 'INNER')
  733.                 // ->Join('campaign_price_table', 'T.goods_id = T1.gp_goods AND cp_start_datetime <= :datetime AND cp_end_datetime >= :datetime AND cp_del_flg = 0', 'LEFT')
  734.                 // ->Set('goods_id', $goodsId)
  735.                 ->Sql($sql)
  736.                 ->Params($params)
  737.             ;
  738.             //
  739.             if (is_array($goodsId)) {
  740.                 //
  741.                 $result $sql->FetchAll();
  742.             } else {
  743.                 //
  744.                 $result $sql->Fetch();
  745.             }
  746.             //
  747.             return $result;
  748.         }, $label);
  749.     }
  750.     public function getStock()
  751.     {
  752.         return self::getStockById($this->getGoodsId(), true);
  753.     }
  754.     public function isStockAvailable()
  755.     {
  756.         return self::getStockById($this->getGoodsId());
  757.     }
  758.     /**
  759.      * @param int|NULL $goods_id
  760.      * @param bool $is_matrix
  761.      * @param int|NULL $jan_id
  762.      * @param int|NULL $gp_id
  763.      * @param int|NULL $gcl_id
  764.      * @param string|NULL $date
  765.      * @return mixed
  766.      * @throws \Exception
  767.      */
  768.     public static function getStockById($goods_id NULL$is_matrix false$jan_id NULL$gp_id NULL$gcl_id NULL$date NULL)
  769.     {
  770.         return CacheService::getCached(function () use ($goods_id$is_matrix$jan_id$gp_id$gcl_id$date) {
  771.             //
  772.             $skuList SkuExtended::getInstanceList($goods_id$jan_id$gcl_id$gp_id$date);
  773.             $stock_total_summary 0;
  774.             $stockList = [];
  775.             foreach ($skuList as $sku) {
  776.                 //
  777.                 $gclId $sku->getGclId();
  778.                 $gpId $sku->getGpId();
  779.                 //
  780.                 if (!isset($stockList[$gclId])) {
  781.                     //
  782.                     $stockList[$gclId] = [];
  783.                 }
  784.                 //
  785.                 if (!isset($stockList[$gclId][$gpId])) {
  786.                     //
  787.                     $stockList[$gclId][$gpId] = [
  788.                         'stock_total' => null,
  789.                         'stock_sokujitsu' => null,
  790.                     ];
  791.                 }
  792.                 //
  793.                 if (!$sku->isNoDisplay()) {
  794.                     //
  795.                     $stockTotal $sku->getStockTotal();
  796.                     //
  797.                     $stockList[$gclId][$gpId] = [
  798.                         'stock_total' => $stockTotal,
  799.                         'stock_sokujitsu' => $sku->getStockSokujitsu(),
  800.                     ];
  801.                     //
  802.                     $stock_total_summary += (($stockTotal === '*') ? $stockTotal);
  803.                 }
  804.             }
  805.             //
  806.             if ($is_matrix === false) {
  807.                 //
  808.                 if (!empty($jan_id) || (!empty($gcl_id) && !empty($gp_id))) {
  809.                     // is_matrix = false ・・・ 指定されたSKUの分のみ出力
  810.                     return $stockList[$gclId][$gpId];
  811.                 } else {
  812.                     // is_matrix = false ・・・ SKU指定が省略された場合、在庫の有/無を返す。
  813.                     return $stock_total_summary 0;
  814.                 }
  815.             } else {
  816.                 //is_matrix = true ・・・ 全SKUの多段配列でSKU毎に出力
  817.                 return $stockList;
  818.             }
  819.         });
  820.     }
  821.     public function getAsGoodsSetPurchase($flattened false)
  822.     {
  823.         return self::getAsGoodsSetPurchaseById($this->getGoodsId(), $flattened);
  824.     }
  825.     public static function getAsGoodsSetPurchaseById($goodsId$flattened false)
  826.     {
  827.         return CacheService::getCached(function () use ($goodsId$flattened) {
  828.             //
  829.             $goodsSetPurchase CacheService::getCached(function () use ($goodsId) {
  830.                 return (new SqlService())
  831.                     ->Select(implode(', ', [
  832.                         'T.gsp_type',
  833.                         'T.gsp_name',
  834.                         'T1.goods_id',
  835.                         'T1.goods_main_kataban',
  836.                         'T1.goods_name',
  837.                         'T3.color_name',
  838.                         'T3.color_rgb',
  839.                     ]))
  840.                     ->Table('goods_set_purchase_table')
  841.                     ->Join('goods_table''T1.goods_id = T.gsp_goods_child''INNER')
  842.                     ->Join('goods_color_table''T1.goods_id = T2.gcl_goods AND T.gsp_type > 0')    // メイン品番のカラーは取得しない
  843.                     ->Join('color_table''T3.color_id = T2.gcl_color_id AND T.gsp_type > 0')    // メイン品番のカラーは取得しない
  844.                     ->Set('gsp_goods_parent'$goodsId)
  845.                     ->OrderBy('T.gsp_type')
  846.                     ->FindAll()
  847.                     ;
  848.             });
  849.             //
  850.             if ($flattened) {
  851.                 //
  852.                 $result = [];
  853.                 //
  854.                 foreach ($goodsSetPurchase as $goods) {
  855.                     //
  856.                     if ($type GoodsSetPurchase::TYPE_LIST[$goods['gsp_type']])
  857.                         //
  858.                         foreach ($goods as $key => $value) {
  859.                             //
  860.                             $result["{$type['name']}_{$key}"] = $value;
  861.                         }
  862.                 }
  863.             } else {
  864.                 //
  865.                 $result $goodsSetPurchase;
  866.             }
  867.             //
  868.             return $result;
  869.         });
  870.     }
  871.     /**
  872.      * @return GoodsSetPurchaseExtended[]
  873.      */
  874.     public function getChildrenAsGoodsSetPurchase()
  875.     {
  876.         return GoodsSetPurchaseExtended::getListByParentGoodsId($this->getGoodsId());
  877.     }
  878.     public static function getFeedGoodsByIdForYDN($goodsId$quantity 1)
  879.     {
  880.         //
  881.         $feedGoods self::getFeedGoodsById($goodsId);
  882.         //
  883.         return array(
  884.             'item_id' => $feedGoods['goods_id'],
  885.             'category_id' => $feedGoods['categoryId'],
  886.             'price' => ((int)($feedGoods['sale_price'])),
  887.             'quantity' => $quantity,
  888.         );
  889.     }
  890.     public static function getFeedGoodsByIdForGDN($goodsId$google_business_vertical 'retail')
  891.     {
  892.         //
  893.         $feedGoods self::getFeedGoodsById($goodsId);
  894.         //
  895.         $gdn = array(
  896.             'id' => $feedGoods['goods_id'],
  897.         );
  898.         if (!empty($google_business_vertical)) {
  899.             $gdn['google_business_vertical'] = $google_business_vertical;
  900.         }
  901.         return $gdn;
  902.     }
  903.     /**
  904.      * @param $goodsId
  905.      * @return string
  906.      * @link
  907.      */
  908.     public static function getFeedGoodsById($goodsId)
  909.     {
  910.         //
  911.         $result = (new SqlService())
  912.             ->Select("
  913.                     T.goods_id
  914.                 ,    T3.jan_shiire_no_list
  915.                 ,    T2.price
  916.                 ,    T2.sale_price
  917.                 ,    T1.categoryId
  918.                 ,    T1.categoryName
  919.                 ,    T3.stock
  920.                 ,    T3.lm_stock
  921.                 ,    T3.ki_date
  922.                 ,    T3.zero_period
  923.                 ,    T4.rating
  924.                 ,    T4.reviews
  925.             ")
  926.             ->Table('goods_table')
  927.             ->Join('feed_goods_category''T.goods_id = T1.goods_id''INNER')
  928.             ->Join('feed_goods_price''T.goods_id = T2.goods_id''INNER')
  929.             ->Join('feed_goods_stock''T.goods_id = T3.goods_id''INNER')
  930.             ->Join('feed_goods_review''T.goods_id = T4.goods_id''LEFT')
  931.             ->Set('T.goods_id'$goodsId)
  932.             ->Find()
  933.         ;
  934.         //
  935.         return $result;
  936.     }
  937.     /**
  938.      * @return Item
  939.      */
  940.     public function getItem()
  941.     {
  942.         return self::compute($this->item, function () {
  943.             return new Item($this);
  944.         });
  945.     }
  946.     /**
  947.      * @return TargetGenderLabel
  948.      */
  949.     public function getTargetGenderLabel()
  950.     {
  951.         return self::compute($this->targetGenderLabel, function () {
  952.             try {
  953.                 return TargetGenderLabel::getById($this->getGoodsTargetGender());
  954.             } catch(\Exception $e) {
  955.                 return null;
  956.             }
  957.         });
  958.     }
  959.     /**
  960.      * @return CategoryListIconLabel
  961.      */
  962.     public function getCategoryListIconLabel()
  963.     {
  964.         return self::compute($this->categoryListIconLabel, function () {
  965.             try {
  966.                 return CategoryListIconLabel::getById($this->getGoodsCategoryListIconId());
  967.             } catch(\Exception $e) {
  968.                 return null;
  969.             }
  970.         });
  971.     }
  972.     /**
  973.      * @return VendingStatusLabel
  974.      */
  975.     public function getVendingStatusLabel()
  976.     {
  977.         return self::compute($this->vendingStatusLabel, function () {
  978.             try {
  979.                 $status $this->getGoodsVendingStatus();
  980.                 if ($status === Goods::VENDING_STATUS_EXCLUDE_MANUFACTURER_STOCK) {
  981.                     $status Goods::VENDING_STATUS_VENDING;
  982.                 }
  983.                 return VendingStatusLabel::getById($status);
  984.             } catch(\Exception $e) {
  985.                 return null;
  986.             }
  987.         });
  988.     }
  989.     /**
  990.      * @return ColorNumberLabel
  991.      */
  992.     public function getColorNumberLabel()
  993.     {
  994.         return self::compute($this->colorNumberLabel, function () {
  995.             try {
  996.                 return ColorNumberLabel::getById(count($this->getItem()->getColorList()));
  997.             } catch(\Exception $e) {
  998.                 return null;
  999.             }
  1000.         });
  1001.     }
  1002.     /**
  1003.      * @return Label[]
  1004.      */
  1005.     public function getLabelList()
  1006.     {
  1007.         return self::compute($this->labelList, function () {
  1008.             return array_filter([
  1009.                 $this->getTargetGenderLabel(),
  1010.                 $this->getColorNumberLabel(),
  1011.                 $this->getCategoryListIconLabel(),
  1012.             ], function ($label) {
  1013.                 return $label !== null;
  1014.             });
  1015.         });
  1016.     }
  1017.     /**
  1018.      * @return mixed
  1019.      * @throws \Exception
  1020.      */
  1021.     public function isSokujitsuHassouAvailable()
  1022.     {
  1023.         //
  1024.         return self::compute($this->isSokujitsuHassouAvailable, function () {
  1025.             //
  1026.             return (new SokujitsuHassou())
  1027.                 ->check($this->getGoodsId(), false) > SokujitsuHassou::FLG_NONE
  1028.             ;
  1029.         });
  1030.     }
  1031. }