Интересные задачи верстки и клиентского программирования

Правильный AJAX в 1С-Битрикс

добавил Шубин Александр 20 Июль, 2012


В CMS 1С-Битрикс есть свой встроенный ajax в нескольких компонентах. В этом встроенном ajax есть один существенный минус — страница все равно полностью выполняется.

Кроме встроенного в стандартные компоненты варианта ajax, можно самому написать более правильную реализацию на битриксовской библиотеке. Про встроенную библиотеку можно прочитать у Рамиля Юналиева:
http://yunaliev.ru/2010/02/bitrix-ajax/

Здесь я покажу как мне кажется более правильный вариант ajax, именно организацию работы. Отправлять сами ajax запросы я буду на jquery. Больший акцент я сделаю именно на серверной стороне, но чтобы получился законченный пример клиентскую сторону мы тоже расссмотрим.

Хочется показать какой то живой пример, поэтому мы будем отображать на странице количество и общую сумму товаров в корзине.

Клиентская часть. Демонстрация

Для работы AJAX как вам наверное известно нужно две части — клиентская и серверная. На клиенте нужно написать javascript код который будет отправлять сам запрос без перезагрузки страницы. На сервере нужно поместить какой то html файл или скрипт, который будет давать ответы на клиентские запросы.

Начнем с клиентской стороны.

Как это все будет выглядет для пользователя:
http://verstaem.com/examples/ajax-1c-bitrix/

Отмечу, что свободной копии битрикса, которую я могу постоянно держать для тестов у меня нет. Поэтому ответ сервера при демонстрации работы эмулируется. На самом деле никакой код на серверной стороне в примере по ссылке не выполняется, в качестве ответа берется просто html файл. Но для пользователя все будет именно так, у него где то отображается корзина при просмотре сайта, и при нажатии на «обновить», данные его корзины обновляются.

Вот html код корзины:

<div class="basket">
	<h2>Корзина</h2>
	<span class="current">2 800 руб. — 1 шт.</span>
	<a href="javascript:void(0)">Обновить</a>
</div>

Вот скрипт на jquery который отправляет ajax запрос:

$(document).ready(function(){
	$(".basket a").click(updateBasket)
})

function updateBasket()
{
    $.ajax({
        url: "/ajax.handler.php",
        type: "POST",
        dataType: "html",
        data: "PAGE=BASKET",
        success: function(data){
		$('.basket .current').html(data)
        }
    });
	
}

То есть при нажатии на ссылку «Обновить» происходит отправка ajax запроса к ajax.handler.php из корня сайта. Полученный ответ вставляется в <span class=’current’></span>. Все довольно просто.

На самом деле это не AJAX, а AJAH, так как в ответе приходит уже готовый html, а не xml из которого еще нужно забрать данные. Но суть примерно одна.

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

Работа на серверной стороне

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

Создадим файл ajax.handler.php в корне сайта, на котором установлен 1С-Битрикс. В этом файле напишем

<?require_once($_SERVER['DOCUMENT_ROOT']. "/bitrix/modules/main/include/prolog_before.php");

Что делает эта строка? Подключает служебную часть пролога в битриксе. При этом шаблон сайта не подключается. С помощью этой строки мы получаем доступ к АПИ битрикса и при этом никакого лишнего html кода не выводится.

Впринципе можно написать код, который будет отвечать на ajax запросы прямо здесь. Можно не заморачиваться ни с какими компонентами и отдельными файлами. Но что будет если у нас будет несколько разных видов запросов? Например если один вид ajax запроса обновляет корзину, второй совершает заказ, третий удаляет из корзины один товар и т.д. В рамках одного файла работать будет не очень удобно.

Чтобы разграничить ответы на разные запросы, я добавлю такой код в файл ajax.handler.php:

<?require_once($_SERVER['DOCUMENT_ROOT']. "/bitrix/modules/main/include/prolog_before.php");

if($_REQUEST["PAGE"] == "BASKET"){
	require_once($_SERVER["DOCUMENT_ROOT"] . "/handlers/basket.php");
}

То есть, если в запросе переменная PAGE равна BASKET, то подключим файл basket.php, который находится в папке handlers. Таким образом, для разных ajax запросов можно будет просто добавлять новые условия в файле ajax.handler.php, и не думать про взаимодействие с другими запросами. Само собой нужно создать папку handlers в корне сайта и внутри нее файл basket.php.

В файле /handlers/basket.php уже можно собирать данные по корзине, оформлять это все в html и отдавать по запросу. Но если прямо тут писать код, то страница грязная получится, то есть программный код собирающий данные будет прямо рядом с кодом который эти данные отображает в виде html. Поэтому я буду делать стандартный компонент битрикса и просто вставлю его на эту страницу. В стандартном компоненте можно разделить логику и представление. Под каждый вид ajax запрос нужно будет создавать свои отдельные компоненты. Код вызова компонента на странице basket.php

<?
$APPLICATION->IncludeComponent("ajax:basket", ".default", array(), false);
?>

Если у компонента не много параметров, то можно прямо внутри условия в файле /ajax.handler.php его вызвать и не выносить вызов в отдельный файл.

<?require_once($_SERVER['DOCUMENT_ROOT']. "/bitrix/modules/main/include/prolog_before.php");

if($_REQUEST["PAGE"] == "BASKET"){
	$APPLICATION->IncludeComponent("ajax:basket", ".default", array(), false);
}

Теперь напишем сам компонент. Создадим папку /bitrix/components/ajax. Внутри создаем подпапку basket с самим компонентом. На том как правильно писать компонент я не буду останавливаться подробно, вот основные сведения которые нужно знать про компоненты:
http://dev.1c-bitrix.ru/api_help/main/general/component20/index.php

Приведу листинг файла component.php

<?
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();

CModule::IncludeModule("sale");

$arBasketItems = array();

$dbBasketItems = CSaleBasket::GetList(
        array(
            "NAME" => "ASC",
            "ID" => "ASC"
        ),
        array(
            "FUSER_ID" => CSaleBasket::GetBasketUserID(),
            "LID" => SITE_ID,
            "ORDER_ID" => "NULL",
            "CAN_BUY" => "Y",
        ),
        false,
        false,
        array("ID", "CALLBACK_FUNC", "MODULE", 
              "PRODUCT_ID", "QUANTITY", "DELAY", 
              "CAN_BUY", "PRICE", "WEIGHT")
    );

while ($arItem = $dbBasketItems->Fetch())
{
    if(strlen($arItem["CALLBACK_FUNC"]) > 0)
    {
        CSaleBasket::UpdatePrice(
            $arItem["ID"], 
            $arItem["CALLBACK_FUNC"], 
            $arItem["MODULE"], 
            $arItem["PRODUCT_ID"], 
            $arItem["QUANTITY"]
        );
        $arItem = CSaleBasket::GetByID($arItem["ID"]);
    }
    
    $arResult["TOTAL_PRICE"] = $arResult["TOTAL_PRICE"] + $arItem["PRICE"] * $arItem["QUANTITY"];
    $arResult["ITEMS"][$arItem["PRODUCT_ID"]] = $arItem;
}

$arResult["TOTAL_PRICE"] = SaleFormatCurrency($arResult["TOTAL_PRICE"], 'RUB');

$this->IncludeComponentTemplate();

Последняя строка подключает шаблон компонента. Вот код шаблона компонента:

<?if(count($arResult["ITEMS"]) > 0):?>
    <?=$arResult["TOTAL_PRICE"]?> — <?=count($arResult["ITEMS"])?> шт.
<?else:?>
    Корзина пуста
<?endif;?>

Вот такой небольшой шаблон.
Проверить работоспособность серверной части можно открыв в браузере «адрес сайта/ajax.handler.php?PAGE=BASKET». После открытия страницы должен появиться примерно такой текст на белом фоне «6 830 руб. — 2 шт.», то есть сумма и количество товаров или текст «Корзина пуста» если у вас нет ни одного товара в корзине.

Итого, какие преимущества дает вышеописанная организация AJAX:

  • Добавление новых запросов без труда
  • Все старые запросы обрабатываются независимо, поэтому их удобно редактировать
  • Во время ajax запроса выполняется только код, который необходим для ответа. Ничего лишнего не выполняется
  • Благодаря использованию стандартных компонентов получаем удобное разделение логики и представления

Если у вас есть какие то дополнения к описанной выше организации работы ajax или ajax запросы у вас обрабатываются как то по другому — пишите в комментариях.


добавил Шубин Александр 20 Июль, 2012
Рубрика: AJAX


11 комментариев

  • Владимир:

    Существенный и наверное самый жирный минус этого способа, клонирование компонента для аякс подгрузки. А если у нас на сайте будет 500 компонентов новостей в разных местах. То что нам 500 папок создать для компонента ? Поэтому лучше битриксовского способа еще никто не придумал. Мы обращаемся аяксон на ту же самую страницу со всеми настройками копонента и получаем новый контент такого же оформления.

    • А зачем папку компонента клонировать? Один компонент, одна функция.

      Просмотр новостей аяксом, вообще включать не стоит, иначе проблемы с индексацией практически наверняка будут. Но даже в примере с новостями можно обойтись и одним компонентом, просто в аяксовском запросе к этому компоненту передавать ID инфоблока, номер страницы и другие значимые данные. Сам аяксовский компонент просто будет получать данные из базы и отправлять их пользователю. Но это будет уже не AJAH тогда, то есть не HTML по запросу нужно будет возвращать, а чистые данные.

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

      Здесь я рассказывал больше о другом, статья не для визиток, а для каких то более функциональных сайтов, где аякс действительно нужен. Всякие переносы элементов, например изменить сортировку в списке с помощью перетаскивания мышкой(прямо в браузере), или добавление в корзину, да много есть задач, которые стандартными битриксовскими компонентами впринципе не решаются. Статья больше для них, а не для отображения новостей аяксом.

  • Денис:

    Добрый день.
    Сделал так, как Вы написали в статье.
    Однако при выполнении условного basket.php — нет доступа к переменной $USER почему-то. Доступа к ней не получить никак или нужно какой-то компонент подключать?

  • MD5:

    Привет, статья понравилась, просто и доступно. Побольше бы таких появлялось в рунете. Основной недостаток Битрикса как раз в том, что по нему написано мало хорошей информации.

    • Рад, что понравилось 🙂

      По битриксу есть хорошие и бесплатные курсы обучения, от самого битрикса:
      http://dev.1c-bitrix.ru/learning/
      «Разработчик битрикс фреймворк» в самом низу. Чтобы подтянуться до уровня среднего разработчика хватает, как мне кажется.

  • Евгений:

    Это костыль, а не правильный AJAX, причём этим способом можно всё сделать гораздо проще, тут всё сильно заморочено, а правильный AJAX это когда запрос идёт напрямую к компоненту…

    • Поведай же свою секретную технику отправки аякс запроса напрямую к компоненту 🙂 Если эта секретная техника положить файл ajax.php в папку с компонентом и в js писать путь до этого файла, то у меня для тебя плохие новости, это и есть костыль 🙂 Даже не буду расписывать почему. Но вдруг ты что-то новое изобрел, я прям в предвкушении весь 🙂

  • Алекс:

    «положить файл ajax.php в папку с компонентом и в js писать путь до этого файла, то у меня для тебя плохие новости, это и есть костыль»
    и от чего же это костыль? -__-
    Если AJAX запросы компонента, то они и должны идти к компоненту, а не в другое место.
    В чём тогда смысл компонентов, если их части будут разбросаны? -___-
    Мне нравится как в WordPress’е реализовано.

    • В чём тогда смысл компонентов, если их части будут разбросаны?

      Смысл компонентов в разделении логики и представления. Это их основная функция.

      Если AJAX запросы компонента, то они и должны идти к компоненту, а не в другое место.

      Файл ajax.php в папку с компонентом стоит добавлять только в том случае, если вы js код пишете тоже в компоненте.

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

  • GarrySeldon:

    Хорошая статья! Единственный минус, что нельзя использовать пользовательские настройки или их придется задавать жестко. Но это вина самого Битрикса, его извратной логики.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *