PHP Урок 20. Создаем AJAX (JavaScript) API
PHP Урок 20. Создаем AJAX (JavaScript) API для нашего PHP-приложения
Предыдущие уроки:
Программируем на PHP - Введение
PHP - Запросы от браузера к серверу
PHP - Как работает сервер
PHP - Урок 4. PHP - интерпретатор
PHP - Урок 5. Переменные сервера и глобальные переменные
PHP - Урок 6. Конструкции print и echo. Кавычки одинарные и двойные и конкатенация строк
PHP - Урок 7. Переменные, константы и условия
PHP - Урок 8. Точка входа в приложение. Настройка mod_rewrite и файл .htaccess
PHP - Урок 9. Массивы и switch. Кодим основной каркас
PHP - Урок 10. COOKIE
PHP - Урок 11. Функции. Добавляем ядро системы core.php
PHP - Урок 12. Обзор модели MVC. Добавляем шаблоны страниц в наше приложение
PHP - Урок 13. Введение в базы данных и SQL. СУБД MySQL. Подключаемся к БД из нашего приложения
PHP - Урок 14. Регистрация пользователей на сайте
PHP Урок 15. Авторизация пользователей
PHP. Урок 16. Проверка авторизации. Функция check().
PHP Урок 17. Добавляем CSS фреймворк Bootstrap и jQuery
PHP Урок 18. Загрузка файлов на сервер
PHP Урок 19. Добавляем меню навигации
Теория
Все современные Интернет-приложения используют две основные технологии JavaScript: WebSocket (WS) и AJAX. Ну про WS я планирую отдельный курс. Так как его серверная часть обычно пишется не на PHP, хотя если использовать не хостинг, а например VPS, то можно и на PHP написать сервер. Но тогда он должен работать самостоятельно, как отдельное приложение. Но мы будем в сдедующих курсах подобные вещи делать не на PHP, а в одном из запланированных курсов на NODE.js и естественно в другом (думаю самом интересном) на C =) В данный момент разберемся с технологией AJAX и внедрим ее в наше PHP приложение, которое пишем на PHP.Что такое AJAX
Ну здесь нужно немного вдаться в историю браузеров. Изначально они были примитивные. И просто загружали некоторый текст с другого узла и форматировали теги. Затем они естественно развивались и настал тот момент, когда разработчики браузеров решили что неплохо бы было, если бы в одной странице загруженной в браузере, можно бы было отобразить часть (или целиком) страницы других сайтов.
Придумали тег фрейм (кстати о фреймах мы филосовствовали в предыдущей лекции). Через такой тег можно было загружать и отображать содержимое страниц аналогично загрузке картинок (без небоходимости переходить по ссылкам на данный контент). Но в итоге и этого оказалось мало.
Дело в том, что на тот момент в браузерах уже начал применяться JavaScript. Ну то есть его туда внедрили разработчики этих браузеров.
А тот JavaScript нужен был для того, чтобы модифицировать содержание этих самых загруженных в браузер страниц.
То есть можно было добавлять, удалять, изменять свойства уже загруженного HTML-документа. Но тут выходило так, что похоже на принцип яйца (куриного). Управлять можно было только теми данными, которые уже были загружены в браузер.
А что если во время выполнения скриптов JavaScript мы захотим дополнительные данные загрузить с сервера и добавить их в уже загруженный HTML-документ.
Вот для этого и придумали добавить в JavaScript возможность получать данные с сервера по средствам JS-кода уже после того, как загрузится сама страница сайта.
Для этого был создан специальный объект, который, в качестве параметров конструктора (либо функции) принимал ссылку на ресурс и аналогично фрейму загружал от-туда данные. Сохраняя их в своем буфере (переменной). А уж как Этими данными пользоваться решает сам разработчик.
Можно например взять, создать на JavaScript создать элемент DIV, добавить него загруженные таким образом данные и добавить этот элемент с этими данными в какое нибудь место документа HTML (дерево DOM).
Ну во время отладки такие данные предварительно отображают через функции console.log()
либо alert()
. Что бы убедиться, что они верные.
В итоге, мы имеем, то что можем как-бы подгружать необходимый контент на станицу по некоторым событиям (обычно действиям пользователя), без ее перезагрузки.
Структура нашего приложения вместе с API
Текущий цикл запросов выглядит так:
- Пользователь посылает HTTP запрос.
- Сервер его передает PHP и отрабатывает index.php.
- Внутри index.php вызываются функции, которые мы описываем в core.php.
- Получив результат index подключает необходимые шаблоны, генерируется HTML-страница и отсылается пользователю.
- Пользователь посылает HTTP запрос.
- Сервер его передает PHP и отрабатывает index.php.
- Внутри index.php вызываются функции, которые мы описываем в core.php.
- Получив результат index подключает необходимые шаблоны, генерируется HTML-страница и отсылается пользователю.
У нашего JavaScript JSON API он будет таков:
- Пользователь что-нибудь делает на странице (например нажимает кнопку).
- JS-код отлавливает это событие и отправляет по AJAX на специальную страницу со скриптом (не index.php который до этого за все отвечал).
- Скрипт API проверяет в запросе название метода и параметры, а также данные авторизации, если необходимы.
- Этот скрипт ищет переданный метод в файле coreapi.php - если находит, то вызывает ее и передает все параметры.
- При успешном выполнении результат кодируется в формат json и отправляется клиенту. Если ошибка - то она также кодируется в json. Также при ответе API отправляет нужный заголовок типа контента.
То есть, мы из ajax (точнее из jaavasript c его помощью) сможем вызывать функции, которые у нас имеются в файле coreapi.php, а из него мы сможем вызвать функции, которые у нас в core.php и непосредственно напрямую работают с базой данных.
Вот такая вот незамудренная цепочка. В принципе на практике думаю будет наглядней.
Практика
В папке sys у нас хранится core.php.
Посмотрим там какую нибудь функцию, которая что-то делает, что нам могло бы захотеть запрашивать через js.
Вот например такая подойдет поставка лайка:
function addPostLikeData($pdo, $user_id, $post_id){
$post_id = $pdo->quote($post_id);
$user_id = $pdo->quote($user_id);
$sql = "SELECT COUNT(status) FROM mc_likes WHERE post_id=$post_id AND user_id=$user_id";
$stmt = $pdo->query($sql);
$row = $stmt->fetch(PDO::FETCH_NUM);
//print $row[0];
if($row[0]){
$sql_update = "
BEGIN;
UPDATE mc_likes SET status=1 WHERE post_id=$post_id AND user_id=$user_id;
UPDATE mc_post SET likes_count = likes_count+1 WHERE id=$post_id;
COMMIT;
";
//print $sql_insert;
if(!$pdo->exec($sql_update)){
return true;
}else{
return false;
}
}else{
$sql_insert = "
BEGIN;
INSERT INTO mc_likes (post_id, user_id) VALUES ($post_id, $user_id);
UPDATE mc_post SET likes_count = likes_count+1 WHERE id=$post_id;
COMMIT;
";
//print $sql_insert;
if(!$pdo->exec($sql_insert)){
return true;
}else{
return false;
}
}
}
Конечно ни кому не нравится когда при после поставки лайка обновляется вся страница, следовательно добавить ее в наше API.
Для этого мы создаем для нее обертку в coreapi.php. Который мы создаем рядом с core.php для удобства.
// Лайки к постам
function addPostLike($pdo){
$user_id = clearInt($_POST['user_id']);
$user_hash = clearStr($_POST['user_hash']);
$post_id = clearInt($_POST['post_id']);
//print_r($_POST);
if(check($pdo, $user_id, $user_hash)){
//print_r($_POST);
if(addPostLikeData($pdo, $user_id, $post_id)){
print '{"response":1}';
}else{
print '{"response":0, "error":"Error added post"}';
}
}else{
print '{"response":0, "error":"No avtorized user"}';
}
}
То есть из функции АПИ мы вызываем основные функции, напрямую работающие с БД. (У них поэтому суффикс Data)
Нам необходимо с помощью данных функций произвести необходимые действия и проверки перед запросом.
Теперь тот самый файл, к которому обращаемся из js мы его создадим в корне приложения, как /api.index.php
Тут кстати файл очень простой. В более новых моих проектах там еще куча кода на выявление десятков ошибок)) А этот как раз для учебы подойдет.
define(ROOT, $_SERVER['DOCUMENT_ROOT']);
require(ROOT.'/sys/core.php');
require(ROOT.'/sys/coreapi.php');
$pdo = init();
$pdo->exec("SET NAMES utf8");
//echo 'Test: '.route(2);
// Проверяем существует ли вызываемая функция в coreapi
$func = clearStr(route(2));
if(function_exists($func)){
//print 'call: '.$func;
$func($pdo);
}else{
print 'Function '.$func.' not fiund';
}
Тут замудренка c route потому что формат я слизал с какой то версии АПИ ВК. На самом деле все просто.
Название функции передается как аргумент http. А вот параметры уже по-человечески :)
В остальном все просто. Проверяем есть ли функция в coreapi.php.
Кстати только сейчас обнаружил что здесь уязвимость: Нельзя допускать, чтобы из API был доступ к основным функциям ядра. Только те функции, которые есть в coreapi должны быть доступны для вызова из JS.
Ну а вызывается функция $func($pdo); - на самом деле в php полно других способов вызвать ее.
Теперь разберемся как обращаться к этому делу из JS.
В проекте у нас есть папка assets в ней js в которой библиотечные и наши собственные функции.
Для работы со странице с лайками там мы создадим файл like.js. А вот внутри него эта функция:
$("#add_like").on("click", function(){
//alert("like " + post_id + " " + this_id);
$.post(
'/api/addPostLike',
{
"user_id": this_id,
"user_hash": this_hash,
"post_id": post_id,
},
function(data){
// тут добавляем наш пост сверху выборки
//alert(data);
location.reload(true);
}
);
});
Во-первых мы в ней внешем обработчик на элемент страницы (кнопку лайка). $.post обращается к нашему API.
При успехе страница обновляется. Это кстате не то что нам было нужно. Вместо location.reload на самом деле нужно поработать с DOM.
Но это я не реализовал. Так как приуныл, когда-то там когда это кодил =)
На самом деле в дальнейшем нужно будет написать проверку data.result, которая возвращается API по средствам JSON.
Главное но это мелочи, их достаточно просто реализовать. Код там очень небольшой будет вместо обновления.
Таким образом мы примерно теперь представляем как устраивается API с помощью PHP.
Ссылку на githab я выложу через один урок.