Генерация оглавления статьи на php

176
Алексей,
Генерация оглавления статьи на php
Генерация оглавления статьи на php

Есть 2 варианта: найти заголовки регулярками и распарсить dom. Я покажу второй вариант с использованием класса DOMDocument.

Есть 2 варианта решения задачи: найти заголовки регулярками и распарсить dom. Я покажу второй вариант с использованием класса DOMDocument.

if ($arResult['DETAIL_TEXT'] && class_exists('DOMDocument')) {
	$dom = new DOMDocument('1.0', 'UTF-8');
	$dom->loadHTML('<?xml version="1.0" encoding="UTF-8"?>'.$arResult['DETAIL_TEXT']);
	$arMenu = [];
	if (!function_exists('recurceHTML')) { 
		function recurceHTML(&$node, &$arMenu){ // ищет заголовки и формирует по ним меню
			$nodeId = md5($node->nodeName . $node->nodeType . $node->textContent);
			if ($node->nodeName === 'h2' || $node->nodeName === 'h3') { // берем только теги h2 и h3
				$arMenu[] = [
					'ID' => $nodeId, // string(32) "c0b6a771e7d68..."
					'TAG' => $node->nodeName, // h2 или h3
					'DEPTH_LEVEL' => ($node->nodeName === 'h2') ? 1 : 2, // глубина вложенности
					'NAME' => $node->textContent // текст заголовка
				];
				$node->setAttribute('id', $nodeId); // атрибут понадобится для создания ссылки на этот заголовок href="#id"
			}

			if ($node->nodeType == 1) {
				foreach ($node->childNodes as $childNode) {
					recurceHTML($childNode, $arMenu);
				}
			}
		}
	}
	foreach ($dom->childNodes as $child) { // рекурсивно проходимся по всему детальному тексту
		recurceHTML ($child, $arMenu);
	}
	$arResult['DETAIL_TEXT'] = str_replace(['<html>', '<body>', '</html>', '</body>'], '', $dom->saveHTML($child));

	foreach ($arMenu as $i =>  $arItem) {
		if ($arItem['TAG'] === 'h3' && $arMenu[$i-1]['TAG'] === 'h2') { // простенькое создание многоуровневой структуры
			$arMenu[$i-1]['IS_PARENT'] = 'Y';
		}

	}
	$arResult['MENU'] = $arMenu;
}

С помощью данного кода собирается массив с оглавлением. В который попадают данные заголовков h2 и h3 из детального текста. В самом тексте заголовкам добавляется атрибут id для работы ссылки-якоря. Код можно вставить в result_modifier.php.

Для построения оглавления можно использовать подобный код:

echo '<ol>';
	$pl = 0;
	foreach($arResult['MENU'] as $arItem) {

		if ($pl && $arItem["DEPTH_LEVEL"] < $pl) {
			echo str_repeat("</ul></li>", ($pl - $arItem["DEPTH_LEVEL"]));
		}

		if ($arItem["IS_PARENT"]) {
			echo '<li><a href="#'.$arItem["ID"].'">'.$arItem["NAME"].'</a><ul>';
		} else {
			echo '<li><a href="#'.$arItem["ID"].'">'.$arItem["NAME"].'</a></li>';
		}

		$pl = $arItem["DEPTH_LEVEL"];
	}
	if ($pl > 1) { // close last item tags
		echo str_repeat("</ul></li>", ($pl-1) );
	}
echo '</ol>';

Получится так:

Пример многоуровневого оглавления статьи, сформированного на php

Если детальный текст у вас, вдруг, вывелся иероглифами, значит у вас кодировка отличная от UTF-8. Нужно будет перед $dom->loadHTML сконвертировать (iconv например) в UTF-8, а после работы скрипта обратно в свою кодировку. Замена значения кодировки в скрипте (например new DOMDocument('1.0', SITE_CHARSET)) не помогает. По крайней мере, с однобайтовой windows-1251 работать не захотело.

Если видите ошибку Class 'DOMDocument' not found, надо включить библиотеку php-dom, в битрикс окружении /etc/php.d/20-dom.ini.disabled переименовать в 20-dom.ini и ребутнуть апач.

Опубликовано 25 марта 2022 | Обновлено 29 марта 2022
оглавление, php, готовый код, содержание
Поделиться
Комментарии
Авторизуйтесь чтобы получать уведомления об ответе
Есть 2 варианта: найти заголовки регулярками и распарсить dom. Я покажу второй вариант с использованием класса DOMDocument.