vendor/lm/engine-ec/src/Entity/CategoryContentsBlockMapModel.php line 184

Open in your IDE?
  1. <?php
  2. /**
  3.  * CategoryContentsBlockMapModel.php
  4.  * @author: worker_h
  5.  */
  6. namespace Lm\Engine\EC\Entity;
  7. use Lm\Service\Db\SqlService;
  8. class CategoryContentsBlockMapModel
  9. {
  10.     const TEMPLATE_NAME_BLOCK 'category-col-block';
  11.     const TEMPLATE_NAME_ITEM_BLOCK_NORMAL 'category-col-item-block-normal';
  12.     const TEMPLATE_NAME_ITEM_BLOCK_MENS_LADIES 'category-col-item-block-mens-ladies';
  13.     const KIHON_IMAGE_PATH '/item/kihon/%s/%s.jpg';
  14.     const MODEL_IMAGE_PATH '/item/kihon/%s/%s-%d-%dcol.jpg';    // バインドするのは「仕入先URL」「型番」「モデル番号」「カラム数」
  15.     const MODEL_PORTRAIT_IMAGE_PATH '/item/kihon/%s/%s-portrait-%d-4col.jpg';    // バインドするのは「仕入先URL」「型番」「モデル縦長番号」(モデル縦長画像は4カラムのみ)
  16.     const MODEL_PORTRAIT_PAIR_IMAGE_PATH '/item/kihon/%s/%s-portrait-pair-4col.jpg';    // バインドするのは「仕入先URL」「型番」(モデル縦長ペア画像は4カラムのみ)
  17.     const IMAGE_COORDINATE_PATH '/item/kihon/%s/%s-%d-%dcol-coo%d.jpg';    // バインドするのは「仕入先URL」「型番」「モデル番号」「カラム数」「コーデ番号-」
  18.     const GOODS_BANNER_IMAGE_PATH '/item/kihon/%s/%s-%d-1col.jpg';    // バインドするのは「仕入先URL」「型番」「バナー番号」
  19.     const HEADER_TYPE_MIDASHI           1;    // 見出し
  20.     const HEADER_TYPE_ONLY_IMAGE        2;    // 画像のみ
  21.     const HEADER_TYPE_MIDASHI_AND_IMAGE 3;    // 見出し+画像
  22.     const HEADER_TYPE_HTML              4;    // 自由記述
  23.     const HEADER_TYPE_NO_HEADER         5;    // ヘッダーなし
  24.     const HEADER_TYPE_ITEM_BANNER       6;    // 商品バナー
  25.     const ITEM_IMAGE_TYPE_GOODS 1;    // 商品
  26.     const ITEM_IMAGE_TYPE_MODEL 2;    // モデル
  27.     const DESIGN_TYPE_NORMAL      1;    // 標準
  28.     const DESIGN_TYPE_MENS_LADIES 2;    // メンズ・レディース系
  29.     const RANK_MARK_NORMAL 1;        // 標準
  30.     const RANK_MARK_CROWN  2;        // 王冠
  31.     // もっと見る 表示タイプ
  32.     const MORE_TYPE_DISABLED 0;        // ボタン
  33.     const MORE_TYPE_BUTTON 1;            // ボタン
  34.     const MORE_TYPE_ONELINER_TEXT 2;    // 1行テキスト
  35.     
  36.     const BOOL_TRUE 1;
  37.     const BOOL_FALSE 0;
  38.     public function getContentsBlockMapById($main_category_id$category_id=null){
  39.         $map = array();
  40.         $rows = (array)(new SqlService())->Sql('
  41.             SELECT
  42.                 main_category_id,
  43.                 category_id,
  44.                 block_position,
  45.                 GROUP_CONCAT(contents_block_id ORDER BY block_order) AS contents_block_id_list
  46.             FROM
  47.                 category_contents_block_map_table 
  48.             WHERE
  49.                 main_category_id = :main_category_id AND
  50.                 IFNULL(category_id, 0) = :category_id
  51.             GROUP BY
  52.                 main_category_id, category_id, block_position
  53.         ')->Params([
  54.             'main_category_id' => $main_category_id,
  55.             'category_id' => (int)$category_id,
  56.         ])->FetchAll();
  57.         $block_id_list = array();
  58.         foreach($rows as $row){
  59.             $block_id_list array_merge($block_id_listexplode(','$row['contents_block_id_list']));
  60.         }
  61.         $block_id_list array_unique($block_id_list);
  62.         $block_map $this->getContentsBlockMap($block_id_list);
  63.         foreach($rows as $row){
  64.             // ブロックidごとの処理
  65.             foreach( explode(','$row['contents_block_id_list']) as $block_id ){
  66.                 if( !isset($block_map[$block_id]) ) continue;
  67.                 $map[$row['block_position']][] = $block_map[$block_id];
  68.             }
  69.         }
  70.         return $map;
  71.     }
  72.     private function getContentsBlockMap($id_list){
  73.         if( !is_array($id_list) || empty($id_list) ) return array();
  74.         // 必要なテンプレートがなければ抜ける。
  75.         $template $this->getTemplate();
  76.         if(
  77.             !isset($template[self::TEMPLATE_NAME_BLOCK]) ||
  78.             !isset($template[self::TEMPLATE_NAME_ITEM_BLOCK_NORMAL]) ||
  79.             !isset($template[self::TEMPLATE_NAME_ITEM_BLOCK_MENS_LADIES])
  80.         ) return array();
  81.         $block_tamplate $template[self::TEMPLATE_NAME_BLOCK];
  82.         $contents_block_list $this->getContentsBlockList($id_list);
  83.         
  84.         // ブロックコンテンツがなければ抜ける。
  85.         if( empty($contents_block_list) ) return array();
  86.         // 最後のブロックID
  87.         if (empty($id_list)) {
  88.             echo '<script>console.log("id_list is empty");</script>';
  89.             $last_block_id null;
  90.         } else {
  91.             $id_list_values array_values($id_list);
  92.             $last_block_id $id_list_valuescount($id_list_values) - ];
  93.             echo '<script>console.log("last_block_id resolved to: ' $last_block_id '");</script>';
  94.         }
  95.         $contents_block_map $goods_id_list = array();
  96.         foreach($contents_block_list as $contents_block){
  97.             // 「ヘッダーのみ」の場合goods_id_listがnullになっている
  98.             if( is_null($contents_block['goods_id_list']) ) continue;
  99.             $goods_id_list array_merge($goods_id_listexplode(','$contents_block['goods_id_list']));
  100.         }
  101.         // 商品情報有無(ブロックの商品有無ではなく、全コンテンツブロックの商品有無)
  102.         if( !empty($goods_id_list) ){
  103.             $goods_id_list array_unique($goods_id_list);
  104.             // 商品情報取得
  105.             $item_data_map $this->getItemDataMap($goods_id_list);
  106.             $kataban_map $this->getGoodsKatabanMap();
  107.         }
  108.         // 商品バナー情報
  109.         $goods_banner_option_map $this->getGoodsBannerOptionMap();
  110.         $map = array();
  111.         // ブロックにデータを構築(ブロックごとの処理)
  112.         $column_number_before null;        // ヘッダーなしの上のブロックとのマージン調整のため、前のカラム数を記録しておく。
  113.         foreach($contents_block_list as $block_idx => $contents_block){
  114.             
  115.             // 基本要素にあてるクラス
  116.             $base_block_class_list = array('managed-contents-block-'.(int)$contents_block['column_number'].'col');
  117.             // 最後のブロッククラス
  118.             if( (int)$last_block_id === (int)$contents_block['contents_block_id'] ){
  119.                 $base_block_class_list[] = 'managed-contents-block-last';
  120.             }
  121.             
  122.             // 商品リストデザインによってクラスをわける
  123.             switch( (int)$contents_block['design_type'] ){
  124.                 // メンズ・レディース系
  125.                 case self::DESIGN_TYPE_MENS_LADIES:
  126.                     $base_block_class_list[] = 'managed-contents-block-item-list-mens-ladies';
  127.                     break;
  128.                 default:
  129.                     break;
  130.             }
  131.             
  132.             $block_part $template[self::TEMPLATE_NAME_BLOCK];
  133.             switch((int)$contents_block['header_type']){
  134.                 case self::HEADER_TYPE_MIDASHI:
  135.                     $this->setMidashiHeaderToContents($block_part$contents_block);
  136.                     break;
  137.                 case self::HEADER_TYPE_ONLY_IMAGE:
  138.                     $this->setImageHeaderToContents($block_part$contents_block);
  139.                     break;
  140.                 case self::HEADER_TYPE_MIDASHI_AND_IMAGE:
  141.                     $this->setMidashiImageHeaderToContents($block_part$contents_block$base_block_class_list);
  142.                     break;
  143.                 case self::HEADER_TYPE_HTML:
  144.                     $this->setHtmlHeaderToContents($block_part$contents_block);
  145.                     break;
  146.                 case self::HEADER_TYPE_NO_HEADER:
  147.                     $this->setNoHeaderToContents($block_part$contents_block$column_number_before$base_block_class_list);
  148.                     break;
  149.                 case self::HEADER_TYPE_ITEM_BANNER:
  150.                     $this->setItemBannerHeaderToContents($block_part$contents_block$base_block_class_list$goods_banner_option_map);
  151.                     
  152.                     // 商品バナーの時のテキスト、リンクの上書き処理
  153.                     if(
  154.                         (int)$contents_block['more_button_type'] === self::MORE_TYPE_ONELINER_TEXT &&
  155.                         isset($goods_banner_option_map[$contents_block['contents_block_id']])
  156.                     ){
  157.                         $goods_banner_data $goods_banner_option_map[$contents_block['contents_block_id']];
  158.                         
  159.                         if( !empty($goods_banner_data['oneliner_text']) ) $contents_block['more_button_text'] = $goods_banner_data['oneliner_text'];
  160.                         if( !empty($goods_banner_data['url']) ) $contents_block['more_button_link_url'] = $goods_banner_data['url'];
  161.                     }
  162.                     break;
  163.             }
  164.             $column_number_before = (int)$contents_block['column_number'];
  165.             $this->setDescriptionToContents($block_part$contents_block);
  166.             // もっと見る
  167.             if(
  168.                 (int)$contents_block['more_button_type'] !== self::MORE_TYPE_DISABLED &&
  169.                 !empty($contents_block['more_button_text']) &&
  170.                 !empty($contents_block['more_button_link_url'])
  171.             ){
  172.                 $class_name '';
  173.                 switch( (int)$contents_block['more_button_type'] ){
  174.                     case self::MORE_TYPE_BUTTON:
  175.                         $class_name 'managed-contents-block-more-link-button';
  176.                         break;
  177.                     case self::MORE_TYPE_ONELINER_TEXT:
  178.                         $class_name 'managed-contents-block-more-link-oneliner-text';
  179.                         break;
  180.                 }
  181.                 $block_part str_replace(
  182.                     '<div class="managed-contents-block-footer"></div>',
  183.                     '<div class="'.$class_name.' managed-contents-block-footer"><a href="'.htmlspecialchars($contents_block['more_button_link_url']).'">'.htmlspecialchars($contents_block['more_button_text']).'</a></div>',
  184.                     $block_part
  185.                 );
  186.             }
  187.             else{
  188.                 $block_part str_replace('<div class="managed-contents-block-footer"></div>'''$block_part);
  189.             }
  190.             // 色設定
  191.             $block_part $this->setColor($block_part$contents_block);
  192.             // 基本要素にクラスを当てる
  193.             $block_part str_replace(
  194.                 '<div class="managed-contents-block"',
  195.                 '<div class="managed-contents-block '.implode(' '$base_block_class_list).'"',
  196.                 $block_part
  197.             );
  198.             // ブロックに商品情報がなければ商品リスト箇所を削除してマップに入れて次のブロックへ
  199.             if( is_null($contents_block['goods_id_list']) ) {
  200.                 $block_part str_replace('<goods-list />'''$block_part);
  201.                 $map[$contents_block['contents_block_id']] = $block_part;
  202.                 continue;
  203.             }
  204.             // 商品ごとの処理
  205.             $item_part_list = array();
  206.             $goods_count 1;
  207.             foreach( explode(','$contents_block['goods_id_list']) as $idx => $goods_id ){
  208.                 // 商品情報がなければ次へ
  209.                 if( !isset($item_data_map[$goods_id]) ) continue;
  210.                 $goods_data $item_data_map[$goods_id];
  211.                 $block_class '';
  212.                 // 商品リストのデザインタイプ
  213.                 switch( (int)$contents_block['design_type'] ){
  214.                     case self::DESIGN_TYPE_NORMAL:
  215.                         $item_part $template[self::TEMPLATE_NAME_ITEM_BLOCK_NORMAL];
  216.                         break;
  217.                     case self::DESIGN_TYPE_MENS_LADIES:
  218.                         $item_part $template[self::TEMPLATE_NAME_ITEM_BLOCK_MENS_LADIES];
  219.                         $goods_data['color'] = array_slice($goods_data['color'], 010);    // カラーチップは最大10個まで
  220.                         break;
  221.                 }
  222.                 
  223.                 $item_part $this->setItem(
  224.                     $item_part
  225.                     $goods_data
  226.                     $contents_block
  227.                     $kataban_map
  228.                     $idx,
  229.                     $goods_count++
  230.                 );
  231.                 
  232.                 $item_part_list[] = $item_part;
  233.             }
  234.             // 商品リストデザインが「メンズ・レディース系」(space-betweenじゃないのでliを補完する必要なし)
  235.             if( (int)$contents_block['design_type'] === self::DESIGN_TYPE_MENS_LADIES ){
  236.                 // 2カラム画像として表示の時、非2colのliを隠す処理
  237.                 $this->set2colLi$item_part_list );
  238.             }
  239.             // 不足分のliを埋める(間隔調整などspace-betweenの方が楽なので、space-between+不足分を埋める実装)
  240.             else{
  241.                 $item_part_list $this->fillItemList$item_part_list$contents_block['column_number'] );
  242.             }
  243.             $block_part str_replace('<goods-list />'implode(''$item_part_list), $block_part);
  244.             
  245.             // 補足説明テキストを追加
  246.             if( (int)$contents_block['design_type'] === self::DESIGN_TYPE_MENS_LADIES ){
  247.                 $block_part $this->setAdditionalText($block_part$contents_block);
  248.             }
  249.             $map[$contents_block['contents_block_id']] = $block_part;
  250.         }
  251.         
  252.         return $map;
  253.     }
  254.     private function setNoHeaderToContents(&$block_part$contents_block$column_number_before, &$base_block_class_list){
  255.         if( !is_null($column_number_before) ){
  256.             $base_block_class_list[] = 'managed-contents-block-no-header';
  257.         }
  258.         $block_part str_replace('<div class="managed-contents-block-midashi-image"></div>'''$block_part);
  259.         $block_part preg_replace('!<div class="managed-contents-block-header-text-wrap">.*?</div>!s'''$block_part);
  260.         $block_part str_replace('<div class="managed-contents-block-header-html"></div>'''$block_part);
  261.         $block_part preg_replace('!<div class="managed-contents-block-header">.*?</div>!s'''$block_part);
  262.     }
  263.     private function setHtmlHeaderToContents(&$block_part$contents_block){
  264.         $block_part str_replace('<div class="managed-contents-block-midashi-image"></div>'''$block_part);
  265.         $block_part preg_replace('!<div class="managed-contents-block-header-text-wrap">.*?</div>!s'''$block_part);
  266.         $block_part str_replace('<div class="managed-contents-block-header-html"></div>''<div class="managed-contents-block-header-html">'.$contents_block['header_html'].'</div>'$block_part);
  267.     }
  268.     private function setMidashiImageHeaderToContents(&$block_part$contents_block, &$base_block_class_list){
  269.         $base_block_class_list[] = 'managed-contents-block-midashi-and-text';
  270.         $this->setMidashiHeaderToContents($block_part$contents_block);
  271.         $this->setImageHeaderToContents($block_part$contents_blockfalse);
  272.         
  273.         $style sprintf('style="top: %dpx; left: %dpx"'$contents_block['header_text_pos_y'], $contents_block['header_text_pos_x']);
  274.         $block_part str_replace('<div class="managed-contents-block-header-text-wrap">''<div class="managed-contents-block-header-text-wrap" '.$style.'>'$block_part);
  275.         $block_part str_replace('<midashi-header-description />'nl2br($contents_block['header_description']), $block_part);
  276.     }
  277.     private function setItemBannerHeaderToContents(&$block_part$contents_block, &$base_block_class_list$goods_banner_option_map){
  278.         
  279.         if( !isset($goods_banner_option_map[$contents_block['contents_block_id']]) ) return;
  280.         // バナーデータ
  281.         $goods_banner_data $goods_banner_option_map[$contents_block['contents_block_id']];
  282.         $base_block_class_list[] = 'managed-contents-block-goods-banner';
  283.         
  284.         // バナーデータで見出しなどのデータを上書き
  285.         $contents_block['header_image_url'] = $goods_banner_data['image_src'];
  286.         $contents_block['header_image_alt'] = $goods_banner_data['image_alt'];
  287.         if( !is_null($goods_banner_data['text_1']) ) $contents_block['midashi_large'] = $goods_banner_data['text_1'];
  288.         if( !is_null($goods_banner_data['text_2']) ) $contents_block['midashi_small'] = $goods_banner_data['text_2'];
  289.         if( !is_null($goods_banner_data['description']) ) $contents_block['header_description'] = $goods_banner_data['description'];
  290.         if( !is_null($goods_banner_data['pos_x']) ) $contents_block['header_text_pos_x'] = $goods_banner_data['pos_x'];
  291.         if( !is_null($goods_banner_data['pos_y']) ) $contents_block['header_text_pos_y'] = $goods_banner_data['pos_y'];
  292.         // 見出しと画像をセット
  293.         $this->setMidashiHeaderToContents($block_part$contents_block);
  294.         $this->setItemBannerImageToHeaderContents($block_part$contents_block);
  295.         
  296.         // ブロックdom
  297.         // DOM修正が複雑になるためDOMDocumentを使用する。
  298.         $dom self::getDomDocumentFromBlockPart($block_part);
  299.         if( !$dom ) return;
  300.         
  301.         // バナーアンカーエレメント
  302.         $banner_a_elem null;
  303.         // バナー全体に商品ページへのリンクを設置
  304.         foreach( $dom->getElementsByTagName('div') as $elem ){
  305.             if( $elem->getAttribute('class') !== 'managed-contents-block-midashi-image' ) continue;
  306.             $banner_a_elem $dom->createElement('a');
  307.             $banner_a_elem->setAttribute('class''managed-contents-block-goods-banner-link');
  308.             $banner_a_elem->setAttribute('href'$goods_banner_data['url']);
  309.             $elem->appendChild$banner_a_elem );
  310.             break;
  311.         }
  312.         // 画像をバナーに移す
  313.         $banner_img_elem $dom->getElementsByTagName('img')->item(0);
  314.         if( !is_null($banner_img_elem) ){
  315.             $banner_a_elem->appendChild($banner_img_elem);
  316.         }
  317.         // テキストエリアをバナーに移す
  318.         foreach( $dom->getElementsByTagName('div') as $elem ){
  319.             if( $elem->getAttribute('class') !== 'managed-contents-block-header-text-wrap' ) continue;
  320.             // ポジションセット
  321.             $elem->setAttribute('style'sprintf(
  322.                 'top: %dpx; left: %dpx',
  323.                 $contents_block['header_text_pos_y'],
  324.                 $contents_block['header_text_pos_x']
  325.             ));
  326.             $banner_a_elem->appendChild($elem);
  327.             break;
  328.         }
  329.         
  330.         $replace_elem $banner_a_elem->getElementsByTagName('midashi-header-description')->item(0);
  331.         if( !is_null($replace_elem) ) $replace_elem->parentNode->removeChild($replace_elem);
  332.         // バナー説明文設置
  333.         foreach( $banner_a_elem->getElementsByTagName('p') as $p_elem ){
  334.             if( $p_elem->getAttribute('class') !== 'managed-contents-block-header-description' ) continue;
  335.             $inner $dom->createElement('span');
  336.             $inner->setAttribute('class''managed-contents-block-header-description-inner');
  337.             $text nl2brhtmlspecialchars($contents_block['header_description']) );
  338.             $inner->appendChild$dom->createTextNode($text) );
  339.             $p_elem->appendChild$inner );
  340.             break;
  341.         };
  342.         // フォント設定を反映
  343.         if($goods_banner_data['text_1_size']) $contents_block['midashi_large_size'] = $goods_banner_data['text_1_size'];
  344.         if($goods_banner_data['text_1_color']) $contents_block['midashi_large_color'] = $goods_banner_data['text_1_color'];
  345.         if($goods_banner_data['text_1_bg_color']) $contents_block['midashi_large_bg_color'] = $goods_banner_data['text_1_bg_color'];
  346.         if($goods_banner_data['text_2_size']) $contents_block['midashi_small_size'] = $goods_banner_data['text_2_size'];
  347.         if($goods_banner_data['text_2_color']) $contents_block['midashi_small_color'] = $goods_banner_data['text_2_color'];
  348.         if($goods_banner_data['text_2_bg_color']) $contents_block['midashi_small_bg_color'] = $goods_banner_data['text_2_bg_color'];
  349.         if($goods_banner_data['description_size']) $contents_block['header_description_size'] = $goods_banner_data['description_size'];
  350.         if($goods_banner_data['description_color']) $contents_block['header_description_color'] = $goods_banner_data['description_color'];
  351.         if($goods_banner_data['description_bg_color']) $contents_block['header_description_bg_color'] = $goods_banner_data['description_bg_color'];
  352.         $has_bg_color_class 'managed-contents-block-text-has-bg-color';
  353.         $use_shadow_class 'managed-contents-block-text-shadow';
  354.         /**
  355.          * @var \DOMNode $elem
  356.          */
  357.         foreach( $banner_a_elem->getElementsByTagName('*') as $elem ){
  358.             if( !in_array(
  359.                 $elem->getAttribute('class'),
  360.                 [
  361.                     'managed-contents-block-midashi-large',
  362.                     'managed-contents-block-midashi-small',
  363.                     'managed-contents-block-header-description-inner'
  364.                 ]
  365.             )) continue;
  366.             $style = [];
  367.             $add_class '';
  368.             $style_text '';
  369.             switch( $elem->getAttribute('class') ){
  370.                 // 見出し1
  371.                 case 'managed-contents-block-midashi-large':
  372.                     if( !empty($contents_block['midashi_large_size']) ) $style[] = "font-size: {$contents_block['midashi_large_size']}px;";
  373.                     if( !empty($contents_block['midashi_large_color']) ) $style[] = "color: {$contents_block['midashi_large_color']};";
  374.                     if( !empty($contents_block['midashi_large_bg_color']) && !empty($elem->textContent) ){
  375.                         $style[] = "background: {$contents_block['midashi_large_bg_color']};";
  376.                         $add_class $has_bg_color_class;
  377.                     }
  378.                     if( (int)$contents_block['midashi_large_use_shadow'] === ){
  379.                         $add_class .= ' '.$use_shadow_class;
  380.                     }
  381.                     break;
  382.                 // 見出し2
  383.                 case 'managed-contents-block-midashi-small':
  384.                     if( !empty($contents_block['midashi_small_size']) ) $style[] = "font-size: {$contents_block['midashi_small_size']}px;";
  385.                     if( !empty($contents_block['midashi_small_color']) ) $style[] = "color: {$contents_block['midashi_small_color']};";
  386.                     if( !empty($contents_block['midashi_small_bg_color']) ){
  387.                         $style[] = "background: {$contents_block['midashi_small_bg_color']};";
  388.                         $add_class $has_bg_color_class;
  389.                     }
  390.                     if( (int)$contents_block['midashi_small_use_shadow'] === ){
  391.                         $add_class .= ' '.$use_shadow_class;
  392.                     }
  393.                     break;
  394.                 // 説明文
  395.                 case 'managed-contents-block-header-description-inner':
  396.                     if( !empty($contents_block['header_description_size']) ) $style[] = "font-size: {$contents_block['header_description_size']}px;";
  397.                     if( !empty($contents_block['header_description_color']) ) $style[] = "color: {$contents_block['header_description_color']};";
  398.                     if( !empty($contents_block['header_description_bg_color']) ){
  399.                         $style[] = "background: {$contents_block['header_description_bg_color']};";
  400.                         $add_class $has_bg_color_class;
  401.                     }
  402.                     break;
  403.             }
  404.             $style_text = empty($style) ? '' implode(' '$style);
  405.             $class $elem->getAttribute('class').' '.$add_class;
  406.             $elem->setAttribute('class'$class);
  407.             $elem->setAttribute('style'$style_text);
  408.         }
  409.         // 価格表示
  410.         // 価格ajaxの実装に合わせるため、get-price-ajax、throughの位置を調整
  411.         // 価格ajaxの数字が基準になるため、パーセンテージアイコンはJSで出力する以外方法がない。
  412.         if( $goods_banner_data['show_price'] ){
  413.             $banner_a_elem->appendChild$dom->createTextNode('
  414.                 <span class="managed-contents-block-retail-prices">希望小売価格 <span class="through"></span></span>
  415.                 <div class="managed-contents-block-price-box">
  416.                     <div class="managed-contents-block-list-price through"></div>
  417.                     <div>
  418.                         <span class="managed-contents-block-list-price get-price-ajax formed" data-item-id="'.$goods_banner_data['goods_id'].'"></span><span class="yen"> 円</span><span class="zei">(税込)</span>
  419.                     </div>
  420.                 </div>
  421.             '));
  422.         }
  423.         // SALEアイコン
  424.         if( $goods_banner_data['show_sale_icon'] ){
  425.             $banner_a_elem->appendChild$dom->createTextNode('
  426.                 <div class="managed-contents-block-sale-icon">
  427.                     <img />
  428.                 </div>
  429.             '));
  430.         }
  431.         $block_part self::getBlockPartFromDomDocument($dom);
  432.         // DOMDocumentで整形された箇所を元に戻す
  433.         $block_part str_replace('<description/>''<description />'$block_part);
  434.         $block_part str_replace('<goods-list/>''<goods-list />'$block_part);
  435.         $block_part str_replace('<div class="managed-contents-block-footer"/>''<div class="managed-contents-block-footer"></div>'$block_part);
  436.         // 不要な要素を削除
  437.         $block_part str_replace('<div class="managed-contents-block-header-html"/>'''$block_part);
  438.         
  439.     }
  440.     private function setImageHeaderToContents(&$block_part$contents_block$delete_other=true){
  441.         $img sprintf(
  442.             '<img src="%s" alt="%s">',
  443.             // CFIMG_URL . preg_replace('!^/+ut_img/!', '', $contents_block['header_image_url']),
  444.             $contents_block['header_image_url'],
  445.             htmlspecialchars($contents_block['header_image_alt'])
  446.         );
  447.         $block_part str_replace('<div class="managed-contents-block-midashi-image"></div>''<div class="managed-contents-block-midashi-image">'.$img.'</div>'$block_part);
  448.         if(!$delete_other) return;
  449.         $block_part preg_replace('!<div class="managed-contents-block-header-text-wrap">.+?</div>!s'''$block_part);
  450.     }
  451.     /**
  452.      * @todo ヘッダーの画像操作を統一
  453.      */
  454.     private function setItemBannerImageToHeaderContents(&$block_part$contents_block){
  455.         $img sprintf(
  456.             '<img src="%s" alt="%s">',
  457.             $contents_block['header_image_url'],
  458.             htmlspecialchars($contents_block['header_image_alt'])
  459.         );
  460.         $block_part str_replace('<div class="managed-contents-block-midashi-image"></div>''<div class="managed-contents-block-midashi-image">'.$img.'</div>'$block_part);
  461.     }
  462.     private function setDescriptionToContents(&$block_part$contents_block){
  463.         // 説明文非表示
  464.         if( (int)$contents_block['show_description'] === ){
  465.             $block_part str_replace('<p class="managed-contents-block-description"><description /></p>'''$block_part);
  466.             return;
  467.         }
  468.         
  469.         // ボーダー非表示
  470.         if( (int)$contents_block['show_description_border'] === ){
  471.             $block_part str_replace('class="managed-contents-block-description"''class="managed-contents-block-description managed-contents-block-description-no-border"'$block_part);
  472.         }
  473.         $block_part str_replace('<description />'nl2br(htmlspecialchars($contents_block['description'])), $block_part);
  474.     }
  475.     private function setMidashiHeaderToContents(&$block_part$contents_block){
  476.         // 大見出し
  477.         $block_part str_replace('<midashi-large />'htmlspecialchars($contents_block['midashi_large']), $block_part);
  478.         // 小見出し
  479.         if( empty($contents_block['midashi_small']) ){
  480.             $block_part str_replace('<p class="managed-contents-block-midashi-small"><midashi-small /></p>'''$block_part);
  481.         }
  482.         else{
  483.             $block_part str_replace('<midashi-small />'htmlspecialchars($contents_block['midashi_small']), $block_part);
  484.         }
  485.     }
  486.     private function setItem($item_part$goods_data$contents_block$kataban_map$item_idx$goods_count){
  487.         // liにあてるクラスをためる
  488.         $li_class = array();
  489.         // 商品URL
  490.         self::setLink($item_part$goods_data);
  491.         // 価格
  492.         self::setPrice($item_part$goods_data);
  493.         // 型番
  494.         self::setHinban($contents_block$item_part$goods_data$item_idx);
  495.         // カラーチップ
  496.         self::setColorTip($item_part$goods_data);
  497.         switch($contents_block['design_type']){
  498.             case self::DESIGN_TYPE_NORMAL:
  499.                 // ランキング
  500.                 self::setRanking($contents_block$item_part$item_idx$li_class);
  501.                 // 商品画像
  502.                 self::setMainImage($contents_block$item_part$goods_data$item_idx);
  503.                 // 即日配送画像
  504.                 self::setDirectDeliveryImage($contents_block$item_part$goods_data$item_idx);
  505.                 // 説明文
  506.                 self::setDescriptionText($contents_block$item_part$goods_data);
  507.                 // コーディネート
  508.                 self::setModelCoordinate($contents_block$item_part$goods_data$item_idx$kataban_map$li_class);
  509.                 break;
  510.             case self::DESIGN_TYPE_MENS_LADIES:
  511.                 $this->setMensLadies($contents_block$item_part$goods_data$item_idx);
  512.                 break;
  513.         }
  514.         $item_part str_replace(
  515.             '<li class=""',
  516.             '<li class="'.implode(' '$li_class).'"'
  517.             $item_part
  518.         );
  519.         return $item_part;
  520.     }
  521.     private static function setRanking($contents_block, &$item_part$item_idx, &$li_class){
  522.         if( (int)$contents_block['show_ranking_number'] === ){
  523.             $rank_num = (int)$contents_block['ranking_start_number'] + (int)$item_idx;
  524.             $li_class[] = 'managed-contents-block-item-rank-list';
  525.             
  526.             switch($rank_num){
  527.                 case 1:
  528.                     $class 'managed-contents-block-item-rank-gold';
  529.                     break;
  530.                 case 2:
  531.                     $class 'managed-contents-block-item-rank-silver';
  532.                     break;
  533.                 default:
  534.                     $class 'managed-contents-block-item-rank-bronze';
  535.                     break;
  536.             }
  537.             if( (int)$contents_block['ranking_mark_type'] === self::RANK_MARK_CROWN ){
  538.                 $class .= ' managed-contents-block-item-rank-mark-crown';
  539.             }
  540.             $item_part str_replace('<span class="managed-contents-block-item-rank"''<span class="managed-contents-block-item-rank '.$class.'"'$item_part);
  541.             $item_part str_replace('<span class="managed-contents-block-item-rank-label"></span>''<span class="managed-contents-block-item-rank-label">第'.$rank_num.'位</span>'$item_part);
  542.         }
  543.         else{
  544.             $item_part str_replace('<span class="managed-contents-block-item-rank"><span class="managed-contents-block-item-rank-label"></span></span>'''$item_part);
  545.         }
  546.         
  547.     }
  548.     
  549.     
  550.     
  551.     
  552.     private function setModelCoordinate($contents_block, &$item_part$goods_data$item_idx$kataban_map, &$li_class){
  553.         if( (int)$contents_block['show_coordinate_goods'] === ){
  554.             $model_num_list explode(','$contents_block['model_num_list']);
  555.             $model_num = (int)$model_num_list[$item_idx];
  556.             $column_num $contents_block['column_number'];
  557.             $coo_limit 2;
  558.             $coo_count 0;
  559.             for( $coo_num 1$coo_num <= $coo_limit$coo_num++ ){
  560.                 // id
  561.                 $field "model_{$model_num}_{$column_num}col_coordinate_goods_{$coo_num}_id";
  562.                 $coo_goods_id $goods_data[$field];
  563.                 if( empty($coo_goods_id) || !isset($kataban_map[$coo_goods_id]) ){
  564.                     $item_part preg_replace('!<li data-coo-li="'.$coo_num.'">.*?</li>!s'''$item_part1);
  565.                     continue;
  566.                 }
  567.                 $coo_count++;
  568.                 // src
  569.                 $src sprintf(
  570.                     self::IMAGE_COORDINATE_PATH,
  571.                     $goods_data['shiiresaki_url'],
  572.                     $goods_data['goods_main_kataban'],
  573.                     $model_num,
  574.                     $column_num,
  575.                     $coo_num
  576.                 );
  577.                 // alt
  578.                 $field "model_{$model_num}_{$column_num}col_coordinate_goods_{$coo_num}_alt";
  579.                 $alt $goods_data[$field];
  580.                 $img sprintf('<img src="%s" alt="%s">'$src$alt);
  581.                 $item_part preg_replace(
  582.                     '!<div class="managed-contents-block-item-model-coordinate-figure"></div>!',
  583.                     '<div class="managed-contents-block-item-model-coordinate-figure">'.$img.'</div>',
  584.                     $item_part,
  585.                     1
  586.                 );
  587.                 // href
  588.                 $href '/item/';
  589.                 $href .= $kataban_map[$coo_goods_id];
  590.                 $href .= '.html';
  591.                 $item_part preg_replace('!<a href="" data-coo-url>!''<a href="'.$href.'">'$item_part1);
  592.                 // 価格
  593.                 $item_part preg_replace('/data-item-id=""/''data-item-id="'.$coo_goods_id.'"'$item_part1);
  594.             }
  595.             
  596.             if( $coo_count === ){
  597.                 $item_part preg_replace('!<div class="managed-contents-block-item-model-coordinate">.*</li>!s''</li>'$item_part);
  598.             }
  599.             else{
  600.                 $li_class[] = 'has-coo';
  601.                 $item_part str_replace(
  602.                     '<span class="managed-contents-block-item-model-coordinate-label"></span>',
  603.                     '<span class="managed-contents-block-item-model-coordinate-label">↓モデルの着用コーデ↓</span>',
  604.                     $item_part
  605.                 );
  606.             }
  607.         }
  608.         else{
  609.             $item_part preg_replace('!<div class="managed-contents-block-item-model-coordinate">.*</li>!s''</li>'$item_part);
  610.         }
  611.     }
  612.     public function getGoodsKatabanMap(){
  613.         
  614.         $rows = (new SqlService())->Sql('
  615.             SELECT
  616.                 goods_id,
  617.                 goods_canonical_hinban,
  618.                 goods_main_kataban
  619.             FROM
  620.                 goods_table
  621.             WHERE
  622.                 goods_ddate IS NULL AND
  623.                 goods_status = 1
  624.         ')->FetchAll();
  625.         $map = array();
  626.         foreach($rows as $row){
  627.             $map[$row['goods_id']] = empty($row['goods_canonical_hinban']) ? $row['goods_main_kataban'] : $row['goods_canonical_hinban'];
  628.         }
  629.         return $map;
  630.     }
  631.     
  632.     
  633.     
  634.     
  635.     private function setColor($block_part$contents_block_data){
  636.         // 全体背景とフォント
  637.         $block_part str_replace(
  638.             '<div class="managed-contents-block">',
  639.             '<div class="managed-contents-block" style="background: '.$contents_block_data['bg_color'].'; color: '.$contents_block_data['text_color'].'">',
  640.             $block_part
  641.         );
  642.         // h2
  643.         $block_part str_replace(
  644.             '<h2 class',
  645.             '<h2 style="background: '.$contents_block_data['element_bg_color'].'; color: '.$contents_block_data['element_text_color'].';" class',
  646.             $block_part
  647.         );
  648.         // 画像上説明文
  649.         $block_part str_replace(
  650.             '<p class="managed-contents-block-header-description">',
  651.             '<p class="managed-contents-block-header-description" style="color: '.$contents_block_data['element_text_color'].'">',
  652.             $block_part
  653.         );
  654.         // コーディネート
  655.         $block_part str_replace(
  656.             '<div class="managed-contents-block-item-model-coordinate">',
  657.             '<div class="managed-contents-block-item-model-coordinate" style="background: '.$contents_block_data['bg_color'].';">',
  658.             $block_part
  659.         );
  660.         // フッター
  661.         // 1行テキストの場合は背景色設定無効
  662.         if( (int)$contents_block_data['more_button_type'] === self::MORE_TYPE_BUTTON ){
  663.             $block_part str_replace(
  664.                 'managed-contents-block-footer"><a ',
  665.                 'managed-contents-block-footer"><a style="background: '.$contents_block_data['element_bg_color'].'; color: '.$contents_block_data['element_text_color'].';" ',
  666.                 $block_part
  667.             );
  668.         }
  669.         
  670.         return $block_part;
  671.     }
  672.     private function getItemDataMap($goods_id_list){
  673.         
  674.         if( !is_array($goods_id_list) || empty($goods_id_list) ) return [];
  675.         $map = array();
  676.         $goods_id_list array_map( function($goods_id){
  677.             return (int)$goods_id;
  678.         }, $goods_id_list);
  679.         $in_clause implode(','$goods_id_list);
  680.         
  681.         $color_map $this->getGoodsColorMap($goods_id_list);
  682.         $rows = (new SqlService())->Sql("
  683.             SELECT
  684.                 g.goods_name,
  685.                 g.goods_canonical_hinban,
  686.                 g.goods_main_kataban,
  687.                 g.goods_series_2column_caption,
  688.                 g.goods_series_3column_caption,
  689.                 g.goods_series_4column_caption,
  690.                 g.goods_show_marks_above_model_banner_1,
  691.                 g.goods_show_marks_above_model_banner_2,
  692.                 g.goods_show_marks_above_model_banner_3,
  693.                 g.goods_show_direct_mark_below_model_banner_1,
  694.                 g.goods_show_direct_mark_below_model_banner_2,
  695.                 g.goods_show_direct_mark_below_model_banner_3,
  696.                 s.shiiresaki_id,
  697.                 s.shiiresaki_url,
  698.                 o.*,
  699.                 g.goods_id
  700.             FROM
  701.                 goods_table g
  702.             JOIN
  703.                 shiiresaki_table s
  704.             ON
  705.                 g.goods_shiiresaki = s.shiiresaki_id
  706.             LEFT JOIN
  707.                 goods_contents_block_option_table o
  708.             USING(goods_id)
  709.             WHERE
  710.                 g.goods_id IN({$in_clause}) AND
  711.                 g.goods_ddate IS NULL AND
  712.                 g.goods_status = 1 AND
  713.                 s.shiiresaki_url IS NOT NULL
  714.         ")->FetchAll();
  715.         foreach($rows as $row){
  716.             $row['uri'] = '/item/'.($row['goods_canonical_hinban'] ? $row['goods_canonical_hinban'] : $row['goods_id']).'.html';
  717.             if( isset($color_map[$row['goods_id']]) ) $row['color'] = $color_map[$row['goods_id']];
  718.             $map[$row['goods_id']] = $row;
  719.         }
  720.         return $map;
  721.     }
  722.     private function getContentsBlockList($id_list){
  723.         $in_clause implode(','array_map( function($id){
  724.             return (int)$id;
  725.         }, $id_list));
  726.         return (new SqlService())->Sql("
  727.             SELECT
  728.                 cb.*,
  729.                 GROUP_CONCAT(cbi.goods_id ORDER BY order_num) AS goods_id_list,
  730.                 GROUP_CONCAT(cbi.display_name ORDER BY order_num) AS display_name_list,
  731.                 GROUP_CONCAT(cbi.display_name_color ORDER BY order_num) AS display_name_color_list,
  732.                 GROUP_CONCAT(cbi.display_name_bg_color ORDER BY order_num) AS display_name_bg_color_list,
  733.                 GROUP_CONCAT(cbi.model_num ORDER BY order_num) AS model_num_list,
  734.                 GROUP_CONCAT(cbi.model_portrait_num ORDER BY order_num) AS model_portrait_num_list,
  735.                 GROUP_CONCAT(cbi.model_portrait_pair_num ORDER BY order_num) AS model_portrait_pair_num_list,
  736.                 GROUP_CONCAT(cbi.model_portrait_is_2col_image ORDER BY order_num) AS model_portrait_is_2col_image_list
  737.             FROM
  738.                 contents_block_table cb
  739.             LEFT JOIN
  740.                 contents_block_item_map_table cbi
  741.             USING(contents_block_id)
  742.             WHERE
  743.                 cb.contents_block_id IN({$in_clause}) AND
  744.                 cb.is_active = :is_active
  745.             GROUP BY
  746.                 cb.contents_block_id
  747.         ")->Params([
  748.             'is_active' => self::BOOL_TRUE,
  749.         ])->FetchAll();
  750.     }
  751.     private function getTemplate(){
  752.         $rows = (new SqlService())->Sql("
  753.             SELECT
  754.                 *
  755.             FROM
  756.                 contents_block_template_table
  757.             WHERE
  758.                 name LIKE 'category-col%'
  759.         ")->FetchAll();
  760.         $map = array();
  761.         foreach($rows as $row){
  762.             $map[$row['name']] = $row['html'];
  763.         }
  764.         
  765.         return $map;
  766.     }
  767.     private function getGoodsColorMap($goods_id_list){
  768.         if( !is_array($goods_id_list) || empty($goods_id_list) ) return [];
  769.         $where_clause 'AND g.goods_id IN(' implode(','$goods_id_list) . ')';
  770.         
  771.         $rows = (new SqlService())->Sql("
  772.             SELECT DISTINCT
  773.                 g.goods_id,
  774.                 c.color_name,
  775.                 c.color_rgb
  776.             FROM
  777.                 goods_table g
  778.             JOIN
  779.                 goods_color_table gc
  780.             ON
  781.                 g.goods_id = gc.gcl_goods AND
  782.                 gc.gcl_display_status = 1
  783.             LEFT JOIN
  784.                 color_table c
  785.             ON
  786.                 c.color_id = gc.gcl_color_id
  787.             WHERE
  788.                 g.goods_ddate IS NULL AND
  789.                 g.goods_status = 1
  790.                 {$where_clause}
  791.             ORDER BY
  792.                 COALESCE(gc.gcl_display, gc.gcl_display, 999),
  793.                 COALESCE(c.color_display, c.color_display, 999)
  794.         ")->FetchAll();
  795.         $map = array();
  796.         foreach($rows as $row){
  797.             $map[$row['goods_id']][] = $row['color_rgb'];
  798.         }
  799.         return $map;
  800.     }
  801.     private function fillItemList$item_part_list$column ){
  802.         $goods_count count($item_part_list);
  803.         $remainder 0;
  804.         
  805.         if($goods_count > (int)$column){
  806.             if( $goods_count $column !== ){
  807.                 $remainder $column - ($goods_count $column);
  808.             }
  809.         }
  810.         else{
  811.             $remainder $column $goods_count;
  812.         }
  813.         if( $remainder !== ){
  814.             for($i 1$i <= $remainder$i++){
  815.                 $item_part_list[] = '<li style="visibility: hidden; order: 99;" />';
  816.             }
  817.         }
  818.         return $item_part_list;
  819.     }
  820.     public function getGoodsBannerOptionMap(){
  821.         // 菅画面で「フォントをカスタマイズ」してない場合のバナーデータ
  822.         $rows = (new SqlService())->Sql('
  823.             SELECT
  824.                 *
  825.             FROM
  826.                 goods_banner_contents_block_option_mst_table
  827.         ')->FetchAll();
  828.         // バナーのマスターデータ(サイズ、色、位置)
  829.         $mst_map = [];
  830.         foreach($rows as $row){
  831.             $row['banner_no'] = (int)$row['banner_no'];
  832.             $row['text_1_size'] = (int)$row['text_1_size'];
  833.             $row['text_2_size'] = (int)$row['text_2_size'];
  834.             $row['description_size'] = (int)$row['description_size'];
  835.             $row['pos_x'] = (int)$row['pos_x'];
  836.             $row['pos_y'] = (int)$row['pos_y'];
  837.             
  838.             $mst_map[$row['banner_no']] = $row;
  839.         }
  840.         // 商品ごとのテキストデータ(商品詳細で設定されるもの)
  841.         $rows = (new SqlService())->Sql('
  842.             SELECT
  843.                 *
  844.             FROM
  845.                 goods_banner_contents_block_option_table
  846.         ')->FetchAll();
  847.         $option_map = [];
  848.         foreach($rows as $row){
  849.             if( !isset($option_map[(int)$row['goods_id']][(int)$row['banner_no']]) ) $option_map[(int)$row['goods_id']][(int)$row['banner_no']] = [];
  850.             $data = &$option_map[(int)$row['goods_id']][(int)$row['banner_no']];
  851.             $data[$row['option_key']] = $row['option_value'];
  852.         }
  853.         unset($data);
  854.         $rows = (new SqlService())->Sql('
  855.             SELECT
  856.                 *
  857.             FROM
  858.                 contents_block_goods_banner_map_table
  859.         ')->FetchAll();
  860.         if( empty($rows) ) return [];
  861.         /**
  862.          * return するマップ生成
  863.          */
  864.         $map = [];
  865.         $goods_id_list = [];
  866.         foreach($rows as $row){
  867.             if (!empty($option_map[(int)$row['goods_id']])) {
  868.                 $map[(int)$row['contents_block_id']] = [];
  869.                 $data = &$map[(int)$row['contents_block_id']];
  870.                 $data['goods_id'] = (int)$row['goods_id'];
  871.                 $data['banner_no'] = (int)$row['banner_no'];
  872.                 $data['show_price'] = (int)$row['show_price'] === self::BOOL_TRUE;
  873.                 $data['show_sale_icon'] = (int)$row['show_sale_icon'] === self::BOOL_TRUE;
  874.                 $data['use_custom_text'] = (int)$row['use_custom_text'] === self::BOOL_TRUE;
  875.                 $data['use_custom_font'] = (int)$row['use_custom_font'] === self::BOOL_TRUE;
  876.                 $data['use_custom_more_text'] = (int)$row['use_custom_more_text'] === self::BOOL_TRUE;
  877.                 $data['use_custom_more_link_url'] = (int)$row['use_custom_more_link_url'] === self::BOOL_TRUE;
  878.                 $goods_id_list[] = (int)$row['goods_id'];
  879.             }
  880.         }
  881.         unset($data);
  882.         
  883.         // 商品情報取得
  884.         $rows = (new SqlService())->Sql('
  885.             SELECT
  886.                 goods_id,
  887.                 goods_name,
  888.                 goods_sub_name,
  889.                 goods_canonical_hinban,
  890.                 goods_main_kataban,
  891.                 shiiresaki_url
  892.             FROM
  893.                 goods_table
  894.             JOIN
  895.                 shiiresaki_table
  896.             ON
  897.                 goods_shiiresaki = shiiresaki_id
  898.             WHERE
  899.                 shiiresaki_url IS NOT NULL AND
  900.                 goods_id IN('.implode(','$goods_id_list).')
  901.         ')->FetchAll();
  902.         $goods_map = [];
  903.         foreach($rows as $row){
  904.             $goods_map[(int)$row['goods_id']] = [
  905.                 'name' => "{$row['goods_name']} {$row['goods_sub_name']}",
  906.                 'kataban' => $row['goods_main_kataban'],
  907.                 'shiiresaki_url' => $row['shiiresaki_url'],
  908.                 'url' => '/item/'.( !empty($row['goods_canonical_hinban']) ? $row['goods_canonical_hinban'] : (!empty($row['goods_main_kataban']) ? $row['goods_main_kataban'] : $row['goods_id']) ).'.html'
  909.             ];
  910.         }
  911.         // ここからデータを生成する。
  912.         foreach($map as $block_id => &$data){
  913.             if( !isset($goods_map[$data['goods_id']]) ){
  914.                 unset($map[$block_id]);
  915.                 continue;
  916.             }
  917.             
  918.             $goods_data $goods_map[$data['goods_id']];
  919.             // バナー画像
  920.             $data['image_src'] = sprintf(
  921.                 self::GOODS_BANNER_IMAGE_PATH,
  922.                 $goods_data['shiiresaki_url'],
  923.                 $goods_data['kataban'],
  924.                 $data['banner_no']
  925.             );
  926.             $data['image_alt'] = $goods_data['name'];
  927.             // テキストデータ
  928.             if (isset($option_map[$data['goods_id']][$data['banner_no']])) {
  929.                 $option_data $option_map[$data['goods_id']][$data['banner_no']];
  930.             } else {
  931.                 // ログ出力を console.log に変更
  932.                 echo '<script>console.log(' json_encode(
  933.                         '[CategoryContentsBlockMapModel.php] option_map missing for goods_id=' .
  934.                         (isset($data['goods_id']) ? $data['goods_id'] : 'undefined') .
  935.                         ', banner_no=' .
  936.                         (isset($data['banner_no']) ? $data['banner_no'] : 'undefined')
  937.                     ) . ');</script>';
  938.                 if (isset($option_map[$data['goods_id']])) {
  939.                     echo '<script>console.log(' json_encode(
  940.                             '[CategoryContentsBlockMapModel.php] available banner_nos for goods_id=' .
  941.                             $data['goods_id'] . ': ' implode(', 'array_keys($option_map[$data['goods_id']]))
  942.                         ) . ');</script>';
  943.                 } else {
  944.                     echo '<script>console.log(' json_encode(
  945.                             '[CategoryContentsBlockMapModel.php] goods_id=' $data['goods_id'] . ' not found in option_map'
  946.                         ) . ');</script>';
  947.                 }
  948.                 $option_data = []; // デフォルト値
  949.             }
  950.             // フォント、位置などのデータ
  951.             $mst_data $mst_map[$data['banner_no']];
  952.             // テキストをカスタマイズする(カスタマイズ時のデータはcontents_block_tableに入っているのでnullにする)
  953.             if( $data['use_custom_text'] ){
  954.                 $data['text_1']      = null;
  955.                 $data['text_2']      = null;
  956.                 $data['description'] = null;
  957.                 $data['pos_x']       = null;
  958.                 $data['pos_y']       = null;
  959.             }
  960.             else{
  961.                 // text_1
  962.                 if (isset($option_data['text_1'])) {
  963.                     $data['text_1'] = $option_data['text_1'];
  964.                 } else {
  965.                     echo '<script>console.log(' json_encode(
  966.                             '[CategoryContentsBlockMapModel.php] text_1 missing in option_data. Current keys: ' implode(', 'array_keys($option_data))
  967.                         ) . ');</script>';
  968.                     $data['text_1'] = '';
  969.                 }
  970. // text_2
  971.                 if (isset($option_data['text_2'])) {
  972.                     $data['text_2'] = $option_data['text_2'];
  973.                 } else {
  974.                     echo '<script>console.log(' json_encode(
  975.                             '[CategoryContentsBlockMapModel.php] text_2 missing in option_data. Current keys: ' implode(', 'array_keys($option_data))
  976.                         ) . ');</script>';
  977.                     $data['text_2'] = '';
  978.                 }
  979. // description
  980.                 if (isset($option_data['description'])) {
  981.                     $data['description'] = $option_data['description'];
  982.                 } else {
  983.                     echo '<script>console.log(' json_encode(
  984.                             '[CategoryContentsBlockMapModel.php] description missing in option_data. Current keys: ' implode(', 'array_keys($option_data))
  985.                         ) . ');</script>';
  986.                     $data['description'] = '';
  987.                 }
  988. // pos_x
  989.                 if (isset($mst_data['pos_x'])) {
  990.                     $data['pos_x'] = $mst_data['pos_x'];
  991.                 } else {
  992.                     echo '<script>console.log(' json_encode(
  993.                             '[CategoryContentsBlockMapModel.php] pos_x missing in mst_data. Current keys: ' implode(', 'array_keys($mst_data))
  994.                         ) . ');</script>';
  995.                     $data['pos_x'] = 0// 必要に応じてデフォルト座標
  996.                 }
  997. // pos_y
  998.                 if (isset($mst_data['pos_y'])) {
  999.                     $data['pos_y'] = $mst_data['pos_y'];
  1000.                 } else {
  1001.                     echo '<script>console.log(' json_encode(
  1002.                             '[CategoryContentsBlockMapModel.php] pos_y missing in mst_data. Current keys: ' implode(', 'array_keys($mst_data))
  1003.                         ) . ');</script>';
  1004.                     $data['pos_y'] = 0;
  1005.                 }
  1006.             }
  1007.             // フォントをカスタマイズする(カスタマイズ時のデータはcontents_block_tableに入っているのでnullにする)
  1008.             if( $data['use_custom_font'] ){
  1009.                 $data['text_1_size']          = null;
  1010.                 $data['text_1_color']         = null;
  1011.                 $data['text_1_bg_color']      = null;
  1012.                 $data['text_2_size']          = null;
  1013.                 $data['text_2_color']         = null;
  1014.                 $data['text_2_bg_color']      = null;
  1015.                 $data['description_size']     = null;
  1016.                 $data['description_color']    = null;
  1017.                 $data['description_bg_color'] = null;
  1018.             }
  1019.             else{
  1020.                 $data['text_1_size']          = $mst_data['text_1_size'];
  1021.                 $data['text_1_color']         = $mst_data['text_1_color'];
  1022.                 $data['text_1_bg_color']      = $mst_data['text_1_bg_color'];
  1023.                 $data['text_2_size']          = $mst_data['text_2_size'];
  1024.                 $data['text_2_color']         = $mst_data['text_2_color'];
  1025.                 $data['text_2_bg_color']      = $mst_data['text_2_bg_color'];
  1026.                 $data['description_size']     = $mst_data['description_size'];
  1027.                 $data['description_color']    = $mst_data['description_color'];
  1028.                 $data['description_bg_color'] = $mst_data['description_bg_color'];
  1029.             }
  1030.             // もっと見るをカスタマイズする(カスタマイズ時のデータはcontents_block_tableに入っているのでnullにする)
  1031.             if (!isset($data['use_custom_more_text']) || !$data['use_custom_more_text']) {
  1032.                 if (isset($option_data['oneliner_text'])) {
  1033.                     $data['oneliner_text'] = $option_data['oneliner_text'];
  1034.                 } else {
  1035.                     echo '<script>console.log(' json_encode(
  1036.                             '[CategoryContentsBlockMapModel.php] oneliner_text missing in option_data. Current keys: ' .
  1037.                             implode(', 'array_keys($option_data))
  1038.                         ) . ');</script>';
  1039.                     $data['oneliner_text'] = ''// または null
  1040.                 }
  1041.             } else {
  1042.                 $data['oneliner_text'] = null;
  1043.             }
  1044.             $data['url'] = $data['use_custom_more_link_url'] ? null $goods_data['url'];
  1045.         }
  1046.         
  1047.         return $map;
  1048.     }
  1049.     /**
  1050.      * loadHTMLにすると許可されない要素配置が直されてしまう。
  1051.      * <h2><span>text1</span><p>text2</p></h2> -> <h2><span>text1</span></h2><p>text2</p>
  1052.      * 
  1053.      * そこでloadXMLを使いたいがそうすると<img>のとじタグがない、というエラーになるためimgを閉じる。
  1054.      * recover = trueでロードすると閉じタグがないというエラーは出なくなるが、後の</div>などの閉じタグを巻き込んで修正してしまうので注意。
  1055.      */
  1056.     private static function getDomDocumentFromBlockPart($block_part){
  1057.         $dom = new \DOMDocument('1.0''utf8');
  1058.         
  1059.         // 閉じタグ対応
  1060.         $block_part preg_replace('!(<img .+?)>!''$1/>'$block_part);
  1061.         
  1062.         $result = @$dom->loadXML$block_part );
  1063.         
  1064.         return $result === false false $dom;
  1065.     }
  1066.     private static function getBlockPartFromDomDocument(\DOMDocument $dom){
  1067.         
  1068.         @$block_part $dom->saveXML();
  1069.         $block_part preg_replace('!<\?xml .+?\?>!'''$block_part);
  1070.         
  1071.         return mb_convert_encoding($block_part'utf-8''HTML-ENTITIES');
  1072.     }
  1073.     private static function convertHex2Rgba($hex){
  1074.         if( !preg_match('/^#([0-9a-f]{3}|[0-9a-f]{6})$/i'$hex) ) return $hex;
  1075.         
  1076.         if( strlen$hex ) == $hex substr$hex0) . substr$hex0) . substr$hex1) . substr$hex1) . substr$hex2) . substr$hex2);
  1077.         $rgb array_map(
  1078.             'hexdec',
  1079.             array(
  1080.                 substr$hex0),
  1081.                 substr$hex2),
  1082.                 substr$hex4)
  1083.             )
  1084.         );
  1085.         return 'rgba('.implode(','$rgb).', .5);';
  1086.     }
  1087.     private function setMensLadies($contents_block, &$item_part$goods_data$item_idx){
  1088.         $model_portrait_num_list explode(','$contents_block['model_portrait_num_list']);
  1089.         $model_portrait_pair_num_list explode(','$contents_block['model_portrait_pair_num_list']);
  1090.         $model_portrait_is_2col_image_list explode(','$contents_block['model_portrait_is_2col_image_list']);
  1091.         
  1092.         $model_portrait_num = (int)$model_portrait_num_list[$item_idx];
  1093.         $model_portrait_pair_num = (int)$model_portrait_pair_num_list[$item_idx];
  1094.         $model_portrait_is_2col_image = (int)$model_portrait_is_2col_image_list[$item_idx] === self::BOOL_TRUE;
  1095.         $model_num_list explode(','$contents_block['model_num_list']);
  1096.         $model_num = (int)$model_num_list[$item_idx];
  1097.         // モデル縦長ペア画像は1つだけしか登録されない
  1098.         if( (int)$model_portrait_pair_num === ){
  1099.             $main_src sprintf(
  1100.                 self::MODEL_PORTRAIT_PAIR_IMAGE_PATH,
  1101.                 $goods_data['shiiresaki_url'],
  1102.                 $goods_data['goods_main_kataban']
  1103.             );
  1104.     
  1105.             $item_part str_replace(
  1106.                 '<image-main class="managed-contents-block-item-figure-main" />',
  1107.                 '<img src="'.htmlspecialchars($main_src).'" class="managed-contents-block-item-figure-main" alt="'.htmlspecialchars($goods_data['goods_name']).'" />',
  1108.                 $item_part
  1109.             );
  1110.         }
  1111.         $list_line_idx $item_idx === floor($item_idx 2);    // 商品2個で一列
  1112.         $start_order_num $list_line_idx === $item_idx : ($list_line_idx 2) + $item_idx;
  1113.     
  1114.         $item_part str_replace('<li class="">''<li class="managed-contents-block-item-list-price-li" style="order: '.($start_order_num+2).';">'$item_part);
  1115.         
  1116.         // 2カラム画像を使用している場合、もう片方のmodel_portrait_numは0が入っている
  1117.         $src $model_portrait_num === 0
  1118.             null
  1119.             sprintf(
  1120.                 self::MODEL_PORTRAIT_IMAGE_PATH,
  1121.                 $goods_data['shiiresaki_url'],
  1122.                 $goods_data['goods_main_kataban'],
  1123.                 $model_portrait_num
  1124.             )
  1125.         ;
  1126.         $li_2col_class '';
  1127.         // 「2カラム画像として表示」がチェックされている
  1128.         if( $model_portrait_is_2col_image ){
  1129.             $li_2col_class 'managed-contents-block-item-list-full-height-2col-li';
  1130.             if( $item_idx === ){
  1131.                 $li_2col_class .= ' managed-contents-block-item-list-hide-after-li';
  1132.             }
  1133.             else{
  1134.                 $li_2col_class .= ' managed-contents-block-item-list-hide-before-li';
  1135.             }
  1136.         }
  1137.         if( is_null($src) ){
  1138.             $pair_li sprintf('
  1139.                     <li class="managed-contents-block-item-list-full-height-li %s" style="order: %d;"></li>
  1140.                 ',
  1141.                 $li_2col_class,
  1142.                 $start_order_num
  1143.             );
  1144.         }
  1145.         else{
  1146.             $pair_li sprintf('
  1147.                     <li class="managed-contents-block-item-list-full-height-li %s" style="order: %d;">
  1148.                         <a href="%s"><img src="%s" alt="%s"></a>
  1149.                     </li>
  1150.                 ',
  1151.                 $li_2col_class,
  1152.                 $start_order_num,
  1153.                 htmlspecialchars($goods_data['uri']),
  1154.                 htmlspecialchars($src),
  1155.                 htmlspecialchars($goods_data['goods_name'])
  1156.             );
  1157.         }
  1158.         $item_part $pair_li.$item_part;
  1159.     }
  1160.     private function setAdditionalText($block_part$contents_block){
  1161.         $additional_midashi $contents_block['additional_midashi'];
  1162.         $additional_description $contents_block['additional_description'];
  1163.         $additional_midashi_color $contents_block['additional_midashi_color'];
  1164.         
  1165.         $additional_description_color $contents_block['additional_description_color'];
  1166.         $midashi_style '';
  1167.         $midashi_style .= !empty($additional_midashi_color) ? "color: {$additional_midashi_color}; " '';
  1168.         $description_style '';
  1169.         $description_style .= !empty($additional_description_color) ? "color: {$additional_description_color}; " '';
  1170.         $html sprintf('
  1171.                 <div id="managed-contents-block-additional-text">
  1172.                     <h3 id="managed-contents-block-additional-midashi" style="%s">%s</h3>
  1173.                     <span id="managed-contents-block-additional-description" style="%s">%s</span>
  1174.                 </div>
  1175.             ',
  1176.             $midashi_style,
  1177.             htmlspecialchars($additional_midashi),
  1178.             $description_style,
  1179.             nl2brhtmlspecialchars($additional_description) )
  1180.         );
  1181.         return str_replace('<div class="managed-contents-block-body">''<div class="managed-contents-block-body">'.$html$block_part);
  1182.     }
  1183.     private function set2colLi(&$item_part_list){
  1184.         foreach($item_part_list as $idx => $item_part){
  1185.             unset($hide_li);
  1186.             if( preg_match('/managed-contents-block-item-list-hide-before-li/'$item_part) ){
  1187.                 if( isset($item_part_list[$idx 1]) ) $hide_li = &$item_part_list[$idx 1];
  1188.             }
  1189.             else if( preg_match('/managed-contents-block-item-list-hide-after-li/'$item_part) ){
  1190.                 if( isset($item_part_list[$idx 1]) ) $hide_li = &$item_part_list[$idx 1];
  1191.             }
  1192.             
  1193.             // style="order: xx"を指定しているので必ずstyle=の定義がある
  1194.             if( isset($hide_li) ) $hide_li preg_replace(
  1195.                 '/^(.+?managed-contents-block-item-list-full-height-li.+?)style=".+?"(.+)$/s',
  1196.                 '$1 style="display: none;" $2',
  1197.                 $hide_li
  1198.             );
  1199.             
  1200.         }
  1201.     }
  1202.     private static function setColorTip(&$item_part$goods_data){
  1203.         // カラーチップ
  1204.         if( empty($goods_data['color']) ){
  1205.             $item_part preg_replace(
  1206.                 '!<div class="managed-contents-block-item-description-color-tip">[\r\n\t]+<div class="managed-contents-block-item-description-color-tip-count"><span></span>COLOR</div>[\r\n\t]+<ul><goods_color_tip_list /></ul>[\r\n\t]+</div>!',
  1207.                 '',
  1208.                 $item_part
  1209.             );
  1210.         }
  1211.         else{
  1212.             $item_part str_replace(
  1213.                 '<div class="managed-contents-block-item-description-color-tip-count"><span></span>COLOR</div>',
  1214.                 '<div class="managed-contents-block-item-description-color-tip-count"><span>'.count($goods_data['color']).'</span>COLOR</div>',
  1215.                 $item_part
  1216.             );
  1217.             $item_part str_replace(
  1218.                 '<goods_color_tip_list />',
  1219.                 implode(''array_map( function($rgb){
  1220.                     return '<li style="background: #'.htmlspecialchars($rgb).'"></li>';
  1221.                 }, $goods_data['color'])),
  1222.                 $item_part
  1223.             );
  1224.         }
  1225.     }
  1226.     private static function setHinban($contents_block, &$item_part$goods_data$item_idx){
  1227.         $display_name_list explode(','$contents_block['display_name_list']);
  1228.         $display_name_color_list explode(','$contents_block['display_name_color_list']);
  1229.         $display_name_bg_color_list explode(','$contents_block['display_name_bg_color_list']);
  1230.         $display_name $display_name_list[$item_idx];
  1231.         $display_name_color = isset($display_name_color_list[$item_idx]) ? $display_name_color_list[$item_idx] : null;
  1232.         $display_name_bg_color = isset($display_name_bg_color_list[$item_idx]) ? $display_name_bg_color_list[$item_idx] : null;
  1233.         // 型番
  1234.         $item_part str_replace(
  1235.             '<goods-kataban />',
  1236.             nl2brhtmlspecialchars( !empty($display_name) ? $display_name $goods_data['goods_main_kataban'] ) ),
  1237.             $item_part
  1238.         );
  1239.         $kataban_style '';
  1240.         if( !empty($display_name_color) ) $kataban_style .= "color: {$display_name_color}; ";
  1241.         if( !empty($display_name_bg_color) )  $kataban_style .= "background-color: {$display_name_bg_color}; ";
  1242.         if( $kataban_style !== '' ){
  1243.             $item_part str_replace(
  1244.                 'managed-contents-block-item-description-kataban"',
  1245.                 'managed-contents-block-item-description-kataban" style="'.$kataban_style.'"',
  1246.                 $item_part
  1247.             );
  1248.         }
  1249.     }
  1250.     private static function setPrice(&$item_part$goods_data){
  1251.         // 価格(コーデの価格もあるのでlimit 1にする)
  1252.         $item_part preg_replace('/data-item-id=""/''data-item-id="'.$goods_data['goods_id'].'"'$item_part1);
  1253.     }
  1254.     private static function setLink(&$item_part$goods_data){
  1255.         $item_part str_replace(
  1256.             '<a href="">',
  1257.             '<a href="'.htmlspecialchars($goods_data['uri']).'">',
  1258.             $item_part
  1259.         );
  1260.     }
  1261.     private static function setMainImage($contents_block, &$item_part$goods_data$item_idx){
  1262.         $model_num_list explode(','$contents_block['model_num_list']);
  1263.         $model_num = (int)$model_num_list[$item_idx];
  1264.         if( (int)$model_num === ){
  1265.             $main_src sprintf(
  1266.                 self::KIHON_IMAGE_PATH,
  1267.                 $goods_data['shiiresaki_url'],
  1268.                 $goods_data['goods_main_kataban']
  1269.             );
  1270.         }
  1271.         // モデル画像
  1272.         else{
  1273.             $column_num $contents_block['column_number'];
  1274.             $main_src sprintf(
  1275.                 self::MODEL_IMAGE_PATH,
  1276.                 $goods_data['shiiresaki_url'],
  1277.                 $goods_data['goods_main_kataban'],
  1278.                 $model_num,
  1279.                 $column_num
  1280.             );
  1281.         }
  1282.         $item_part str_replace(
  1283.             '<image-main class="managed-contents-block-item-figure-main" />',
  1284.             '<img src="'.htmlspecialchars($main_src).'" class="managed-contents-block-item-figure-main" alt="'.htmlspecialchars($goods_data['goods_name']).'" />',
  1285.             $item_part
  1286.         );
  1287.         // 画像タイプ
  1288.         if( (int)$contents_block['item_image_type'] === self::ITEM_IMAGE_TYPE_GOODS $item_part str_replace('"managed-contents-block-item-figure"''"managed-contents-block-item-figure managed-contents-block-item-figure-goods"'$item_part);
  1289.         if( (int)$contents_block['item_image_type'] === self::ITEM_IMAGE_TYPE_MODEL $item_part str_replace('"managed-contents-block-item-figure"''"managed-contents-block-item-figure managed-contents-block-item-figure-model"'$item_part);
  1290.     }
  1291.     private static function setDirectDeliveryImage($contents_block, &$item_part$goods_data$item_idx){
  1292.         $model_num_list explode(','$contents_block['model_num_list']);
  1293.         $model_num = (int)$model_num_list[$item_idx];
  1294.         if(
  1295.             $model_num &&
  1296.             === (int)($goods_data["goods_show_marks_above_model_banner_{$model_num}"]) &&
  1297.             === (int)$goods_data["goods_show_direct_mark_below_model_banner_{$model_num}"]
  1298.         ){
  1299.             $column_num $contents_block['column_number'];
  1300.             $chokusou_src '/images/itempage/chokusou/banner'.$goods_data['shiiresaki_id'].'_'.$column_num.'col.jpg';
  1301.             $item_part str_replace(
  1302.                 '<image-sokujitsu class="managed-contents-block-item-figure-sokujitsu" />',
  1303.                 '<img src="'.htmlspecialchars($chokusou_src).'" class="managed-contents-block-item-figure-sokujitsu" alt="この商品は即日配送できます" />',
  1304.                 $item_part
  1305.             );
  1306.         }
  1307.         else{
  1308.             $item_part str_replace(
  1309.                 '<image-sokujitsu class="managed-contents-block-item-figure-sokujitsu" />',
  1310.                 '',
  1311.                 $item_part
  1312.             );
  1313.         }
  1314.     }
  1315.     
  1316.     private static function setDescriptionText($contents_block, &$item_part$goods_data){
  1317.         $model_num_list explode(','$contents_block['model_num_list']);
  1318.         $column_num $contents_block['column_number'];
  1319.         $item_part str_replace(
  1320.             '<goods-description-text />',
  1321.             is_null($goods_data["goods_series_{$column_num}column_caption"]) ? '' htmlspecialchars$goods_data["goods_series_{$column_num}column_caption"] ),
  1322.             $item_part
  1323.         );
  1324.     }
  1325. }