Недавно мне довелось поработать с довольно интересным типовым решением TopShop (
https://marketplace.1c-bitrix.ru/solutions/alexkova.emarket/) версии 1.6.60 по задаче микроразметки. В данной статье описана последовательность действий, которая позволит любому администратору/разработчику без особых усилий добавить поддержку микроразметки на свой сайт на базе данного решения с помощью моего модуля "Микроразметка Schema.org"
Статическая микроразметка
Адрес Организации и Хлебные крошки
Эти 2 типа микроразметки для большинства магазинов принимают на вход статические параметры и не меняются в зависимости от страницы. Вы просто настраиваете их один раз, размещаете на странице, во включаемой области или в шаблоне сайта и они работают так, как им и положено.
Итак, находим подходящее место (например сквозную включаемую область в подвале страницы с копирайтом) и открываем её на редактирование в режиме визуального редактора:
Добавляем в режиме визуального редактора 2 компонента: Адрес и Хлебные крошки - перетащив их из правой панели списка в рабочую область мышью:
Двойной клик по иконке компонент в рабочем поле откроет его параметры (на примере компонента Адреса):
Заполняем параметры обоих компонентов.
- Спецификация и документация для Адреса (заполняем все поля, какие сочтём нужным. Кашу маслом не испортишь, как не испортишь сайт микроразметкой)
- Документация для Хлебных крошек (ничего особенного, компонент выводит данные автоматически, нужно лишь проверить правильность формирования цепочки, которая генерируется на основе файловой структуры и данных от компонентов на странице).
Важно!
v.1.6.60 типового решения TopShop не содержит компонента хлебных крошек на главной, а некоторые компоненты добавляют запись в цепочку навигации, поэтому сразу после добавления компонента для микроразметки я получил длинную и некрасивую цепочку.
Проблема решается снятие галочек "Включать инфоблок в цепочку навигации":
|
После настройки компонентов добавляем галочку "Не отображать на сайте":
Она добавит компоненту параметр:
"SHOW" => "Y",
Это универсальный параметр, имеющийся у всех компонентом модуля "Микроразметка Schema.org". Значение Y говорит компоненту, что параметр не следует показывать обычным пользователям, но отдавать поисковым системам. Это позволяет вписать компонент в любой сайт без изменения его внешнего вида.
Микроразметка Адреса и Хлебных крошек готова.
Динамическая микроразметка
Динамическая разметка - это та, где нельзя 1 раз заполнить параметры и вывести данные вручную.
Например, карточка товара и информация о ценовом предложении - товаров на сайте интернет-магазина множество и они формируются автоматически на основании структуры инфоблока. А значит так же автоматически необходимо формировать и микроразметку.
Для этого потребуется внести небольшие изменения в шаблон компонента, отвечающего за вывод соответствующей информации в публичной части, т.е. выполнить функции Администратора/Разработчика.
Схема действий всегда будет одинаковой:
-
Берём пустую страницу (например создаём её в административной разделе) и открываем в визуальном редакторе;
-
Размещаем на странице компонент микроразметки (аналогично тому как выше сделано для Адреса);
-
Заполняем те параметры, которые в дальнейшем будут нами использоваться в динамическом компоненте, статическими заглушками (например #NAME# для параметра с названием товара, #PRICE# для цены и т.д.);
-
Переключаемся в режим исходного кода для получения кода вызова компонента с нашими заглушками (см скриншот ниже);
-
Находим шаблон компонента, отвечающего за вывод в публичной части той сущности, которую мы хотим разметить (например, детальной страницы товара), копируем его (иначе наша правка может быть затёрта при обновлении решения) и открываем копию для редактирования в режиме php;
-
Выбираем место для вставки вызова нашего компонента;
-
Заменяем заглушку (#NAME#, #PRICE# и т.д.) на переменные с данными, использующимися шаблоном (для изучения структуры массива с данными и выяснения названий ключей/переменных рекомендуется использовать конструкцию
<? echo "<pre>"; print_r($arResult); echo "</pre>";?>
).
Товары и Цены
Заходим в публичной части на страницу товара, авторизовавшись под Администратором.
Включаем режим правки.
Открываем список компонентов (с помощью стрелочки рядом с "шестерёнкой" открывающей параметры компонента):
Нас интересуют в первую очередь "Элемент каталога Детально" и "Каталог".
Однако мы видим, что для пункта "Элемент каталога Детально" нет возможности копирования или редактирования шаблона:
Это говорит о том, что наш компонент каталога - комплексный и не использует отдельные самостоятельные компоненты детальной.
А значит, нам придётся скопировать весь компонент целиком:
Я скопировал шакблок компонента в шаблон сайта "topshop_orange_flat" (это один из шаблонов TopShop, тот который был установлен при установке решения Мастером) и назвал его "template".
Теперь переходим в папку с шаблоном компонента (путь в ней будет /bitrix/templates/topshop_orange_flat/components/alexkova.emarket/catalog/template/ цветом я выделил название шаблона сайта и шаблона компонента, поскольку эти значения у вас могут отличаться, если вы выбрали другой шаблон или переименовали его иначе).
Если мы посмотрим содержимое файла /bitrix/templates/topshop_orange_flat/components/alexkova.emarket/catalog/template/element.php, отвечающего за вывод детальной страницы каталога (Элемента), то поймём что нас интересует компонент catalog.element в пространстве имён bitrix с дефолтным (пустота идентична .default) шаблоном. Искать его надлежит прямо в папке шаблона компонента каталога. И интересует нас в нём файл template.php (т.е. файл шаблона):
/bitrix/templates/topshop_orange_flat/components/alexkova.emarket/catalog/template/bitrix/catalog.element/.default/template.php
Добавляем в конце шаблона вызов компонента Товара (coffeediz:schema.org.Product) с указанием цены, заменив заглушки на переменные с ключами массивов, где хранятся данные:
<?$APPLICATION->IncludeComponent(
"coffeediz:schema.org.Product",
"",
Array(
"SHOW" => "Y",
"NAME" => $arResult['NAME'],
"DESCRIPTION" => $arResult['DETAIL_TEXT'],
"PRICE" => $arResult['CATALOG_PRICE_1'],
"PRICECURRENCY" => $arResult['CATALOG_CURRENCY_1'],
"ITEMAVAILABILITY" => "InStock",
"ITEMCONDITION" => "NewCondition",
"PAYMENTMETHOD" => array("VISA", "MasterCard", "Cash"),
"PARAM_RATING_SHOW" => "Y",
)
);?>
Обратите внимание, что мы задали фиксированными для всех товаров только 3 величины: Состояние, Наличие и Способы оплаты. Компонент элемента каталога не оперирует ими.
Так же мы не задали значение рейтинга (надо было бы знать не только значение, но и число голосов), поскольку на момент написания статьи этот функционал в решении работал некорректно, а обновление было в стадии "Бета версии".
Изображения (товаров)
Поскольку мы уже нашли шаблон детальной страницы товара (и даже скопировали его и внесли правку на этапе работы с товарами и ценами), то мы теперь уже знаем, что нам нужен /bitrix/templates/topshop_orange_flat/components/alexkova.emarket/catalog/template/bitrix/catalog.element/.default/template.php.
Внимательно изучив шаблон мы видим цикл в котором выводится массив фотографий:
<?if (is_array($firstPhoto)):?>
<a href="<?=$firstPhoto["SRC"]?>" rel="emarket-gallery">
<img id="i0" src="<?=$firstPhoto["SRC"]?>" alt="<?=$arResult["NAME"]?>" class="inslider zoom-img"
data-state="show" data-large="<?=$firstPhoto["SRC"]?>" data-text-bottom="<?=$arResult["NAME"]?>">
</a>
<?else:?>
<img src="<?=$arResult["DEFAULT_PICTURE"]["SRC"]?>" alt=" ">
<?endif;?>
<?if (count($arResult["MORE_PHOTO"]) > 0):?>
<?foreach($arResult["MORE_PHOTO"] as $cell=>$photo):?>
<?if ($cell == 0) {
continue;
}?>
<a href="<?=$photo["SRC"]?>" rel="emarket-gallery"><img id="i<?=$cell?>" src="<?=$photo["SRC"]?>" class="inslider inslider-hide zoom-img" alt="<?=$arResult["NAME"]."_".$cell?>"
data-state="hide" data-large="<?=$photo["SRC"]?>" data-text-bottom="<?=$arResult["NAME"]."_".$cell?>" ></a>
<?endforeach;?>
<?endif;?>
Суть этого кода сводится к проверке есть ли дополнительные фото. Если их нет, то выводится только 1 карточка детального фото. А если есть, то ещё набор плиток ниже, позволяющий просмотреть остальные фотографии товара.
Воспользуемся спецификацией компонента Изображение для задания максимально детальной микроразметки.
Добавим в шаблон (в 2 местах для 1 фото и для остальных) вызов компонента coffeediz:schema.org.ImageObject с минимальным набором параметров:
<?$APPLICATION->IncludeComponent(
"coffeediz:schema.org.ImageObject",
"",
Array(
"COMPONENT_TEMPLATE" => ".default",
"SHOW" => "Y",
"CONTENTURL" => $firstPhoto["SRC"],
"NAME" => $arResult["NAME"],
"CAPTION" => $arResult["NAME"],
"DESCRIPTION" => $arResult['DETAIL_TEXT'],
"HEIGHT" => "",
"WIDTH" => "",
"TRUMBNAIL_CONTENTURL" => "",
"ITEMPROP" => "",
"REPRESENTATIVEOFPAGE" => "True",
"PARAM_RATING_SHOW" => "N"
)
);?>
Свернуть/Развернуть ИТОГОВЫЙ код шаблона компонента (с микроразметкой изображений и товаров)
<?if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();
/** @var array $arParams */
/** @var array $arResult */
/** @global CMain $APPLICATION */
/** @global CUser $USER */
/** @global CDatabase $DB */
/** @var CBitrixComponentTemplate $this */
/** @var string $templateName */
/** @var string $templateFile */
/** @var string $templateFolder */
/** @var string $componentPath */
/** @var CBitrixComponent $component */
$this->setFrameMode(true);
//unset($arResult["MORE_PHOTO"]);
global $moreSettings;
//echo "<pre>34"; print_r($arParams); echo "</pre>";
//echo "<pre>34"; print_r($arResult); echo "</pre>";
//echo "<pre>34"; print_r($arResult["OFFERS"]); echo "</pre>";
$templateData = array(
'TEMPLATE_THEME' => $this->GetFolder().'/themes/'.$arParams['TEMPLATE_THEME'].'/style.css',
'TEMPLATE_CLASS' => 'bx_'.$arParams['TEMPLATE_THEME']
);
$strMainID = $this->GetEditAreaId($arResult['ID']);
$arItemIDs = array(
'ID' => $strMainID,
'PICT' => $strMainID.'_pict',
'DISCOUNT_PICT_ID' => $strMainID.'_dsc_pict',
'STICKER_ID' => $strMainID.'_sticker',
'BIG_SLIDER_ID' => $strMainID.'_big_slider',
'BIG_IMG_CONT_ID' => $strMainID.'_bigimg_cont',
'SLIDER_CONT_ID' => $strMainID.'_slider_cont',
'SLIDER_LIST' => $strMainID.'_slider_list',
'SLIDER_LEFT' => $strMainID.'_slider_left',
'SLIDER_RIGHT' => $strMainID.'_slider_right',
'OLD_PRICE' => $strMainID.'_old_price',
'PRICE' => $strMainID.'_price',
'DISCOUNT_PRICE' => $strMainID.'_price_discount',
'SLIDER_CONT_OF_ID' => $strMainID.'_slider_cont_',
'SLIDER_LIST_OF_ID' => $strMainID.'_slider_list_',
'SLIDER_LEFT_OF_ID' => $strMainID.'_slider_left_',
'SLIDER_RIGHT_OF_ID' => $strMainID.'_slider_right_',
'QUANTITY' => $strMainID.'_quantity',
'QUANTITY_DOWN' => $strMainID.'_quant_down',
'QUANTITY_UP' => $strMainID.'_quant_up',
'QUANTITY_MEASURE' => $strMainID.'_quant_measure',
'QUANTITY_LIMIT' => $strMainID.'_quant_limit',
'BUY_LINK' => $strMainID.'_buy_link',
'ADD_BASKET_LINK' => $strMainID.'_add_basket_link',
'COMPARE_LINK' => $strMainID.'_compare_link',
'PROP' => $strMainID.'_prop_',
'PROP_DIV' => $strMainID.'_skudiv',
'DISPLAY_PROP_DIV' => $strMainID.'_sku_prop',
'OFFER_GROUP' => $strMainID.'_set_group_',
'BASKET_PROP_DIV' => $strMainID.'_basket_prop',
);
$strObName = 'ob'.preg_replace("/[^a-zA-Z0-9_]/", "x", $strMainID);
$templateData['JS_OBJ'] = $strObName;
$strTitle = (
isset($arResult["IPROPERTY_VALUES"]["ELEMENT_DETAIL_PICTURE_FILE_TITLE"]) && '' != $arResult["IPROPERTY_VALUES"]["ELEMENT_DETAIL_PICTURE_FILE_TITLE"]
? $arResult["IPROPERTY_VALUES"]["ELEMENT_DETAIL_PICTURE_FILE_TITLE"]
: $arResult['NAME']
);
$strAlt = (
isset($arResult["IPROPERTY_VALUES"]["ELEMENT_DETAIL_PICTURE_FILE_ALT"]) && '' != $arResult["IPROPERTY_VALUES"]["ELEMENT_DETAIL_PICTURE_FILE_ALT"]
? $arResult["IPROPERTY_VALUES"]["ELEMENT_DETAIL_PICTURE_FILE_ALT"]
: $arResult['NAME']
);
$useVoteRating = ('Y' == $arParams['USE_VOTE_RATING']);
$boolDiscountShow = (0 < $arResult['MIN_PRICE']['DISCOUNT_DIFF']);
?>
<div class="container" id="<? echo $arItemIDs['ID']; ?>">
<div class="emarket-cart-left-column xs-span_5_of_5">
<ul class="emarket-top-link xs-hide sm-hide">
<?if (count($arResult["OFFERS"])>0):?>
<li><a href="#detail" data-scroll="emarket-offers"><?=GetMessage('CATALOG_OFFERS')?></a></li>
<?endif;?>
<li><a href="#detail" data-scroll="emarket-description"><?=GetMessage('CATALOG_DESCRIPTION')?></a></li>
<?if (count($arResult["DISPLAY_PROPERTIES"])>0):?>
<li><a href="#detail" data-scroll="emarket-props"><?=GetMessage('CATALOG_PROPS')?></a></li>
<?endif;?>
<?if ('Y' == $arParams['USE_COMMENTS']):?>
<li><a href="#detail" data-scroll="emarket-comments"><?=GetMessage('CATALOG_COMMENTS')?></a></li>
<?endif?>
<?if ($moreSettings["USE_STORE_FLAG"]):?>
<li><a href="#detail" data-scroll="emarket-stores"><?=GetMessage('CT_BCE_CATALOG_BTN_MESSAGE_USE_STORE')?></a></li>
<?endif;?>
<?if (isset($arResult["PROPERTIES"]["ACCESSORIES"]) && !empty($arResult["PROPERTIES"]["ACCESSORIES"]["VALUE"])>0):?>
<li><a href="#accessories" data-scroll="accessories"><?=GetMessage('ACCESSORIES_TITLE')?></a></li>
<?endif;?>
</ul>
<div class="clear"></div>
<?
$firstPhoto = false;
if (is_array($arResult["DETAIL_PICTURE"])){
$firstPhoto = $arResult["DETAIL_PICTURE"];
}
elseif(count($arResult["MORE_PHOTO"]) > 0){
$firstPhoto = $arResult["MORE_PHOTO"][0];
}
?>
<div class="emarket_slider">
<div id="emarket_big_photo">
<?if (is_array($firstPhoto)):?>
<a href="<?=$firstPhoto["SRC"]?>" rel="emarket-gallery">
<img id="i0" src="<?=$firstPhoto["SRC"]?>" alt="<?=$arResult["NAME"]?>" class="inslider zoom-img"
data-state="show" data-large="<?=$firstPhoto["SRC"]?>" data-text-bottom="<?=$arResult["NAME"]?>">
</a>
<?$APPLICATION->IncludeComponent(
"coffeediz:schema.org.ImageObject",
"",
Array(
"COMPONENT_TEMPLATE" => ".default",
"SHOW" => "Y",
"CONTENTURL" => $firstPhoto["SRC"],
"NAME" => $arResult["NAME"],
"CAPTION" => $arResult["NAME"],
"DESCRIPTION" => $arResult['DETAIL_TEXT'],
"HEIGHT" => "",
"WIDTH" => "",
"TRUMBNAIL_CONTENTURL" => "",
"ITEMPROP" => "",
"REPRESENTATIVEOFPAGE" => "True",
"PARAM_RATING_SHOW" => "N"
)
);?>
<?else:?>
<img src="<?=$arResult["DEFAULT_PICTURE"]["SRC"]?>" alt=" ">
<?endif;?>
<?if (count($arResult["MORE_PHOTO"]) > 0):?>
<?foreach($arResult["MORE_PHOTO"] as $cell=>$photo):?>
<?if ($cell == 0) {
continue;
}?>
<a href="<?=$photo["SRC"]?>" rel="emarket-gallery"><img id="i<?=$cell?>" src="<?=$photo["SRC"]?>" class="inslider inslider-hide zoom-img" alt="<?=$arResult["NAME"]."_".$cell?>"
data-state="hide" data-large="<?=$photo["SRC"]?>" data-text-bottom="<?=$arResult["NAME"]."_".$cell?>" ></a>
<?$APPLICATION->IncludeComponent(
"coffeediz:schema.org.ImageObject",
"",
Array(
"COMPONENT_TEMPLATE" => ".default",
"SHOW" => "Y",
"CONTENTURL" => $photo["SRC"],
"NAME" => $arResult["NAME"]."_".$cell,
"CAPTION" => $arResult["NAME"]."_".$cell,
"DESCRIPTION" => $arResult['DETAIL_TEXT'],
"HEIGHT" => "",
"WIDTH" => "",
"TRUMBNAIL_CONTENTURL" => "",
"ITEMPROP" => "",
"REPRESENTATIVEOFPAGE" => "True",
"PARAM_RATING_SHOW" => "N"
)
);?>
<?endforeach;?>
<?endif;?>
<?if (count($arResult["MORE_PHOTO"]) > 0):?>
<input type="button" class='list-slide-button-prev' id="c_bigphotos_prev" data-fix="c_photos" value=" ">
<input type="button" class='list-slide-button-prev' id="c_bigphotos_zoom" data-fix="c_photos" value=" ">
<input type="button" class='list-slide-button-next' id="c_bigphotos_next" data-fix="c_photos" value=" ">
<?endif;?>
</div>
<?if (count($arResult["MORE_PHOTO"]) > 1):?>
<div id="emarket-photo-slider" style="position: relative" class="sm-hide xs-hide">
<div class="sale-carousel" id="c_photos">
<ul class="sale-carousel-row">
<?foreach($arResult["MORE_PHOTO"] as $cell=>$photo):?>
<li class="photo <?=$cell == 0 ? 'active' : ''?>" id="rec_tab1" data-slide="<?=$cell?>" data-type="group" data-state="load">
<div class="photo-wrap">
<img src="<?=$photo["SRC"]?>" alt="<?=$arResult["NAME"]."_".$cell?>" data-item="i<?=$cell?>" <?if ($photo["WIDTH"] > $photo["HEIGHT"]) {?>width="100%"<?} else {?>height="100%"<?}?>>
</div>
</li>
<?endforeach;?>
</ul>
</div>
<input type="button" class='list-slide-button-prev' id="c_photos_prev" data-fix="c_photos" value=" ">
<input type="button" class='list-slide-button-next' id="c_photos_next" data-fix="c_photos" value=" ">
</div>
<?endif;?>
<div class="emarket-big-label-area">
<?if ($boolDiscountShow):?>
<div class="emarket-label emarket-label-sale"></div>
<?endif;?>
<?if ($arResult["PROPERTIES"]["SPECIALOFFER"]["VALUE_ENUM_ID"]>0):?>
<div class="emarket-label emarket-label-soffer"></div>
<?endif;?>
<?if ($arResult["PROPERTIES"]["NEWPRODUCT"]["VALUE_ENUM_ID"]>0):?>
<div class="emarket-label emarket-label-new"></div>
<?endif;?>
<?if ($arResult["PROPERTIES"]["SALELEADER"]["VALUE_ENUM_ID"]>0):?>
<div class="emarket-label emarket-label-hit"></div>
<?endif;?>
<?if ($arResult["PROPERTIES"]["RECOMMENDED"]["VALUE_ENUM_ID"]>0):?>
<div class="emarket-label emarket-label-rec"></div>
<?endif;?>
</div>
</div>
</div>
<div class="emarket-cart-right-column xs-span_5_of_5">
<?
if ($useVoteRating)
{
?><?$APPLICATION->IncludeComponent(
"bitrix:iblock.vote",
"stars",
array(
"IBLOCK_TYPE" => $arParams['IBLOCK_TYPE'],
"IBLOCK_ID" => $arParams['IBLOCK_ID'],
"ELEMENT_ID" => $arResult['ID'],
"ELEMENT_CODE" => "",
"MAX_VOTE" => "5",
"VOTE_NAMES" => array("1", "2", "3", "4", "5"),
"SET_STATUS_404" => "N",
"DISPLAY_AS_RATING" => $arParams['VOTE_DISPLAY_AS_RATING'],
"CACHE_TYPE" => $arParams['CACHE_TYPE'],
"CACHE_TIME" => $arParams['CACHE_TIME']
),
false,
array("HIDE_ICONS" => "Y")
);?><?
}
if (isset($arResult['OFFERS']) && !empty($arResult['OFFERS']))
{
$canBuy = $arResult['OFFERS'][$arResult['OFFERS_SELECTED']]['CAN_BUY'];
}
else
{
$canBuy = $arResult['CAN_BUY'];
}
if ($canBuy && $arResult["CATALOG_QUANTITY"]>0){
echo '<div class="emarket-available">'.GetMessage('CT_BCE_CATALOG_AVAILABLE').'</div>';
}
else if(!$canBuy && false){
if (strlen($arParams['MESS_NOT_AVAILABLE'])>0)
echo GetMessage($arParams['MESS_NOT_AVAILABLE']);
}
?>
<div class="clear"></div>
<div class="sale-panel span-xs_5_of_5">
<div class="sale-panel-container">
<?$frame = $this->createFrame()->begin();?>
<?if ((((is_array($arResult["PRICE_MATRIX"]) && !empty($arResult["PRICE_MATRIX"]))) || count($arResult["PRICES"])>0) && count($arResult["OFFERS"])<=0){?>
<?if (is_array($arResult["PRICE_MATRIX"]) && !empty($arResult["PRICE_MATRIX"])){?>
<?/*?>
<div><?=GetMessage('PRICE_MATRIX_TITLE')?></div>
<table>
<?foreach($arResult["PRICE_MATRIX"]["ROWS"] as $cell=>$val):?>
<tr>
<td><?=$val["QUANTITY_FROM"]?>-<?=$val["QUANTITY_TO"]?></td>
<?foreach($arResult["PRICE_MATRIX"]["COLS"] as $cell2=>$val2):?>
<td><?=SaleFormatCurrency($arResult["MATRIX"][$cell2][$cell]["DISCOUNT_PRICE"],$arResult["MATRIX"][$cell2][$cell]["RUB"])?></td>
<?endforeach;?>
</tr>
<?endforeach;?>
</table>
<?*/?>
<div class="emarket-item-price">
<?
reset($arResult['PRICE_MATRIX']['MATRIX']);
$firstPrice = current($arResult['PRICE_MATRIX']['MATRIX']);
if (count($arResult["PRICE_MATRIX"]["ROWS"]) == 1) {
$currency = ($firstPrice[0]["CURRENCY"] == "RUB") ? GetMessage("PRICE_RUB") : $firstPrice[0]["CURRENCY"];?>
<div class="emarket-current-price emarket-format-price">
<?=$firstPrice[0]["DISCOUNT_PRICE"]." ".$currency?>
</div>
<?if ('Y' == $arParams['SHOW_OLD_PRICE'] && $firstPrice[0]["DISCOUNT_PRICE"] < $firstPrice[0]["PRICE"]):?>
<div class="sale-cart-old-price emarket-format-price">
<?=$firstPrice[0]["PRICE"]." ".$currency?>
</div>
<?endif;?>
<?} else {
foreach ($arResult["PRICE_MATRIX"]["ROWS"] as $cell => $quantity) {
$currency = ($firstPrice[$cell]["CURRENCY"] == "RUB") ? GetMessage("PRICE_RUB") : $firstPrice[$cell]["CURRENCY"];
$showOldPrice = ('Y' == $arParams['SHOW_OLD_PRICE'] && $firstPrice[$cell]["DISCOUNT_PRICE"] < $firstPrice[$cell]["PRICE"]) ? true : false;
$colClass = ($showOldPrice) ? "left-third-col" : "left-second-col";?>
<div class="sale-cart-quantity-price <?=$colClass?>">
<?if ($quantity["QUANTITY_TO"] == 0) {?>
<?="�� ".$quantity["QUANTITY_FROM"]." ".GetMessage("PRICE_QTE")?>
<?} else {?>
<?=$quantity["QUANTITY_FROM"]." - ".$quantity["QUANTITY_TO"]." ".GetMessage("PRICE_QTE")?>
<?}?>
</div>
<div class="sale-cart-price emarket-format-price <?=$colClass?>">
<?=$firstPrice[$cell]["DISCOUNT_PRICE"]." ".$currency?>
</div>
<?if ($showOldPrice):?>
<div class="sale-cart-old-price emarket-format-price left-third-col">
<?=$firstPrice[$cell]["PRICE"]." ".$currency?>
</div>
<?endif;?>
<div class="clear"></div>
<?}?>
<?}?>
</div>
<?}else{?>
<?foreach($arResult["PRICES"] as $priceCode => $arPrice):?>
<?if (in_array($arResult["CAT_PRICES"][$priceCode]["ID"], $arResult["PRICES_ALLOW"])):?>
<div class="emarket-item-price">
<div class="price-name"><?=$arResult["CAT_PRICES"][$priceCode]["TITLE"]?></div>
<div class="emarket-old-price" id="<? echo $arItemIDs['OLD_PRICE']; ?>" style="display: <? echo ($boolDiscountShow ? '' : 'none'); ?>"><? echo ($boolDiscountShow ? $arPrice['PRINT_VALUE'] : ''); ?></div>
<div class="emarket-current-price emarket-format-price" id="<? echo $arItemIDs['PRICE']; ?>"><? echo $arPrice['PRINT_DISCOUNT_VALUE']; ?></div>
<div class="clear"></div>
</div>
<?endif;?>
<?endforeach;?>
<?}?>
<?}else{?>
<div class="emarket-item-price">
<div class="emarket-old-price" id="<? echo $arItemIDs['OLD_PRICE']; ?>" style="display: <? echo ($boolDiscountShow ? '' : 'none'); ?>"><? echo ($boolDiscountShow ? $arResult['MIN_PRICE']['PRINT_VALUE'] : ''); ?></div>
<div class="emarket-current-price emarket-format-price" id="<? echo $arItemIDs['PRICE']; ?>"><? echo $arResult['MIN_PRICE']['PRINT_DISCOUNT_VALUE']; ?></div>
<?if (count($arResult["OFFERS"]) == 1) {
$curOffer = current($arResult["OFFERS"]);
?>
<form class="basket_action">
<?if ('Y' == $arParams['USE_PRODUCT_QUANTITY']):?>
<input type="button" id="quantity-button-minus-<?=$curOffer["ID"]?>" class="quantity-button-minus sm-hide xs-hide" value="-">
<input type="text" value="1" class="quantity-text quantity-<?=$curOffer["ID"]?> sm-hide xs-hide" name="quantity" id="quantity-<?=$curOffer["ID"]?>">
<input type="hidden" value="<?=$curOffer["ID"]?>" name="item">
<input type="hidden" value="ADDTOCART" name="action">
<input type="button" id="quantity-button-plus-<?=$curOffer["ID"]?>" class="quantity-button-plus sm-hide xs-hide" value="+">
<?endif;?>
<input type="submit" class="color-icon-button color-icon-button-basket" value="<?=GetMessage('TO_BASKET')?>" name="ADDTOCART">
<input type="button" class="color-button color-button-blue small one-click-button sm-hide xs-hide" value="<?=GetMessage('ONE_CLICK_SALE')?>" name="ONECLICK" data-trade="<?=$arResult["ID"]?>" data-offer="<?=$curOffer["ID"]?>">
</form>
<?}?>
<div class="clear"></div>
</div>
<?}?>
<div class="emarket-sale-buttons">
<?if (count($arResult["OFFERS"])<=0){?>
<form class="basket_action" id="cart_form_<?=$arResult["ID"]?>"">
<div class="basket-button-area" data-basketitem="<?=$arResult["ID"]?>">
<?if ('Y' == $arParams['USE_PRODUCT_QUANTITY']):?>
<input type="button" id="quantity-button-minus-<?=$arResult["ID"]?>" class="quantity-button-minus" value="-">
<input type="text" value="1" class="quantity-text" name="quantity" id="quantity-<?=$arResult["ID"]?>">
<input type="button" id="quantity-button-plus-<?=$arResult["ID"]?>" class="quantity-button-plus" value="+">
<?endif;?>
<input type="hidden" value="<?=$arResult["ID"]?>" name="item">
<input type="hidden" value="ADDTOCART" name="action">
<?
global $eMarketBasketData;
$buttonMSG = GetMessage('TO_BASKET');
$basketMSG = "";
$basketMSGTitle = "";
if (in_array($arResult["ID"], $eMarketBasketData["ITEMS"])){
$compareMSG = " active";
$buttonMSG = GetMessage('IN_BASKET_BUTTON');
$basketMSGTitle = GetMessage('IN_BASKET');
$basketMSG = " active";
}
?>
<input type="submit" class="color-icon-button color-icon-button-basket" value="<?=$buttonMSG?>" name="basketadd">
<div class="basket-button-state<?=$basketMSG?>"><?=$basketMSGTitle?></div>
</div>
<input type="button" class="color-button color-button-blue small one-click-button" value="<?=GetMessage('ONE_CLICK_SALE')?>" name="ONECLICK" data-trade="<?=$arResult["ID"]?>">
</form>
<?}else{?>
<a href="#detail" data-scroll="emarket-offers" class="color-button color-button-blue small scroll-navigate"><?=GetMessage('CATALOG_OFFERS')?></a>
<?}?>
<?if (count($arResult["OFFERS"])<=0):?>
<?
$addClass="";
if (in_array($arResult["ID"], $eMarketBasketData["DELAY"])) $addClass=" active";
?>
<a href="#" data-basketitem="<?=$arResult["ID"]?>" class="icon-transparent icon-delay-transparent<?=$addClass?>"><?=GetMessage('CT_BCE_CATALOG_DELAY')?></a>
<?endif;?>
<?
$compareType = (isset($_SESSION["CATALOG_COMPARE_LIST"][$arResult["IBLOCK_ID"]]["ITEMS"][$arResult["ID"]])) ? "show" : "set";
$compareMSG = (isset($_SESSION["CATALOG_COMPARE_LIST"][$arResult["IBLOCK_ID"]]["ITEMS"][$arResult["ID"]])) ? GetMessage('CT_BCE_CATALOG_COMPARE_LIST') : GetMessage('CT_BCE_CATALOG_COMPARE')
?>
<a href="javascript:void(0)" data-compare="<?=$arResult["ID"]?>" class="compare-button icon-transparent icon-compare-transparent sm-hide xs-hide" data-scroll="body-counter" data-comparestate="<?=$compareType?>"><?=$compareMSG?></a>
<div class="clear"></div>
</div>
</div>
<?$frame->beginStub()?>
...
<?$frame->end()?>
<div class="emarket-sonet-share sm-hide xs-hide">
<? $GLOBALS["APPLICATION"]->IncludeComponent("bitrix:main.share", "emarket-sonet-share", Array(
"HIDE" => "N",
"HANDLERS" => array(
0 => "lj",
1 => "twitter",
2 => "vk",
3 => "facebook",
4 => "mailru",
),
"PAGE_URL" => $arResult["DETAIL_PAGE_URL"],
"PAGE_TITLE" => $arResult["NAME"],
"SHORTEN_URL_LOGIN" => "",
"SHORTEN_URL_KEY" => "",
),
false
);?>
</div>
</div>
<div class="emarket-detail-info sm-hide xs-hide">
<input type="button" data-scrollable="1" data-scroll="emarket-extented" class="color-button" value="<?=GetMessage('EMARKET_DETAIL_INFO')?>">
</div>
</div>
<div class="clear"></div>
</div>
<script>
var createSlider = false;
<?if (count($arResult["MORE_PHOTO"])>5):?>
createSlider = true;
var countPhoto = <?=count($arResult["MORE_PHOTO"])-1?>;
<?endif;?>
<?if ($arParams["ZOOM_ON"] == "Y") {?>
jQuery(function(){
$(".zoom-img").imagezoomsl({
zoomrange: [2.12, 12],
magnifiersize: [300, 300],
scrollspeedanimate: 10,
loopspeedanimate: 5,
cursorshadeborder: "1px solid black",
magnifiereffectanimate: "slideIn",
magnifierborder: "1px solid #eee",
zindex: 1000,
});
$(document).on("click", ".tracker", function() {
$('.zoom-img:visible').closest('a').trigger("click");
})
});
<?}?>
</script>
<?$APPLICATION->IncludeComponent(
"coffeediz:schema.org.Product",
"",
Array(
"SHOW" => "Y",
"NAME" => $arResult['NAME'],
"DESCRIPTION" => $arResult['DETAIL_TEXT'],
"PRICE" => $arResult['CATALOG_PRICE_1'],
"PRICECURRENCY" => $arResult['CATALOG_CURRENCY_1'],
"ITEMAVAILABILITY" => "InStock",
"ITEMCONDITION" => "NewCondition",
"PAYMENTMETHOD" => array("VISA", "MasterCard", "Cash"),
"PARAM_RATING_SHOW" => "Y",
)
);?>
Новости и Статьи
Решение TopShop содержит 2 раздела с текстовым контентом:
- /company/news/
- /company/press/
Оба раздела используют один и тот же комплексный компонент bitrix:news с одним и тем же шаблоном emarket_news (для вывода детальной страницы используется bitrix:news.detail с дефолтным компонентом в составе комлексного).
Копируем шаблон комплексного компонента в наш шаблон сайта "topshop_orange_flat", переименовывая в "emarket_news_schema" на одной из страниц (например на новостной).
Применяем этот же шаблон для компонента на второй странице (чтобы разметить сразу оба раздела).
Переходим к редактированию шаблона детальной страницы новости в комплексном шаблоне компонента:
/bitrix/templates/topshop_orange_flat/components/bitrix/news/emarket_news_schema/bitrix/news.detail/.default/template.php
Воспользуемся спецификацией компонента Статья для максимально детальной микроразметки.
Добавляем туда вызов компонента Статья:
<?$APPLICATION->IncludeComponent(
"coffeediz:schema.org.Article",
"",
Array(
"COMPONENT_TEMPLATE" => ".default",
"SHOW" => "Y",
"TYPE" => "",
"LEARNING_RESOURCE_TYPE" => "",
"NAME" => $arResult["NAME"],
"ARTICLEBODY" => $arResult["DETAIL_TEXT"],
"ABOUT" => $arResult["PREVIEW_TEXT"],
"GENRE" => "",
"ARTICLE_SECTION" => array($arResult["SECTION"]["NAME"], ""),
"KEYWORDS" => array(""),
"IN_LANGUAGE" => "ru",
"DATA_PUBLISHED" => $arResult["DISPLAY_ACTIVE_FROM"],
"AUTHOR_TYPE" => "",
"IMAGEURL" => "",
"PARAM_RATING_SHOW" => "N"
)
);?>
Так же отредактируем параметры компонента на обеих страницах. Нас интересует параметр "Формат показа даты" раздела "Настройки Детального Просмотра". Поскольку дата в публичной части у нас всё равно не выводится, мы приведём её к формату ГГГГ-ММ-ДД, который должен быть у этого поля согласно спецификации Schema.Org:
Обращаем внимание читателя, что семантически вернее было бы создать у комплексного компонента новый параметр "Тип публикации" со значениями Статья/Новость и передать этот тип из комплексного компонента новостей, в детальную страницу, а оттуда компоненту микроразметки.
Это позволило бы семантически вернее обозначить тип контента (однако в настоящий момент нет чёрткой информации о том какие преимущества бы это дало, поэтому мы воздержались от ещё одной правки чужого шаблона).
К сожалению, в решении TopShop не используется ни автор, ни рейтинг для статей/новостей, поэтому мы не использовали эту микроразметку (Хотя спецификация компонент и позволяет, так что небольшая дополнительная правка легко и просто обогатила бы ваш поисковый сниппет)
Итоги
Мы успешно добавили микроразметку Schema.Org на сайт, созданный на типовом решении TopShop, постратив на это менее часа.
В рамках задачи было добавлено 5 типов микроразметки:
- schema.org/Organization - Адрес организации
- schema.org/ImageObject - Изображение (для товаров)
- schema.org/Product - Продукт (с ценой для товаров)
- schema.org/Article - Статья (для Статей и Новостей)
- Data-Vocabulary.org/Breadcrumb - Хлебные крошки
До 4 типов теперь могут встречаться на 1 странице:
А это значительно увеличит шансы на интересный и привлекательный поисковый сниппет для сайта!
Задойный А.В.