AJAX на MaxSite CMS
Использование Аякса всегда имеет некоторые сложности, потому что требуется сразу решить несколько задач: HTML + CSS, PHP и JS. Сегодня я расскажу как можно создать небольшое приложение на MaxSite CMS.
Назвал я его «Крестики-нолики» по аналогии с одноименной игрой. Правда я не стал реализовывать алгоритм поиска верных ходов и анализ выигрышних комбинаций. Эта часть довольно трудоёмкая, и при желании, вы можете её самостоятельно сделать.
Для начала, как обычно, нужно составить небольшое ТЗ.
- У нас будет приложение, которое мы оформим в виде шаблона MaxSite CMS. Пусть он называется «cross».
- Нужно оформить внешний вид в виде клеток 3x3.
- Нужно использовать анимацию для красоты.
- Нужно запретить ставить ходы на уже занятые клетки.
- Ответ сервера будет генерироваться случайным образом из пустых клеток.
- Ответ сервера нужно получать без перезагрузки страницы по AJAX.
Итак приступаем.
Делаем каталог «cross» и в нем располагаем файлы index.php и info.php. Поскольку у нас будет только одна страница, то в index.php мы подключим основной файл нашего приложения cross.php:
require('cross.php');Почему мы делаем подключение еще одного файла, а не сразу программируем в index.php? Всё просто: я стараюсь использовать «классический» вариант построения шаблона MaxSite CMS, где index.php выполняет роль диспетчера типов данных. То есть, если у вас возникнет потребность что-то добавить, то это легко сделать именно в index.php.
По этой же причине мы оформим наш HTML в виде файлов:
- main-start.php
- header.php
- main-end.php
- style.css
Описывать разметку нет смысла, потому что дизайн будет зависеть от ваших потребностей, просто остановлюсь на нескольких моментах.
Прежде всего еще раз напомню как происходит подключение файлов. В index.php мы подключаем cross.php. Этот файл в свою очередь выполняется так:
require(getinfo('template_dir') . 'main-start.php');
тут наш код
require(getinfo('template_dir') . 'main-end.php');
В main-start.php мы подключаем header.php. Таким образом наш шаблон разбит на секцию HEAD, начальную часть, выполняемую и конечную.
В header.php мы должны подключить jQuery, поскольку js-часть будет использовать именно эту библиотеку. Кроме этого мы вынесем свою часть js в отдельный файл (my.js).
<?= mso_load_jquery() ?>
<?= mso_load_jquery('ui/effects.core.packed.js') ?>
<?= mso_load_jquery('ui/effects.highlight.packed.js') ?>
<script type="text/javascript" src="<?= getinfo('stylesheet_url') ?>js/my.js"></script>
В первой строчке подключаем jQuery. Во второй и третьей подключаем эффекты. Последней - наш скрипт.
Теперь давайте перейдем к cross.php. Я решил не усложнять задачу, поэтому наше поле 3x3 оформил в виде обычной таблицы, где каждой ячейке прописал свой class. Поскольку код очень маленький, то я привожу полный листинг файла:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
# начальная часть шаблона
require(getinfo('template_dir') . 'main-start.php');
?>
<table>
<tr>
<td class="r1"></td>
<td class="r2"></td>
<td class="r3"></td>
</tr>
<tr>
<td class="r4"></td>
<td class="r5"></td>
<td class="r6"></td>
</tr>
<tr>
<td class="r7"></td>
<td class="r8"></td>
<td class="r9"></td>
</tr>
</table>
<?php
# конечная часть шаблона
require(getinfo('template_dir') . 'main-end.php');
?>
Оформление я вынес в style.css.
Уверен, что никаких сложностей с пониманием этого кода у вас не возникнет. В браузере это будет выглядеть так:
Теперь переходим к самому интересному: определим алгоритм работы. У нас есть уникальные ячейки (td.class) - по ним мы можем отследить какой был клик/ход. На сервер нам нужно передавать не только последний ход посетителя, но и всю таблицу. Сделать это можно получив js-свойство text() каждой ячейки и сформировав строчку вида: «r1 - - X - 0 - - - -». Где элементы разделены пробелом; если «-», значит клетка пустая; «X» - ход посетителя; «0» - ход сервера.
Эту строчку мы передаем на сервер, что-то с ней делаем и отдаем обратно в виде массива (в формате JSON). JS-скрипт обходит этот массив и устанавливает тексты ячеек таблицы в соответствии с массивом. В массиве от сервера мы также передадим и номер хода - он потребуется для анимации.
Теперь перейдем непосредственно к js. В файле my.js вначале подключим jQuery.
$(function(){
тут js-код
});Все дальнейшие действия мы будем проводить внутри этого блока.
Для начала определимся с анимацией. Я выбрал «вспышку», которая будет происходить когда курсор мыши находится над ячейкой таблицы:
$('td').mouseover(
function()
{
if ( $(this).text() )
$(this).effect("highlight", {color: "#FFE0E0"}, 500);
else
$(this).effect("highlight", {color: "#A0FFA0"}, 500);
});
Единственный момент - я указал условие «if ( $(this).text() )», которое проверяет есть ли текст внутри ячейки. Если есть, значит в ней ход ставить нельзя и мы подсвечиваем его светло-красным. Если же текста нет, то это пустая ячейка и она подсвечивается светло-зеленым.
Теперь нам нужно определить действия по клику.
$("td").click(function()
{
наши действия
};
Для начала мы должны запретить действия, если ход запрещен. Код вам уже знаком:
if ( $(this).text() )
{
$(this).effect("highlight", {color: "#FF0000"}, 200);
return;
}
Подсвечиваем красным и выходим из функции.
Теперь нам нужно создать строчку, которую мы отправим на сервер. Каждая ячейка может содержать три значения: пусто, X и 0. Я сделал вспомогательную функцию, которая получает какое-то значение текста, а на выходе отдает одно из трех вариантов (пусто заменяем на «-»).
function k(e)
{
if (e)
{
if ( e == "0") m = "0";
else m = "X";
}
else m = "-";
return m;
}
Составить строчку теперь не составлает труда:
var s = $(this).attr("class") + ' '
+ k($("td.r1").text()) + ' '
+ k($("td.r2").text()) + ' '
+ k($("td.r3").text()) + ' '
+ k($("td.r4").text()) + ' '
+ k($("td.r5").text()) + ' '
+ k($("td.r6").text()) + ' '
+ k($("td.r7").text()) + ' '
+ k($("td.r8").text()) + ' '
+ k($("td.r9").text());
Мы прогоняем через функцию k() каждую ячейку и получаем строчку, разделенную пробелами. Вот эта часть «$(this).attr("class")» возвращает класс нажатой ячейки (r1-r9). То есть на сервере по этому значению мы можем отследить последний ход.
Для того чтобы отправить строчку на сервер используется несколько функций. Я остановился на $.ajax(), которая считается универсальной.
$.ajax({
type: "POST",
url: path_ajax,
data: "cross=" + s,
dataType: "text",
success: function(msg)
{
ответ сервера в msg
}
});
Мы отправляем данные как POST. Данные (data) отправляются как поле «cross». Тип - текст.
Ответ сервера помещается в success в виде аргумента функции, в нашем случае это msg.
Теперь самое интересное. Обратите внимание на переменнную path_ajax. Это путь к «приёмному» php-файлу. Здесь я хочу остановиться поподробнее.
Путь к файлу нужно указывать полным в виде адреса. Казалось бы ничего сложного, однако есть маленький нюанс. Если мы напрямую обратимся к php-файлу, то мы не сможем получить доступ к MaxSite CMS! Очень часто нужна работа АЯКСА именно в рамках системы. Например при отправке данных со страницы какой-то записи. Очевидно, что одиночный php-скрипт просто не получит никаких данных об отправляемой странице. Чтобы обойти все эти ограничений в MaxSite CMS используется предопределенный тип данных «ajax» (http://сайт/ajax/). Во втором сегменте (уточнение ниже) нужно указать путь до файла относительно каталога «maxsite».
В нашем примере это будет так:
http://сайт/ajax/templates/cross/cross-post-ajax.php
Получив такой адрес, MaxSite CMS подключит файл
путь_на_сервере/application/maxsite/templates/cross/cross-post-ajax.php
Таким образом обращение к cross-post-ajax.php произойдет уже на уровне системы и мы преспокойно можем использовать все её функции, плагины и т.п.
Однако это еще не всё. Тип «ajax» должен иметь только один сегмент URL. Дело в том, что адреса могут быть самыми разными и иметь разную вложенность («/»). И чтобы не «сбивать» сегменты URL следует кодировать путь к файлу с помощью base64.
Таким образом путь определяется так:
$p = getinfo('ajax')
. base64_encode( 'templates/' . getinfo('template') . '/cross-post-ajax.php' );В итоге получится что-то вроде такого:
http://сайт/ajax/dGVtcGxhdGVzL2Nyb3NzL2Nyb3NzLXBvc3QucGhw
Система автоматически декодирует эту строчку и подключит нужный файл.
В последних версиях MaxSite CMS в целях безопасности имя ajax-файла должно иметь формат «имя-ajax.php». Только в этом случае произойдет его подключение.
Ну вот, когда мы определились с путями для Аякса, нам следует передать имя файла в наш js-скрипт. Очевидно, что php выполнять в js-файле не получится, поэтому мы с помощью php сгенерируем js-код, где в качестве переменной path_ajax укажем путь к файлу.
Перед подключением my.js в header.php допишем:
<script type="text/javascript">
var path_ajax = "<?= getinfo('ajax') . base64_encode( 'templates/' . getinfo('template') . '/cross-post-ajax.php' ) ?>";
</script>Как видите всё очень просто.
Что делать если нужно оправлять несколько Аякс-запросов к разным файлам? В принципе можно задать для них разные переменные, вроде path_ajax, path_ajax1, path_ajax2 и т.д. Но можно воспользорваться и одним. В этом случае в «приёмном» файле следует отслеживать входные данные (в $.ajax это data) и от этого подключать другие php-файлы.
Последняя часть работы с js - получив ответ сервера, изменить тексты ячеек. Сервер будет отдавать массив, где ключи совпадают с номером ячейки, а значения - с текстом. Таким образом мы просто обходим массив и расставляем тексты в ячейки.
...
success: function(msg)
{
var t = eval('(' + msg + ')');
for (var i = 1; i < 10; i++)
{
$("td.r" + i).text(t[i]);
};
$("td.r" + t[0]).effect("highlight", {color: "#40FF40"}, 1500);
$("td.r" + t[10]).effect("highlight", {color: "#FFA080"}, 1500);
}Первая строчка - преобразование JSON-данных в обычный js-массив. Дальше мы запускаем цикл for с 1 по 9 и выставляем тексты. Последние две строчки - анимация. В нулевом элементе у нас хранится последний ход посетителя, в 10-м - ход сервера.
С js покончено, переходим к php-части. Как вы уже поняли из описаний выше, наш «приёмный» файл это cross-post-ajax.php.
Работа в нем организуется аналогично обработке обычной формы, а результат нужно отдать по echo, предварительно кодировав его в формат JSON.
Дальше я привожу полный листинг с комментариями.
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
mso_checkreferer(); // защищаем реферер
if ( $post = mso_check_post(array('cross')) )
{
# разобьем строку в массив
$ar = explode(' ', $post['cross']);
# $ar[0] - это только что выбранная ячейка
# $ar[1]...$ar[9] - клетки
# если «-» - пустая
# если «X» - ход посетителя
# если «0» - ход сайта
# заменим - на ''
foreach ($ar as $key => $val) if ($ar[$key] == '-') $ar[$key] = '';
# добавим X в новый ход
# сразу преобразуем в число - это номер ячейки
$ar[0] = substr($ar[0], 1, 1);
$ar[ $ar[0] ] = 'X';
# после выбираем свой ход случайно из пустых
# сделаем массив от 1 до 9
$my_hod = array_fill(1, 9, '0');
while($my_hod)
{
# случайный элемент
$n = array_rand($my_hod);
# удалим его, чтобы повторно не обращаться
unset($my_hod[$n]);
# пустая клетка?
if ($ar[$n] === '')
{
$ar[$n] = '0'; // да, ставим свой ход
break; // выходим из цикла
}
}
# наш ход в $ar[10]
$ar[] = $n;
# отправляем данные
echo json_encode($ar);
}
?>
Скачать «Крестики-нолики» 826 (ок. 23КБ.)

Тест.
Спасибо, все работает, но:
при ошибке возвращает 200 ОК со всеми вытекающими последствиями, если вместо die
{header("HTTP/1.0 404 Not Found"); die();}и добавить еще строчку чтобы если файл не существует, также возвращало ошибку.Работаю с ajax все отправка данных работает отлично и проверка в php тоже.
Но есть одно но, пытаюсь сделать ответ из базы данных обратно в ajax и тут не чего не выходит. Что можно здесь сделать?