Генерация оглавления статьи на php
Есть 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>';
Получится так:
Если детальный текст у вас, вдруг, вывелся иероглифами, значит у вас кодировка отличная от 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 и ребутнуть апач.
Есть вопросы или нашли ошибку? Напишите комментарий (можно без регистрации), отвечать стараюсь быстро.