Abr@X@bra.ru
ЧПУ компонентов битрикса от корня сайта

ЧПУ компонентов битрикса от корня сайта

14.02.2017
846

Исходные данные.

  • Есть старый сайт, с каталогом и статьями.

  • Сайт проиндексирован где только можно.

  • Есть новый сайт и нужно сохранить урлы при переносе контента.

  • На старом сайте все урлы идут от корня сайта. На пример, карточка товара oldsite.ru/nazvsine_tovara.htm

  • Тоже самое и со статьями - oldsite.ru/detalka_novosti.htm

На новом сайте товары разложены по категориям и нужно поставить чпу так, как на старом сайте.

Начинаем эпопею с простановки параметров чпу в комплексном компоненте каталога bitfix:catalog.

Создаем файлик в корне /catalog.php, в него вызов компонента и ставим чпу.


Ибо у нас есть в меню список категорий, переходя по которым, открывается список товаров. Все стандартно. Но на старом сайте при переходе в какую-либо категорию каталога, урл выглядит oldsite.ru/nazvanie_kategorii_tovarov.htm. Значит, в настройки раздела ставим #SECTION_CODE#.htm. Тоже самое с деталкой товара.

И вот тут начинает кое-что интересное.

При переходе по меню разделов каталога у нас все хорошо. А вот при переходе на товар, компонент матерится – не может найти раздел. А ведь должен искать товар по символьному коду вместо раздела. 

Должен, да не обязан. 

Ковыряем исходный код /bitrix/components/bitrix/catalog/component.php.

В конце файла есть вызов 

$this->IncludeComponentTemplate($componentPage);

где $componentPage – это страница, которую надо подключить, в которой лежит либо деталка каталога, либо список товаров.

Соответственно, когда нам нужна деталка, $componentPage = 'element', а когда список - $componentPage = 'section';

Выведя var_dump($componentPage) мы увидим там 'element'.

Выходит компонент не понимает что ему отдали в урле – элемент инфоблока или категорию. И он начинает искать категорию, не находит и вываливает ошибку, либо кидает на 404-ю страницу.

Значит, нужно его научить искать то что нужно.

Для этого копируем в свое пространство имен компонент /bitrix/components/bitrix/catalog.

И на 109 стр. ставим небольшую проверочку.

if ($componentPage == 'section'){
	$rowSection = Bitrix\Iblock\SectionTable::getRow([
		'select' => ['CODE'],
		'filter' => ['IBLOCK_ID' => $arParams['IBLOCK_ID'], '=CODE' => $arResult['VARIABLES']['SECTION_CODE']],
	]);
	if (is_null($rowSection)){
		$arResult['VARIABLES']['ELEMENT_CODE'] = $arResult['VARIABLES']['SECTION_CODE'];
		unset($arResult['VARIABLES']['SECTION_CODE']);
		$componentPage = 'element';
	}
}
Которая говорит, что если у нас $componentPage == 'section' т.е. мы запросили категорию, давайте убедимся что это действительно категория и она существует в данном ИБ. И вот ежели ее нет, то значит от компонента пытаются получить деталку товара. И нам нужно переписать $componentPage = 'element'. И грохнуть unset($arResult['VARIABLES']['SECTION_CODE']);

После этого деталка должна открываться как надо.

Запрос SectionTable::getRow можно модифицировать в запрос SELECT CNT AS COUNT(*)… И это желательно делать на больших каталогах, ибо нагрузка меньше.

Можно еще и в кеш ложить, это по желанию и необходимости.

С каталогом разобрались, но как быть с статьями? Ведь их урл тоже должен быть newsite.ru/detalka_novosti.htm.

И вот тут уже придется малость подкрутить компонент bitrix:news.detail. Для чего, его тоже тащим в свое пространство имен. И не забываем изменить вызов его в файле detail.php вашего шаблона комплексного компонента новостей.

Как работает развязка урлов комплексных компонентов с чпу.

Клиент приходит на урл  newsite.ru/detalka_novosti.htm. Сервер сообщает что такой страницы нет, и врубается /404.php (эти настройки прописаны в /.htaccess).

В /404.php врубается файл bitrix/modules/main/include/urlrewrite.php, который, в свою очередь, юзает файл /urlrewrite.php из корня сайта. В этот файл сохраняются настройки чпу компонента, когда мы жмакаем «сохранить» в окошке настроек компонента на публичной части (или при редактировании страницы в админке).

Получается что-то вроде 

array(
	"CONDITION" => "#^/#",
	"RULE" => "",
	"ID" => "bitrix:news",
	"PATH" => "/articles.php",
),

bitrix:news – компонент, CONDITION – относительно какой папки строится чпу, PATH – файл, где реально лежит комплексный компонент.

В нашей задаче получится что и каталог и новости будут иметь одинаковый CONDITION. А именно по нему определяется, какое правило использовать.

array(
	"CONDITION" => "#^/#",
	"RULE" => "",
	"ID" => "ab:catalog",
	"PATH" => "/catalog.php",
),
array(
	"CONDITION" => "#^/#",
	"RULE" => "",
	"ID" => "bitrix:news",
	"PATH" => "/articles.php",
),

Получается, что, при загрузке страницы детальной новости, система доходит до правила, которое для каталога("ID" => "ab:catalog") и начинает его использовать. Но нам-то нужна новость, а не каталог. И мы закономерно получаем отлуп 404-ой страницы.

Поменяем местами эти правила, чтоб сначала доходило до новости, потом до каталога.

array(
	"CONDITION" => "#^/#",
	"RULE" => "",
	"ID" => "bitrix:news",
	"PATH" => "/articles.php",
),
array(
	"CONDITION" => "#^/#",
	"RULE" => "",
	"ID" => "ab:catalog",
	"PATH" => "/catalog.php",
)

Теперь новости работают, а каталог отвалился.

Идем в /local/components/my_folder/news.detail/component.php примерно на 431 стр. и перед

Iblock\Component\Tools::process404(
	trim($arParams["MESSAGE_404"]) ?: GetMessage("T_NEWS_DETAIL_NF")
	,true
	,$arParams["SET_STATUS_404"] === "Y"
	,$arParams["SHOW_404"] === "Y"
	,$arParams["FILE_404"]
);

Врубаем это

$siteId = Context::getCurrent()->getSite();
$arUrlRewriteTmp = \Bitrix\Main\UrlRewriter::getList($siteId);
$arUrlRewrite = [];
$requestUri = Context::getCurrent()->getRequest()->getRequestUri();
$Uri = new Bitrix\Main\Web\Uri($requestUri);
$realPath = Context::getCurrent()->getServer()->get('REAL_FILE_PATH');
if($realPath == '/articles.php'){
	$realPath = '/catalog.php';
}
$Uri->setPath($realPath);
$requestUri = $Uri->getUri();
$io = CBXVirtualIo::GetInstance();
foreach ($arUrlRewriteTmp as $k => $val) {
	if($val['ID'] === 'bitrix:news' && $val['CONDITION'] === '#^/#'){
		continue;
	}

	if(preg_match($val["CONDITION"], $requestUri))
	{
		if (strlen($val["RULE"]) > 0)
			$url = preg_replace($val["CONDITION"], (strlen($val["PATH"]) > 0 ? $val["PATH"]."?" : "").$val["RULE"], $requestUri);
		else
			$url = $val["PATH"];

		if(($pos=strpos($url, "?"))!==false)
		{
			$params = substr($url, $pos+1);
			parse_str($params, $vars);
			unset($vars["SEF_APPLICATION_CUR_PAGE_URL"]);

			$_GET += $vars;
			$_REQUEST += $vars;
			$_SERVER["QUERY_STRING"] = $QUERY_STRING = CHTTP::urnEncode($params);
			$url = substr($url, 0, $pos);
		}

		$url = _normalizePath($url);

		if(!$io->FileExists($_SERVER['DOCUMENT_ROOT'].$url))
			continue;

		if (!$io->ValidatePathString($url))
			continue;

		$urlTmp = strtolower(ltrim($url, "/\\"));
		$urlTmp = str_replace(".", "", $urlTmp);
		$urlTmp7 = substr($urlTmp, 0, 7);

		if (($urlTmp7 == "upload/" || ($urlTmp7 == "bitrix/" && substr($urlTmp, 0, 16) != "bitrix/services/" && substr($urlTmp, 0, 18) != "bitrix/groupdavphp")))
			continue;

		$ext = strtolower(GetFileExtension($url));
		if ($ext != "php")
			continue;

		CHTTP::SetStatus("200 OK");
		$_SERVER["REAL_FILE_PATH"] = $url;
		include_once($io->GetPhysicalName($_SERVER['DOCUMENT_ROOT'].$url));
		die();
	}
}

Кусок стащен из ядра, из файла /bitrix/modules/main/include/urlrewrite.php.

Для нас важен кусок
$arUrlRewriteTmp = \Bitrix\Main\UrlRewriter::getList($siteId);

if($realPath == '/articles.php'){
	$realPath = '/catalog.php';
}
$Uri->setPath($realPath);
$requestUri = $Uri->getUri();
......

Который говорит, что, нужно получить сипсок правил из файла /urlrewrite.php. И, если сейчас запросили новости /articles.php (а мы находимся в блоке, когда деталка не найдена и должна показать 404 страница), то нужно переписать $realPath = '/catalog.php'

Зафигачить ее в запроценный uri $Uri->setPath($realPath) чтоб не потерять гет-параметры запроса. И записать $requestUri = $Uri->getUri().

И далее при поиске правил 

foreach ($arUrlRewriteTmp as $k => $val) {
	if($val['ID'] === 'bitrix:news' && $val['CONDITION'] === '#^/#'){
		continue;
	}
.....

пропусить все правила от компонента bitrix:news с 'CONDITION' == #^/# т.е если его чпу тоже настроены от корня сайта.

Таким образом система дойдет до правил для каталога 
array(
	"CONDITION" => "#^/#",
	"RULE" => "",
	"ID" => "ab:catalog",
	"PATH" => "/catalog.php",
),

Найдет файл компонента каталога и вызовет его

include_once($io->GetPhysicalName($_SERVER['DOCUMENT_ROOT'].$url));
die();

Таким образом, у нас и каталог, и новости будут иметь урлы от корня сайта, без промежуточных страниц и разделов.




ЧПУ
Читайте также:
Как управлять меню Битрикс

Как управлять меню Битрикс

Управление меню может осуществляться как из Публичной части сайта, так и из Административной. Контент-мен...
Читать
Как создать информационный блок Битрикс

Как создать информационный блок Битрикс

Управление информационными блоками выбранного типа осуществляется на странице Информационные блоки (Контент &g...
Читать
Как сделать экспорт данных в XML Битрикс

Как сделать экспорт данных в XML Битрикс

Примечание: начиная с версии 15.0.6 модуля Информационные блоки, доступ к экспорту инфоблока имеют только те п...
Читать