BlockLayoutParser.php 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. <?php
  2. namespace App\Support;
  3. use DOMDocument;
  4. use DOMElement;
  5. use App\Support\Traits\BlockLayoutDetectorTrait;
  6. use App\Support\Traits\BlockLayoutBuilderTrait;
  7. use App\Support\Traits\BlockLayoutHelpersTrait;
  8. /*
  9. * BlockLayoutParser — разбирает произвольный HTML-фрагмент и автоматически:
  10. * 1. Определяет поля блока (плоские или repeater-группу).
  11. * 2. Генерирует черновик Blade-шаблона с {{ $data[...] }} / {{ $item[...] }}.
  12. *
  13. * Логика разбита по трейтам (app/Support/Traits/):
  14. * BlockLayoutDetectorTrait — detectRepeater, allSimilar
  15. * BlockLayoutBuilderTrait — buildFlat, buildRepeater
  16. * BlockLayoutHelpersTrait — extractFields, mkField, applyTokens, replaceTokens,
  17. * serializeChildren, publicFields, elementChildren
  18. */
  19. class BlockLayoutParser
  20. {
  21. use BlockLayoutDetectorTrait;
  22. use BlockLayoutBuilderTrait;
  23. use BlockLayoutHelpersTrait;
  24. private DOMDocument $dom;
  25. private array $usedNames = [];
  26. private array $tokens = []; // token => bladeExpr
  27. /**
  28. * Разбирает HTML и возвращает:
  29. * fields — определения полей для BlockLayoutRegistry
  30. * blade — черновик Blade-шаблона
  31. * is_repeater — обнаружена ли repeater-структура
  32. */
  33. public function parse(string $html): array
  34. {
  35. $this->usedNames = [];
  36. $this->tokens = [];
  37. $this->dom = new DOMDocument('1.0', 'UTF-8');
  38. libxml_use_internal_errors(true);
  39. // charset-мета нужна чтобы DOMDocument корректно читал UTF-8 (emoji, кириллица)
  40. $this->dom->loadHTML(
  41. '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
  42. . '<div id="BLPARSER">' . $html . '</div>'
  43. );
  44. libxml_clear_errors();
  45. $wrapper = $this->dom->getElementById('BLPARSER');
  46. if (!$wrapper) {
  47. return ['fields' => [], 'blade' => $html, 'is_repeater' => false];
  48. }
  49. // Ищем repeater-группу
  50. $repeater = $this->detectRepeater($wrapper);
  51. if ($repeater) {
  52. [$container, $items] = $repeater;
  53. return $this->buildRepeater($wrapper, $container, $items);
  54. }
  55. return $this->buildFlat($wrapper);
  56. }
  57. }