[EVO] Ajax. Финальный правильный метод №4

Предлагаемые ранее методы черезжопные изначально.

Запуск сниппетов через index-ajax.php бред бредовый, т.к. это
а) не совсем безопасно, т.к. это без доп. проверок можно вызвать абсолютно любой сниппет в системе
б) Не удобно, т.к. вместо того, чтобы разруливать логикой на уровне контроллера (файла в который приходит запрос), приходится делать дополнительный сниппет на плечи которого перекладывается эта обязанность.

Использование jQuery.load это костлявый ajax, т.к. реально грузится вся страница целиком и уже только потом из этой страницы выбирается нужный HTML блок. При этом нет возможности получать ответ в JSON без дополнительных плагинов и модификаций сниппетов.

Разруливание роутингом на уровне плагина под событием OnPageNotFound — это конечно решение, но если вдруг кто-то создаст документ с алиасом указаным в switch, то весь ajax полетит к чертям, а программист будет искать ошибку пока не поседеет.

Создание документа в дереве ресурсов — хороший выход. Но опять таки, приходится плодить одноразовые сущности в виде сниппетов используемых только 1 раз только в конкретном ресурсе. Помимо этого необходимо еще и настраивать права доступа, чтобы спрятать системный ресурс от рядового менеджера.

Использование index.php как отправная точка — единственное верное решение. Тем более, там уже заложена возможность запуска в режиме API. Итак, создаем в корне сайта (рядом с index.php) какой-нибудь файлик (допустим ajax.php)

define('MODX_API_MODE', true);
include_once(dirname(__FILE__)."/index.php");
$modx->db->connect();
if (empty ($modx->config)) {
    $modx->getSettings();
}
$modx->invokeEvent("OnWebPageInit");

if(!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || (strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest')){
	$modx->sendRedirect($modx->config['site_url']);
}
$out = $modx->runSnippet('test');
$return = array('message' => $out);
echo json_encode($return);


Как итог, в созданном файле автоматически задаются все те же параметры как и во всем движке. Вызывается событие OnWebPageInit (на случай если на это событие подвешены какие-то плагины типа LoadElement или автозагрузчики классов типа Composer).
Более того, сниппеты занимаются своими делами, а внутри файла мы уже задаем непосредственно логику для запуска этих сниппетов и обработку данных без внедрения дополнительных сущностей (сниппетов).

Банальный пример. Необходимо вернуть или в HTML или в json. Для этого последние 3 строки файла перепишем так:
$out = $modx->runSnippet('test');
if(isset($_REQUEST['json'])){
   $out = json_encode(array('message' => $out));
}
echo $out;

32 комментария

avatar
Мне тоже этот метод нравится, в основном я так и работаю с AJAX, но бывает что и создаю документы для конкретного Action с вызовом некэшируемого сниппета обработчика и ставлю Content-type: application/json
avatar
Я тоже иногда документы создаю. Но тем не менее мне такой подход не очень нравится, т.к. приходится создавать одноразовые сниппеты, которые засоряют кеш и мозолят глаза. Я на эту тему вот тут еще мысли свои описал для полноты картины.
avatar
Полностью согласен с тобой)
avatar
if(isset($_REQUEST['json'])){
   $return = json_encode(array('message' => $out));
}
echo $out;


и что это нам даст? :)
avatar
$return = $out ))
Пофиксил…
avatar
Да я то понял, просто зайдет кто-нибудь, скопирует код — а потом сюда же с криком «у меня ваш самый правильный метод ну нифига не работает, json-а не возвращает» и вернется :)))
avatar
Женя логику показал а не 100% рабочий пример. Кому надо и кто потянет без заминок разберётся.
avatar
Я лишь указал на небольшую очепятку — см цитату :)
avatar
Для этого есть личка ;)
avatar
:)
avatar
У меня такой способ не прокатил. Парсер выдает ошибку в вызове runsnippet. А если делаю через сниппет в дереве то все ок.
avatar
При таком вызове не определены documentObject и documentIdentifier. А некоторые сниппеты к ним обращаются если не переданы дополнительные параметры.
avatar
Для полного счастья перед echo не хватает:
header('content-type: application/json');

Ваш метод можно стандартизировать, добавив вместо вызова сниппета проверку$_REQUEST['action'] (к примеру) и далее switch($action)… Но сам я приверженец документного AJAX в MODx. Скрыть папку API от клиента и роботов не трудно, а сниппет можно сделать один универсальный и вызывать его с нужными параметрами.
avatar
сниппет можно сделать один универсальный и вызывать его с нужными параметрами.
Цикл жизни ajax запроса в вашем случае:
— Инициализируется ядро MODX
— Получается documentObject страницы
— Получается сниппет
— Разбираются параметры
— Выполняется код
— Препарируется результат шаблонизатором MODX

Цикл жизни AJAX запроса в моем случае:
— Инициализируется ядро
— Выполняется код

Ну и наконец в вашем случае нужно еще проделать 1 совсем несложный этап:
Скрыть папку API от клиента и роботов
avatar
Тут приходится выбирать между удобством и максимальной производительностью. Кстати, в вашем примере также вызывается сниппет, а закрыть от роботов ajax.php не будет лишним.
avatar
Ваш подход хорош и логичен. Думаю, в него нужно добавить параметры action и mime и применять в качестве альтернативы index-ajax.php.
avatar
Кстати, в вашем примере также вызывается сниппет
Вызывается. Но его не нужно разбирать парсеру и определять параметры.
Да и вызов сниппета это лишь пример — можно и сразу код фигачить. В документах увы так нельзя.
avatar
В офф топ сказать, был один персонаж что выдумал тип документа позволяющий пыху писать прямо в контент — официальное сообщество не одобрило. Ну это я так, к слову.
Комментарий отредактирован 2014-05-04 16:45:48 пользователем vasenin26
avatar
Да хоть в txt файлах пыху выполняйте. Мне от этого не холодно, ни жарко
avatar
случайно нашел плагин:
github.com/ghettovoice/AsyncDocs-EVO
avatar
что-то не могу с ним разобраться
avatar
Вот этот работает,
github.com/Husband/AjaxifyEvo
но не изменяются мета-теги при переходе по страницам
Комментарий отредактирован 2014-09-03 19:44:25 пользователем nohc
avatar
Очень хочу во всем этом разобраться, но для наглядности не хватает примеров. Думаю, не только мне. Может кто-нибудь привести пример, как по этому методу сделать, допустим, пагинацию?
avatar
Создать файл, как написано, в нем вызывать DocLister. Обработчиком клика по ссылкам с номерами страниц посылать запрос с параметром page к файлу.
Но мне кажется, что этот способ хорошо подойдет для одностраничного сайта. А если нужно, чтобы работало и без ajax, то я бы выбрал метод с прерыванием парсера. Описано здесь modx.pro/development/3139-foundations-of-ajax/ — только я делал с помощью плагина.
avatar
Действительно не понятно, как получить доступ к содержимому этого файла, jquery load выдаёт ошибку, так же и с post. Догадался таки, пробую же на Revolution…
avatar
А как с Revo быть?
avatar
Как итог, в созданном файле автоматически задаются все те же параметры как и во всем движке.
Если так, то почему теги modx в вызове чанков и сниппетов не отображаются? Вместо них код: [+placeholder+], [!snippet!].
avatar
подскажите пожалуйста не могу разобраться, у меня все равно страницу перезагружает((
avatar
получаю ошибку 500
код сниппета
<code>define('MODX_API_MODE', true);
include_once(dirname(__FILE__)."/index.php");
$modx->db->connect();
if (empty ($modx->config)) {
    $modx->getSettings();
}
$modx->invokeEvent("OnWebPageInit");

if(!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || (strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest')){
        $modx->sendRedirect($modx->config['site_url']);
}
/*_____start_____*/

			$p['parents'] = $_POST['docid'];
			$p['id'] = $_POST['id'];
			$p['tpl'] = 'proj.tpl';
			$p['start'] = 1;
			$p['display'] = '10'; // all
			$p['depth'] = 1;
			$p['sortDir'] = 'ASC';
			$p['sortBy'] = 'pub_date';
			$p['dateSource'] = 'pub_date';
			$p['noResults'] = 'Каталог пуст.';
			$p['paginate'] = 1;
			$p['hideFolders'] = 1;
			$p['debug'] = 1;
			$p['tplPaginateNext'] = '@CODE:
										<div class="more-projekt">
											<a href="[+url+]" class="transition" id="ditto_next_link">еще 8 проектов</a>
											<div class="transition">
												<i class="fa fa-refresh"></i>
											</div>
										</div>';

			$out = $modx->runSnippet('Ditto', $p);

/*_____end_____*/

if(isset($_REQUEST['json'])){
   $out = json_encode(array('message' => $out));
}
echo $out;</code>
код страницы
<code>
  <span class="ajax[+alias+]">
	[[Ditto?parents=`[+id+]` &tpl=`proj.tpl` &display=`10` &depth=`1` &sortDir=`ASC` &dateSource=`pub_date`
	&sortBy=`id` &paginate=`1` &hideFolders=`1` &start=`0` &id=`p` &tplPaginateNext=`next.tpl`]]

	[+p_next+]
	</span>
<script>
$(function() {
  $('#ditto_next_link').on('click', function(event) {
  	event.preventDefault();

		$.ajax({
		    url:'[(site_url)]ajax.php',
		    type:'post',
		    data: 'id=p&docid=[+id+]',
				success: function(data){ //Функция, которая будет выполняться при успехе
					$('.ajax[+alias+]').html(data); // alert( data )
				}
		})

  });
});
</script></code>
Комментарий отредактирован 2016-10-05 15:31:14 пользователем doc555
avatar
Ну так смотри лог. 500 ошибка это php
avatar
в логах ничего; сам сниппет работает как надо…
avatar
500 — ошибка. Значит она есть. Либо смотрите, почему пустые логи, либо начните с пустого файла и echo '11'; и постепенно дописывая код, пока ошибка не вылезет.
Сегодня юзал этот код для аякса, всё работает, как часики.
Комментарий отредактирован 2016-10-06 13:50:19 пользователем 1px
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.