HandlesEsgParagraphs.php 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. <?php
  2. // app/Traits/HandlesParagraphs.php
  3. namespace App\Traits;
  4. use Illuminate\Database\Eloquent\Model;
  5. use Illuminate\Support\Str;
  6. trait HandlesEsgParagraphs
  7. {
  8. /**
  9. * 格式化 paragraphs 資料以符合表單結構(載入時使用)
  10. */
  11. protected function formatParagraphsForForm(array $paragraphs): array
  12. {
  13. $formattedParagraphs = [];
  14. foreach ($paragraphs as $paragraph) {
  15. $formatted = [
  16. 'id' => $paragraph['id'],
  17. 'type' => $paragraph['type'],
  18. 'order' => $paragraph['order'],
  19. 'content' => $paragraph['content'] ?? [],
  20. ];
  21. // 根據類型進行特殊格式化
  22. switch ($paragraph['type']) {
  23. case 1: // 純文字
  24. $formatted = $this->formatTextParagraphForForm($formatted);
  25. break;
  26. case 2: // 區塊文字
  27. $formatted = $this->formatBlockParagraphForForm($formatted);
  28. break;
  29. case 3: // 表格
  30. $formatted = $this->formatTableParagraphForForm($formatted);
  31. break;
  32. case 4: // 圖片
  33. $formatted = $this->formatImageParagraphForForm($formatted);
  34. break;
  35. }
  36. // 確保有 item_key
  37. if (!isset($formatted['item_key'])) {
  38. $formatted['item_key'] = Str::random();
  39. }
  40. $formattedParagraphs[] = $formatted;
  41. }
  42. return $formattedParagraphs;
  43. }
  44. /**
  45. * 格式化純文字段落(載入時)
  46. */
  47. protected function formatTextParagraphForForm(array $paragraph): array
  48. {
  49. // 確保 text_content 結構正確
  50. if (!isset($paragraph['content']['text_content'])) {
  51. $paragraph['content']['text_content'] = ['zh_TW' => '', 'en' => ''];
  52. }
  53. return $paragraph;
  54. }
  55. /**
  56. * 格式化區塊文字段落(載入時)
  57. */
  58. protected function formatBlockParagraphForForm(array $paragraph): array
  59. {
  60. // 確保 text_blocks 結構正確
  61. if (!isset($paragraph['content']['text_blocks'])) {
  62. $paragraph['content']['text_blocks'] = [];
  63. }
  64. // 為每個 text_block 添加必要的欄位
  65. foreach ($paragraph['content']['text_blocks'] as $index => &$block) {
  66. if (!isset($block['block_item_key'])) {
  67. $block['block_item_key'] = Str::random();
  68. }
  69. if (!isset($block['order'])) {
  70. $block['order'] = $index + 1;
  71. }
  72. }
  73. return $paragraph;
  74. }
  75. /**
  76. * 格式化表格段落(載入時)
  77. */
  78. protected function formatTableParagraphForForm(array $paragraph): array
  79. {
  80. $content = $paragraph['content'];
  81. if (!isset($content['column_count'])) {
  82. $content['column_count'] = 2;
  83. }
  84. if (!isset($content['table_title'])) {
  85. $content['table_title'] = ['zh_TW' => '', 'en' => ''];
  86. }
  87. if (!isset($content['table_description'])) {
  88. $content['table_description'] = ['zh_TW' => '', 'en' => ''];
  89. }
  90. if (!isset($content['simple_table_rows'])) {
  91. $content['simple_table_rows'] = [];
  92. }
  93. // 確保表頭資料結構
  94. $columnCount = $content['column_count'];
  95. for ($i = 1; $i <= $columnCount; $i++) {
  96. if (!isset($content["head{$i}"])) {
  97. $content["head{$i}"] = ['zh_TW' => '', 'en' => ''];
  98. }
  99. }
  100. $paragraph['content'] = $content;
  101. return $paragraph;
  102. }
  103. /**
  104. * 格式化圖片段落(載入時)
  105. */
  106. protected function formatImageParagraphForForm(array $paragraph): array
  107. {
  108. // 確保 multiple_images 結構正確
  109. if (!isset($paragraph['content']['multiple_images'])) {
  110. $paragraph['content']['multiple_images'] = [];
  111. }
  112. // 為每個圖片添加必要的欄位
  113. foreach ($paragraph['content']['multiple_images'] as $index => &$image) {
  114. if (!isset($image['para_img_item_key'])) {
  115. $image['para_img_item_key'] = Str::random();
  116. }
  117. if (!isset($image['order'])) {
  118. $image['order'] = $index + 1;
  119. }
  120. }
  121. return $paragraph;
  122. }
  123. /**
  124. * 預處理 paragraphs 資料(儲存前使用)
  125. */
  126. protected function preprocessParagraphs(array $paragraphs): array
  127. {
  128. foreach ($paragraphs as $index => &$paragraph) {
  129. \Log::info("處理段落 {$index}:", $paragraph);
  130. // 確保基本欄位存在
  131. $paragraph['content'] = $paragraph['content'] ?? [];
  132. $paragraph['order'] = $paragraph['order'] ?? ($index + 1);
  133. // 根據類型進行特殊處理
  134. switch ($paragraph['type']) {
  135. case 1: // 純文字
  136. $paragraph = $this->preprocessTextParagraph($paragraph);
  137. break;
  138. case 2: // 區塊文字
  139. $paragraph = $this->preprocessBlockParagraph($paragraph);
  140. break;
  141. case 3: // 表格
  142. $paragraph = $this->preprocessTableParagraph($paragraph);
  143. break;
  144. case 4: // 圖片
  145. $paragraph = $this->preprocessImageParagraph($paragraph);
  146. break;
  147. }
  148. // 添加處理時間戳
  149. $paragraph['content']['processed_at'] = now()->toISOString();
  150. \Log::info("段落 {$index} 處理完成:", $paragraph);
  151. }
  152. return $paragraphs;
  153. }
  154. /**
  155. * 處理純文字段落(儲存前)
  156. */
  157. protected function preprocessTextParagraph(array $paragraph): array
  158. {
  159. $paragraph["content"]["text_content"] = ["zh_TW" => $paragraph["content"]["text_content_tw"] ?? "" , "en" => $paragraph["content"]["text_content_en"] ?? ""];
  160. // 移除臨時的分離欄位,只保留合併的 JSON 欄位
  161. unset($paragraph["content"]["text_content_tw"]);
  162. unset($paragraph["content"]["text_content_en"]);
  163. // 清理 HTML 標籤(如果需要)
  164. foreach ($paragraph['content']['text_content'] as $locale => $content) {
  165. $paragraph['content']['text_content'][$locale] = $this->cleanHtml($content);
  166. }
  167. return $paragraph;
  168. }
  169. /**
  170. * 處理區塊文字段落(儲存前)
  171. */
  172. protected function preprocessBlockParagraph(array $paragraph): array
  173. {
  174. // 確保有 text_blocks
  175. if (!isset($paragraph['content']['text_blocks'])) {
  176. $paragraph['content']['text_blocks'] = [];
  177. }
  178. // 處理每個文字區塊
  179. foreach ($paragraph['content']['text_blocks'] as $index => &$block) {
  180. // 確保區塊有必要的欄位
  181. $block['block_item_key'] = $block['block_item_key'] ?? Str::random();
  182. $block['order'] = $block['order'] ?? ($index + 1);
  183. $block["block_content"] = ["zh_TW" => $block["block_content_tw"] ?? "" , "en" => $block["block_content_en"] ?? ""];
  184. // 移除臨時的分離欄位,只保留合併的 JSON 欄位
  185. unset($block["block_content_tw"]);
  186. unset($block["block_content_en"]);
  187. // 清理區塊內容
  188. if (isset($block['block_content'])) {
  189. foreach ($block['block_content'] as $locale => $content) {
  190. $block['block_content'][$locale] = $this->cleanHtml($content);
  191. }
  192. }
  193. }
  194. return $paragraph;
  195. }
  196. /**
  197. * 處理表格段落(儲存前)
  198. */
  199. protected function preprocessTableParagraph(array $paragraph): array
  200. {
  201. // 確保欄數設定
  202. $columnCount = $paragraph['content']['column_count'] ?? 2;
  203. // 確保表頭存在
  204. if (!isset($paragraph['content']['headers'])) {
  205. $paragraph['content']['headers'] = ['zh_TW' => [], 'en' => []];
  206. }
  207. // 確保表格資料存在
  208. if (!isset($paragraph['content']['simple_table_rows'])) {
  209. $paragraph['content']['simple_table_rows'] = [];
  210. }
  211. // 處理表格資料,確保欄數一致
  212. foreach ($paragraph['content']['simple_table_rows'] as $rowIndex => &$row) {
  213. // 確保每行都有正確的欄數
  214. for ($i = 1; $i <= $columnCount; $i++) {
  215. $row["col{$i}"] = ["zh_TW" => $row["col{$i}_tw"] ?? "" , "en" => $row["col{$i}_en"] ?? ""];
  216. unset($block["block_content_tw"]);
  217. unset($block["block_content_en"]);
  218. $row["align{$i}"] = ["zh_TW" => $row["align{$i}_tw"] ?? "" , "en" => $row["align{$i}_en"] ?? ""];
  219. unset($row["align{$i}_tw"]);
  220. unset($row["align{$i}_en"]);
  221. }
  222. // 移除多餘的欄位
  223. for ($i = $columnCount + 1; $i <= 4; $i++) {
  224. unset($row["col{$i}_tw"]);
  225. unset($row["col{$i}_en"]);
  226. unset($row["align{$i}_tw"]);
  227. unset($row["align{$i}_en"]);
  228. }
  229. }
  230. return $paragraph;
  231. }
  232. /**
  233. * 處理圖片段落(儲存前)
  234. */
  235. protected function preprocessImageParagraph(array $paragraph): array
  236. {
  237. \Log::info("處理圖片段落");
  238. // 確保圖片陣列存在
  239. if (!isset($paragraph['content']['multiple_images'])) {
  240. $paragraph['content']['multiple_images'] = [];
  241. }
  242. // 處理每個圖片
  243. foreach ($paragraph['content']['multiple_images'] as $index => &$image) {
  244. $image['para_img_item_key'] = $image['para_img_item_key'] ?? Str::random();
  245. $image['order'] = $image['order'] ?? ($index + 1);
  246. // 驗證圖片檔案是否存在
  247. if (isset($image['image_url'])) {
  248. // 這裡可以添加圖片驗證邏輯
  249. }
  250. }
  251. return $paragraph;
  252. }
  253. /**
  254. * 更新 paragraphs 關聯(編輯時使用)
  255. */
  256. protected function updateParagraphs(Model $record, array $paragraphs): void
  257. {
  258. // 獲取現有的段落 IDs
  259. $existingIds = collect($paragraphs)
  260. ->pluck('id')
  261. ->filter()
  262. ->values()
  263. ->toArray();
  264. // 刪除不在列表中的段落
  265. $record->paragraphs()
  266. ->whereNotIn('id', $existingIds)
  267. ->delete();
  268. // 更新或建立段落
  269. foreach ($paragraphs as $paragraphData) {
  270. if (isset($paragraphData['id'])) {
  271. // 更新現有段落
  272. $paragraph = $record->paragraphs()->find($paragraphData['id']);
  273. if ($paragraph) {
  274. $paragraph->update([
  275. 'type' => $paragraphData['type'],
  276. 'order' => $paragraphData['order'],
  277. 'content' => $paragraphData['content'],
  278. ]);
  279. }
  280. } else {
  281. // 建立新段落
  282. $newParagraph = $record->paragraphs()->create([
  283. 'type' => $paragraphData['type'],
  284. 'order' => $paragraphData['order'],
  285. 'content' => $paragraphData['content'],
  286. ]);
  287. }
  288. }
  289. }
  290. /**
  291. * 建立 paragraphs 關聯(新增時使用)
  292. */
  293. protected function createParagraphs(Model $record, array $paragraphs): void
  294. {
  295. foreach ($paragraphs as $paragraphData) {
  296. $newParagraph = $record->paragraphs()->create([
  297. 'type' => $paragraphData['type'],
  298. 'order' => $paragraphData['order'],
  299. 'content' => $paragraphData['content'],
  300. ]);
  301. }
  302. }
  303. /**
  304. * 清理 HTML 內容
  305. */
  306. protected function cleanHtml(string $html): string
  307. {
  308. // 這裡可以根據需求自定義清理邏輯
  309. // 例如:移除危險標籤、清理空白等
  310. return trim($html);
  311. }
  312. /**
  313. * 後處理 paragraphs(儲存後使用)
  314. */
  315. protected function postProcessParagraphs($paragraphs): void
  316. {
  317. foreach ($paragraphs as $paragraph) {
  318. \Log::info("後處理段落:", [
  319. 'id' => $paragraph->id,
  320. 'type' => $paragraph->type,
  321. 'content_keys' => array_keys($paragraph->content ?? [])
  322. ]);
  323. // 這裡可以進行:
  324. // - 快取更新
  325. // - 搜尋索引更新
  326. // - 通知發送
  327. // 等後續處理
  328. }
  329. }
  330. /**
  331. * 獲取並格式化關聯資料(載入時的輔助方法)
  332. */
  333. protected function loadAndFormatParagraphs(Model $record): array
  334. {
  335. $paragraphs = $record->paragraphs()
  336. ->orderBy('order')
  337. ->get()
  338. ->toArray();
  339. return $this->formatParagraphsForForm($paragraphs);
  340. }
  341. /**
  342. * 處理表單狀態中的 paragraphs(儲存時的輔助方法)
  343. */
  344. protected function processFormParagraphs(): ?array
  345. {
  346. $formState = $this->form->getState();
  347. if (!isset($formState['paragraphs'])) {
  348. return null;
  349. }
  350. return $this->preprocessParagraphs($formState['paragraphs']);
  351. }
  352. }