Битрикс. Простой горизонтальный сео-шаблонизатор для интернет-магазина
Rus
Eng
Битрикс. Простой горизонтальный сео-шаблонизатор для интернет-магазина
Решаю сложные технические задачи для Bitrix и 1С: от аудита чужого кода до настройки сложных интеграций с гарантией результата
Рекомендую

Хостинг для сайта

Если выбираете хостинг под проект — советую Timeweb. Современная инфраструктура, широкий выбор тарифов от стартовых до VPS под нагрузку, отзывчивая техподдержка, которая отвечает по существу, а не отписками. Я сам держу на нём часть своих проектов и рекомендую клиентам.

Перейти Партнёрская ссылка. Для вас цена не меняется.

Выводим кастомные Title, H1, Description и произвольный Text на страницах списка товаров разделов

Зададим отдельные шаблоны замены в HL блоке. В каждой записи будет храниться название и правило замены. В названии мы закодируем тип замены и уровень срабатывания. Выглядит это примерно так

Предусмотрим следующие паттерны замены

#CURRENT_SECTION_NAME# - Название текущего уровня каталога
#FIRST_LEVEL_SECTION_NAME# - Название каталога первого уровня
#SECOND_LEVEL_SECTION_NAME# - Название каталога второго уровня
#THIRD_LEVEL_SECTION_NAME# - Название каталога третьего уровня
#BRAND_SECTION_NAME# - Название каталога четвертого уровня
#MIN_PRICE# - Минимальная цена товара в текущем разделе
#SHOP_NAME# - Название магазина ("каталог интернет-магазина Ваше название")

Получаем данные. Например в init.php в событии OnPageStart

global $SEO;

if (Loader::includeModule("highloadblock")){
	$cntIBLOCK_List = 'seoTemplates';

	$cache = new CPHPCache();
	$cache_time = 3600*12*360; //кеш на год
	$cache_id = 'seo'.$cntIBLOCK_List;
	$cache_path = 'seo';
	if ($cache_time > 0 && $cache->InitCache($cache_time, $cache_id, $cache_path))
	{
		$res = $cache->GetVars();
		if (is_array($res["seo"]) && (count($res["seo"]) > 0)){
			$SEO = $res["seo"];
		}
		$SEO['CACHE']='true';

	}
	if (!is_array($SEO))
	{
		$SEO['CACHE']='false';

		$hlblock = HL\HighloadBlockTable::getById(4)->fetch();

		$entity = HL\HighloadBlockTable::compileEntity($hlblock);
		$entity_data_class = $entity->getDataClass();

		$data = $entity_data_class::getList(array(
			"select" => array("*"),
			"order" => array("ID" => "DESC"),
			//"filter" => array("UF_FIELD_1"=>"Значение 1","UF_FIELD_2"=>'Значение 2',"UF_FIELD_3"=>'Значение 3') //Фильтрация выборки
		));

		while($arData = $data->Fetch()){
			$SEO['TEMPLATES'][$arData['UF_TITLE']] = $arData;
		}

		//////////// end cache /////////
		if ($cache_time > 0)
		{
			$cache->StartDataCache($cache_time, $cache_id, $cache_path);
			$cache->EndDataCache(array("seo"=>$SEO));
		}
	}
}

По условию задачи наш шаблонизатор должен действовать только на страницах со списком товаров. Для этого ищем шаблон вашего компонента catalog.section в котором считаем, что у нас уже есть массив похожих данных:

Array
(
    [PATH] => Array
        (
            [0] => Array
                (
                    [ID] => 261
                    [~ID] => 261
                    [IBLOCK_ID] => 15
                    [~IBLOCK_ID] => 15
                    [IBLOCK_SECTION_ID] => 
                    [~IBLOCK_SECTION_ID] => 
                    [NAME] => Груза и джиги
                    [~NAME] => Груза и джиги
                    [DEPTH_LEVEL] => 1
                    [~DEPTH_LEVEL] => 1
                    [IPROPERTY_VALUES] => Array
                        (
                            [ELEMENT_META_TITLE] => TITLE
                            [SECTION_META_TITLE] => TITLE
                            [ELEMENT_META_DESCRIPTION] => DESCRIPTION
                            [SECTION_META_DESCRIPTION] => DESCRIPTION
                            [SECTION_PAGE_TITLE] => TITLE
                            [ELEMENT_META_KEYWORDS] => KEYWORDS
                        )

                )

            [1] => Array
                (
                    [ID] => 262
                    [~ID] => 262
                    [IBLOCK_ID] => 15
                    [~IBLOCK_ID] => 15
                    [IBLOCK_SECTION_ID] => 261
                    [~IBLOCK_SECTION_ID] => 261
                    [NAME] => Аксессуары и оснастка
                    [~NAME] => Аксессуары и оснастка
                    [DEPTH_LEVEL] => 2
                    [~DEPTH_LEVEL] => 2
                    [IPROPERTY_VALUES] => Array
                        (
                            [ELEMENT_META_TITLE] => TITLE
                            [SECTION_META_TITLE] => TITLE
                            [ELEMENT_META_DESCRIPTION] => DESCRIPTION
                            [SECTION_META_DESCRIPTION] => DESCRIPTION
                            [SECTION_PAGE_TITLE] => TITLE
                            [ELEMENT_META_KEYWORDS] => KEYWORDS
                        )

                )

            [2] => Array
                (
                    [ID] => 2238
                    [~ID] => 2238
                    [IBLOCK_ID] => 15
                    [~IBLOCK_ID] => 15
                    [IBLOCK_SECTION_ID] => 262
                    [~IBLOCK_SECTION_ID] => 262
                    [NAME] => Strike Pro
                    [~NAME] => Strike Pro
                    [DEPTH_LEVEL] => 3
                    [~DEPTH_LEVEL] => 3
                    [IPROPERTY_VALUES] => Array
                        (
                            [ELEMENT_META_TITLE] => TITLE
                            [SECTION_META_TITLE] => TITLE
                            [ELEMENT_META_DESCRIPTION] => DESCRIPTION
                            [SECTION_META_DESCRIPTION] => DESCRIPTION
                            [SECTION_PAGE_TITLE] => TITLE
                            [ELEMENT_META_KEYWORDS] => KEYWORDS
                        )

                )

        )

)

Добавляем или редактируем файл component_epilog.php

global $SEO,$cacheName;

$key = array_key_last($arResult['PATH']);

$cacheName = 'SEO'.$arResult['PATH'][$key]['IBLOCK_SECTION_ID'];
$arSEO = getFromCache($cacheName);

if($cacheName){
	$frame = new \Bitrix\Main\Page\FrameHelper('sectionSEO'.$cacheName);
	$frame->begin();

	if(!$arSEO){
		$arData = [
			'DEPTH' => $arResult['PATH'][$key]['DEPTH_LEVEL'],
			'CURRENT_SECTION_NAME' => $arResult['PATH'][$key]['NAME'],
			'FIRST_LEVEL_SECTION_NAME' => $arResult['PATH'][0]['NAME'],
			'SECOND_LEVEL_SECTION_NAME' => $arResult['PATH'][1]['NAME'],
			'THIRD_LEVEL_SECTION_NAME' => $arResult['PATH'][2]['NAME'],
			'BRAND_SECTION_NAME' => $arResult['PATH'][3]['NAME'],
			'MIN_PRICE' => getMinPriceInSections(['IBLOCK_SECTION_ID'=>$arResult['PATH'][$key]['IBLOCK_SECTION_ID'],'PRICE_GROUP'=>1]),
			'SHOP_NAME' => 'каталог интернет-магазина Ваше название',
			//Сюда получаем значения доп. свойств текущего раздела
			//Ведь по условию, заданные здесь правила более приоритетны
			'CURRENT_SECTION_CUSTOM_PROPS' => getSectionProps(['IBLOCK_ID'=>$arResult['PATH'][$key]['IBLOCK_ID'],'ID'=>$arResult['PATH'][$key]['ID']]),
		];

		$text = $SEO['TEMPLATES']['Text_'.$arData['DEPTH'].'_level']['UF_VALUE'];
		if($text){
			$arData['RESULT']['TEXT'] = patternReplace($arData,$text);
		}

		$h1 = $SEO['TEMPLATES']['H1_'.$arData['DEPTH'].'_level']['UF_VALUE'];
		if($h1 && !$arData['CURRENT_SECTION_CUSTOM_PROPS']['UF_H1']){
			$arData['RESULT']['H1'] = patternReplace($arData,$h1);
		}

		$description = $SEO['TEMPLATES']['Description_'.$arData['DEPTH'].'_level']['UF_VALUE'];
		if($description && !$arData['CURRENT_SECTION_CUSTOM_PROPS']['UF_DESC']){
			$arData['RESULT']['DESCRIPTION'] = patternReplace($arData,$description);
		}

		$title = $SEO['TEMPLATES']['Title_'.$arData['DEPTH'].'_level']['UF_VALUE'];
		if($title && !$arData['CURRENT_SECTION_CUSTOM_PROPS']['UF_TITLE']){
			$arData['RESULT']['TITLE'] = patternReplace($arData,$title);
		}

		setToCache($cacheName,$arData);

		$arSEO['BODY'] = $arData;
	}

	if(!empty($arSEO['BODY']['RESULT'])){
		foreach ($arSEO['BODY']['RESULT'] as $key => $value){
			switch ($key) {
				case 'TITLE':
					$APPLICATION->SetPageProperty('Tit', $value);
					break;
				case 'H1':
					$APPLICATION->SetPageProperty('H1', $value);
					break;
				case 'DESCRIPTION':
					$APPLICATION->SetPageProperty("desc", $value);
					break;
				case 'TEXT':
					global $sectionText;
					if(!$sectionText){
						$APPLICATION->SetPageProperty("text", $value);
					}
					break;
			}
		}
	}

	$frame->end();
}

В init.php добавляем

/* Дополнительные функции */
require($_SERVER['DOCUMENT_ROOT'].'/local/libs/init/functions.php');

Листинг файла functions.php

/* Получаем последний ключ массив */
if (! function_exists("array_key_last")) {
    function array_key_last($array) {
        if (!is_array($array) || empty($array)) {
            return NULL;
        }

        return array_keys($array)[count($array)-1];
    }
}

/* Получаем минимальную цену товаров в разделе */
if (! function_exists("getMinPriceInSections")) {
    function getMinPriceInSections($arrData) {
        $minPriceSection = false;
        $priceGroup = $arrData['PRICE_GROUP'];

        $arItems = \CIBlockElement::GetList(["CATALOG_PRICE_{$priceGroup}" => "ASC,NULL"], ["SECTION_ID" => $arrData['IBLOCK_SECTION_ID'], "ACTIVE" => "Y", 'INCLUDE_SUBSECTIONS' => 'Y']);
        $item = $arItems->Fetch();

        if (!empty($item)) {
            //Цена с валютой
            $minPriceSection = \CCurrencyLang::CurrencyFormat((int) $item["CATALOG_PRICE_{$priceGroup}"], $item["CATALOG_CURRENCY_{$priceGroup}"], true);

            //Просто цена без валюты
            //$minPriceSection = (int) $item["CATALOG_PRICE_{$priceGroup}"];
        }

        return $minPriceSection;
    }
}

/* Функция замены */
if (! function_exists("patternReplace")) {
    function patternReplace($arrData,$mask) {

        foreach($arrData as $key => $value){
            if(strpos($mask, $key) !== false){//Исключаем из замены массивы и символы ~
                //Заменяем все возможные паттерны
                $mask = preg_replace('~\#'.$key.'\#~', $value, $mask);
            }

        }

        return $mask;
    }
}

/* Получаем данные из кеша */
if (! function_exists("getFromCache")) {
    function getFromCache($name) {
        CModule::IncludeModule("iblock");

        $cache = new CPHPCache();
        $cache_time = 3600*12; //кеш на сутки
        $cache_id = 'customCache'.$name;
        $cache_path = 'customCache';

        $customCache = false;

        if ($cache_time > 0 && $cache->InitCache($cache_time, $cache_id, $cache_path))
        {
            $res = $cache->GetVars();
            if (is_array($res["customCache"]) && (count($res["customCache"]) > 0)){
                $customCache = $res["customCache"];
            }
            $customCache['CACHE']='true';
        }

        return $customCache;
    }
}

/* Заносим данные в кеш */
if (! function_exists("setToCache")) {
    function setToCache($name, $data) {
        CModule::IncludeModule("iblock");

        $cache = new CPHPCache();
        $cache_time = 3600*12; //кеш на сутки
        $cache_id = 'customCache'.$name;
        $cache_path = 'customCache';

        $customCache['CACHE'] = 'false';
        $customCache['BODY'] = $data;

        //////////// end cache /////////
        if ($cache_time > 0)
        {
            $cache->StartDataCache($cache_time, $cache_id, $cache_path);
            $cache->EndDataCache(array("customCache"=>$customCache));
        }

        return $customCache;
    }
}

/* Получаем доп. свойства раздела */
if (! function_exists("getSectionProps")) {
    function getSectionProps($data) {
        $return = false;

        $dbSection = CIBlockSection::GetList(Array(), array(
            "IBLOCK_ID" => $data['IBLOCK_ID'],
            "ID" => $data['ID']
        ), false ,Array("UF_H1","UF_TITLE","UF_DESC"));

        if($arSection = $dbSection->GetNext()){
            if($arSection['UF_H1']){
                $return['UF_H1'] = $arSection['UF_H1'];
            }
            if($arSection['UF_TITLE']){
                $return['UF_TITLE'] = $arSection['UF_TITLE'];
            }
            if($arSection['UF_DESC']){
                $return['UF_DESC'] = $arSection['UF_DESC'];
            }

        }

        return $return;
    }
}

/* Получаем правильный H1 */
if(!function_exists('aSH1')){
    function aSH1(){
        global $APPLICATION;
        if(!$APPLICATION->GetPageProperty("H1")){
            return $APPLICATION->GetTitle(false);
        } else {
            $h1 = $APPLICATION->GetPageProperty("H1");
            if(!$h1 || $h1==false || $h1=='false')
            {
                return '';
            } elseif(strlen($h1)<=0){
                return $APPLICATION->GetTitle(false);
            } else {
                return $h1;
            }
        }
    }
}

/* Получаем правильный Title */
if(!function_exists('aSTitle')){
    function aSTitle(){
        global $APPLICATION;
        if(!$APPLICATION->GetPageProperty("Tit")){
            //return $APPLICATION->GetTitle(false);
            return $APPLICATION->GetTitle('Title');
        } else {
            $aSTitle = $APPLICATION->GetPageProperty("Tit");
            if(!$aSTitle || $aSTitle==false || $aSTitle=='false')
            {
                return '';
            } elseif(strlen($aSTitle)<=0){
                //return $APPLICATION->GetTitle(false);
                return $APPLICATION->GetTitle('Title');
            } else {
                return $aSTitle;
            }
        }
    }
}

/* Получаем правильный Text */
if(!function_exists('aSText')){
    function aSText(){
        global $APPLICATION;
        if(!$APPLICATION->GetPageProperty("text")){
            //return $APPLICATION->GetTitle(false);
            return $APPLICATION->GetTitle('Title');
        } else {
            $aSTitle = $APPLICATION->GetPageProperty("text");
            if(!$aSTitle || $aSTitle==false || $aSTitle=='false')
            {
                return '';
            } elseif(strlen($aSTitle)<=0){
                //return $APPLICATION->GetTitle(false);
                return $APPLICATION->GetTitle('Title');
            } else {
                return $aSTitle;
            }
        }
    }
}

/* Получаем правильный Description */
if(!function_exists('aSDescription')){
    function aSDescription(){
        global $APPLICATION;
        if(!$APPLICATION->GetPageProperty("desc")){
            //return $APPLICATION->GetTitle(false);
            return $APPLICATION->GetTitle('description');
        } else {
            $aSTitle = $APPLICATION->GetPageProperty("desc");
            if(!$aSTitle || $aSTitle==false || $aSTitle=='false')
            {
                return '';
            } elseif(strlen($aSTitle)<=0){
                //return $APPLICATION->GetTitle(false);
                return $APPLICATION->GetTitle('description');
            } else {
                return $aSTitle;
            }
        }
    }
}

В файле header.php меняем вызов title c

<title><?$APPLICATION->ShowTitle()?></title>

на

<title><?$APPLICATION->AddBufferContent('aSTitle');?></title>

Выводим правильный description

<meta name="description" content="<?$APPLICATION->AddBufferContent('aSDescription');?>" />

И отключаем вывод description в файле bitrix\modules\main\classes\general\main.php в функции ShowHead (~843 стр.)

//$this->ShowMeta("description", false, $bXhtmlStyle);

Заменяем вывод h1 на

<h1><?$APPLICATION->AddBufferContent('aSH1');?></h1>

Так же выводим текст в нужном нам месте

global $sectionText;
$sectionText = $arSection["UF_TEXT"];

$APPLICATION->SetPageProperty("text", $arSection["UF_TEXT"]);
<div class="description_collapse_text"><?$APPLICATION->AddBufferContent('aSText');?></div>
Александр Тихий
Автор статьи

Александр Тихий

Fullstack-разработчик · 1С-Битрикс

Разрабатываю и развиваю интернет-магазины и корпоративные порталы на 1С-Битрикс. Интеграции с 1С, highload-нагрузки, безопасность по 152-ФЗ.


Комментарии

Комментариев еще нет, Вы можете стать первым кто его оставит

Оставьте комментарий

На сайте используется система премодерирования комментариев, поэтому ваше сообщение будет опубликовано лишь после одобрения модератором

Вы отвечаете на комментарий пользователя

Отправить

ОБРАТНАЯ СВЯЗЬ

Напишите мне

Напишите мне, чтобы обсудить задачу. Работаю удаленно, официально (самозанятый), стоимость часа — 1800 ₽, в день могу уделять проекту 3–4 часа. Отвечаю быстро, консультация бесплатная.

Во время отправки произошла ошибка, пожалуйста попробуйте еще раз через некоторое время
Сообщение отправлено успешно

Телефоны

+7(993) 007-18-96

Email

info@tichiy.ru

Адрес

Россия, г. Москва

Отправляя форму Вы автоматически подтверждаете, что ознакомились и принимаете Политику конфиденциальности сайта