| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- <?php
- namespace App\Support\Traits;
- use DOMElement;
- /*
- * Трейт: низкоуровневые хелперы BlockLayoutParser —
- * извлечение полей, токенизация DOM, сериализация.
- * Используется классом BlockLayoutParser.
- */
- trait BlockLayoutHelpersTrait
- {
- // ── Извлечение полей из DOM-элемента ──────────────────────────────────
- /** Рекурсивно собирает поля из $el в массив $raw. */
- private function extractFields(DOMElement $el, array &$raw, string $var): void
- {
- $tag = strtolower($el->tagName);
- // Заголовки → text
- if (in_array($tag, ['h1','h2','h3','h4','h5','h6'])) {
- $text = trim($el->textContent);
- if ($text !== '') {
- $raw[] = $this->mkField('heading', 'Заголовок', 'text', $el, null, $var);
- }
- return;
- }
- // Абзац → textarea
- if ($tag === 'p') {
- $text = trim($el->textContent);
- if ($text !== '') {
- $raw[] = $this->mkField('text', 'Текст', 'textarea', $el, null, $var);
- }
- return;
- }
- // Картинка → image
- if ($tag === 'img') {
- if ($el->getAttribute('src') !== '') {
- $raw[] = $this->mkField('image', 'Изображение', 'image', $el, 'src', $var);
- }
- return;
- }
- // Ссылка → url + text
- if ($tag === 'a') {
- $href = $el->getAttribute('href');
- $text = trim($el->textContent);
- if ($text !== '') {
- $raw[] = $this->mkField('link_text', 'Текст ссылки', 'text', $el, null, $var);
- }
- if ($href !== '' && $href !== '#') {
- $raw[] = $this->mkField('link_url', 'URL ссылки', 'url', $el, 'href', $var);
- }
- return;
- }
- // Листовой элемент с коротким текстом → text
- $childEls = $this->elementChildren($el);
- if (empty($childEls)) {
- $text = trim($el->textContent);
- if ($text !== '' && mb_strlen($text) <= 300) {
- $raw[] = $this->mkField('label', 'Подпись', 'text', $el, null, $var);
- }
- return;
- }
- // Иначе рекурсируем
- foreach ($childEls as $child) {
- $this->extractFields($child, $raw, $var);
- }
- }
- // ── Создание поля ─────────────────────────────────────────────────────
- private function mkField(string $base, string $label, string $type, DOMElement $el, ?string $attr, string $var): array
- {
- $name = $base;
- $i = 2;
- while (isset($this->usedNames[$name])) {
- $name = $base . '_' . $i++;
- }
- $this->usedNames[$name] = true;
- return [
- 'name' => $name,
- 'label' => $label,
- 'type' => $type,
- '_el' => $el,
- '_attr' => $attr,
- '_var' => $var,
- ];
- }
- // ── Токенизация DOM ───────────────────────────────────────────────────
- /** Заменяет содержимое/атрибуты в DOM на уникальные токены. */
- private function applyTokens(array $raw): void
- {
- foreach ($raw as $field) {
- $token = 'BLTKN' . strtoupper($field['name']) . 'END';
- $expr = "{{ {$field['_var']}['{$field['name']}'] }}";
- $this->tokens[$token] = $expr;
- /** @var DOMElement $el */
- $el = $field['_el'];
- if ($field['_attr'] !== null) {
- $el->setAttribute($field['_attr'], $token);
- } else {
- while ($el->firstChild) {
- $el->removeChild($el->firstChild);
- }
- $el->appendChild($this->dom->createTextNode($token));
- }
- }
- }
- private function replaceTokens(string $html): string
- {
- foreach ($this->tokens as $token => $expr) {
- $html = str_replace($token, $expr, $html);
- }
- return $html;
- }
- // ── Сериализация ──────────────────────────────────────────────────────
- private function serializeChildren(DOMElement $el): string
- {
- $html = '';
- foreach ($el->childNodes as $child) {
- $html .= $this->dom->saveHTML($child);
- }
- return $html;
- }
- // ── Хелперы ───────────────────────────────────────────────────────────
- /** Возвращает только публичные поля (без служебных _el, _attr, _var). */
- private function publicFields(array $raw): array
- {
- return array_values(array_map(fn($f) => array_filter(
- $f,
- fn($k) => !str_starts_with($k, '_'),
- ARRAY_FILTER_USE_KEY
- ), $raw));
- }
- /** Возвращает дочерние DOM-элементы (без текстовых узлов). */
- private function elementChildren(DOMElement $el): array
- {
- $result = [];
- foreach ($el->childNodes as $node) {
- if ($node->nodeType === XML_ELEMENT_NODE) {
- $result[] = $node;
- }
- }
- return $result;
- }
- }
|