БлогNot. JQuery: как обратиться из Javascript к базе данных

JQuery: как обратиться из Javascript к базе данных

Нечто подобное уже есть вот тут, но сейчас я хочу сделать AJAX-запрос покороче, с использованием именно базы MySQL, да ещё и дописать уже существующую сборку "шахматной решалки", "закрыв" в ней упомянутые по ссылке "строки 41 и 56".

Сначала совсем простой пример, проще которого я придумать не смогу. На локальном сервере есть папка, допустим, с именем ptest, в ней - обычный файл HTML с именем index.html:

<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
 <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
</head>
<body>

<script type="text/javascript">
 var num = 5;
 var str = 'Hello, world!';
 $.post("./script.php", {num: num, str: str});
</script>

</body></html>

Но файл этот не совсем простой:

  • во-первых, он подключает библиотеку JQuery с сайта Гугля;
  • во-вторых, с помощью JQuery он делает из клиентского кода на Javascript запрос к серверному скрипту на PHP.

В этой же папке лежит и файл script.php, приём данных в нём ничем не отличается от случая, когда данные были переданы из формы HTML или от другого PHP-скрипта:

<?php
 $num = 0;
 if (isset($_POST['num'])) $num = intval(strip_tags(trim($_POST['num'])));
 $str = '';
 if (isset($_POST['str']))  $str = mysql_real_escape_string(strip_tags(trim($_POST['str'])));
 file_put_contents ("data.txt","$num\t$str");
?>

Наш скрипт просто записал полученные данные в текстовый файл, отделив число от строки символом табуляции \t. А на самом деле script.php мог делать с данными, полученными от клиента, что угодно, например, писать их в базу MySQL, как-то обрабатывать и т.д.

Мы можем проверить этот код в работе, но только правильно, не щёлкая по html-файлу, а по протоколу http через браузер, скажем, разместив папку ptest с двумя файлами в корневой папке Denwer или другого локального сервера, а затем набрав в браузере адрес http://localhost/ptest/

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

Пусть к "корневому" документу HTML или скрипту PHP подключён скрипт:

<script src="js/puzzle.js"></script>

Неважно, что скрипт лежит во вложенной папке js, всё равно вызывать серверные php-скрипты он будет из той же папки, где находится "корневой" документ, да иной подход и чреват проблемами. Например, ajax-запросы на ресурсы с доменами, поддоменами и протоколами, отличными от текущего, как правило, запрещены. То есть, из клиентского файла domain.com/script.js нельзя обратиться к серверному anotherdomain.com/script.php. А если бы было можно, любой дятел смог бы бомбить со своего компьютера любую форму в интернете. Разумеется, можно написать браузер или обёртку, где кроссдоменные ajax-запросы разрешены, но ведь порвут :)

Наш клиентский puzzle.js, пользуясь тем, что в нём подключён JQuery, хочет иногда вызывать серверные скрипты plus.php и minus.php - для увеличения и уменьшения рейтинга юзверька в зависимости от того, правильно ли тот решает задачи:

$.post("./minus.php", {balM: puzzle.balM, move: correct_move}); // Отправка данных на обработку рейтинга (-)
//...
$.post("./plus.php", {balP: puzzle.balP, move: correct_move}); // Отправка данных на обработку рейтинга (+)

Это опять образец короткого вызова метода $.ajax для отправки данных на сервер методом POST. Здесь первым параметром, с именем balM и balP, передаётся число (на сколько уменьшить или увеличить рейтинг), а вторым параметром, с именем move, передаётся строка (запись хода).

Скрипты minus.php и plus.php будут очень похожи, только первый из них не будет повторно "минусовать" неверные ходы пользователя, во втором такая проверка не нужна, так как он вызывается только после решения задачи:

<?php
 require_once 'functions.php';
 $balM = intval(strip_tags(trim($_POST['balM'])));
 $mov = mysql_real_escape_string(strip_tags(trim($_POST['move'])));
 $ip = get_ip();
 $result = dbquery('select * from '.DB_TABLE.' where ip="'.$ip.'" limit 0,1');
 if ($result and dbrows($result)) {
  $note = dbfetcha($result);
  $note['cnt'] -= $balM;
  if ($note['mov']!=$mov) { //не минусовать повторно за неверные
   $result = dbquery('update '.DB_TABLE.' set cnt = "'.$note['cnt'].'", mov = "'.$mov.'" where ip = "'.$ip.'"');
   if (!$result) die ('Invalid query 1: ' . mysql_error());
  }
 }
 else {
  $result = dbquery('insert into '.DB_TABLE.' (ip, cnt) values ("'.$ip.'", "'.(0-$balM).'")');
  if (!$result) die ('Invalid query 2: ' . mysql_error());
 }
?>
<?php
 require_once 'functions.php';
 $balP = intval(strip_tags(trim($_POST['balP'])));
 $ip = get_ip();
 $result = dbquery('select * from '.DB_TABLE.' where ip="'.$ip.'" limit 0,1');
 if ($result and dbrows($result)) {
  $note = dbfetcha($result);
  $note['cnt'] += $balP;
  $result = dbquery('update '.DB_TABLE.' set cnt = "'.$note['cnt'].'" where ip = "'.$ip.'"');
  if (!$result) die ('Invalid query 1: ' . mysql_error());
 }
 else {
  $result = dbquery('insert into '.DB_TABLE.' (ip, cnt) values ("'.$ip.'", "'.(0+$balP).'")');
  if (!$result) die ('Invalid query 2: ' . mysql_error());
 }
?>

Скрипты пользуются файлом functions.php, содержащим несложную "обёртку" для операций с базой, а также более-менее адекватную функцию определения IP-адреса пользователя:

<?php

 require_once('config.php');

 if (!isset($conid)) {

  function dbconnect() {
   $mysql=mysql_connect(DB_HOST, DB_USER, DB_PASS); //хост, логин, пароль
   mysql_select_db(DB_NAME); //имя базы данных
   return $mysql;
  }

  function dbquery ($sql) { return mysql_query($sql,get_conid()); }
  function dbrows ($result) { return mysql_num_rows($result); }
  function dbfree ($result) { mysql_free_result($result); }
  function dbclose ($conid) { mysql_close(get_conid()); }

  function dbfetcha ($result) {
   if ($row=mysql_fetch_assoc ($result)) return $row;
   else return false;
  }

  function dbfetch ($result){
   if ($row=mysql_fetch_array($result)) return $row;
   else return false;
  }
 
  function get_conid () {
   static $conid=0;
   if ($conid==0) {
    $conid=dbconnect();
    mysql_set_charset (DB_ENCODING,$conid);
   }
   return $conid;
  }

  get_conid ();
 }

 function get_ip () {
  if( getenv('HTTP_X_FORWARDED_FOR') != '' ) {
   $client_ip = ( !empty($_SERVER['REMOTE_ADDR']) ) ? $_SERVER['REMOTE_ADDR'] : 
    ( ( !empty($_ENV['REMOTE_ADDR']) ) ? $_ENV['REMOTE_ADDR'] : $REMOTE_ADDR );
   $entries = explode(',', getenv('HTTP_X_FORWARDED_FOR'));
   reset($entries);
   while (list(, $entry) = each($entries)) {
    $entry = trim($entry);
    if ( preg_match("/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/", $entry, $ip_list) ) {
     $private_ip = array('/^0\./', '/^127\.0\.0\.1/', '/^192\.168\..*/', 
      '/^172\.((1[6-9])|(2[0-9])|(3[0-1]))\..*/', '/^10\..*/', '/^224\..*/', '/^240\..*/');
     $found_ip = preg_replace($private_ip, $client_ip, $ip_list[1]);
     if ($client_ip != $found_ip) {
      $client_ip = $found_ip;
      break;
     }
    }
   }
  }
  else {
   $client_ip = ( !empty($_SERVER['REMOTE_ADDR']) ) ? $_SERVER['REMOTE_ADDR'] : 
    ( ( !empty($_ENV['REMOTE_ADDR']) ) ? $_ENV['REMOTE_ADDR'] : $REMOTE_ADDR );
  }
  return $client_ip;
 }
?>

Примечание. Адекватна функция get_ip будет только в случае "классического" ipv4 и PHP5. Для ipv6 и PHP7 лучше поискать другое решение (см. комментарии здесь).

В свою очередь, functions.php подключает config.php, содержащий обычные настройки для коннекта, у меня на локалхосте требуется вот такой config.php, на реальном хосте настройки придётся изменить:

<?php
 define('DB_HOST', 'localhost'); //Хост, на котором размещен сервер MySQL
 define('DB_USER', 'root'); //Имя пользователя MySQL
 define('DB_PASS', 'root'); //Пароль пользователя MySQL
 define('DB_NAME', 'my'); //Имя базы данных
 define('DB_TABLE', 'chesspuzzles'); //Имя таблицы
 define('DB_ENCODING', 'cp1251'); //Кодировка базы данных
?>

Перед тем, как запустить скрипты, я создал на локальном сервере таблицу chesspuzzles для хранения данных. Для этого достаточно выбрать в phpMyAdmin (в Denwer он вызывается из браузера адресом http://localhost/tools/phpmyadmin/ ) нужную базу данных (у меня с именем my ) и выполнить к ней такой SQL-запрос:

DROP TABLE IF EXISTS chesspuzzles;
CREATE TABLE IF NOT EXISTS chesspuzzles (
  ip varchar(15),
  mov varchar(8),
  cnt int
);

В той же папке, где остальной PHP-код и главный файл, можно поместить скрипт list.php, который покажет текущую статистику по IP-адресам пользователей скрипта и их баллам:

<html><head>
 <meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
 <title>Решалка: весь рейтинг</title>
</head>
<body bgcolor="white">
<?php
 require_once 'functions.php';
 $result = dbquery('select * from '.DB_TABLE.' order by cnt desc');
 echo '<table align="center" width="90%" border="1" cellpadding="4" cellspacing="0">';
 if ($result and dbrows($result)) {
  while ($note = dbfetcha($result)) {
   echo '<tr><td width="50%">'.$note['ip'].'</td><td>'.$note['cnt'].'</td></tr>';
  }
 }
 else { echo '<tr><td>Нет данных</td></tr>'; }
 echo '</table><div align="center"><a href="list.php">Обновить</a></div>';
?>
</body></html>

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

P.S. Для PHP версий 5.5 и выше нужно перевести код работы с базой с MySQL на MySQLi, как сделано здесь.

18.11.2015, 01:01 [28793 просмотра]


теги: javascript php mysql шахматы jquery сервер

показать комментарии (1)