четверг, 21 января 2010 г.

jQuery и Google Maps API №2: Добавляем в БД и на карту маркеры при помощи AJAX

jQuery и Google Maps API №2: Добавляем в БД и на карту маркеры при помощи AJAX

Итак, продолжаем наши мытарства вместе с jQuery и Google Maps API. В этом уроке мы рассмотрим, каким образом можно добавлять маркеры на карту и сохранять информацию о них в БД при помощи AJAX и PHP/MySQL.

Этот урок можно разбить на две части. В первой мы будем делать все для того, чтобы посредством AJAX сохранить информацию о добавленном маркере. Во второй части мы будем делать все для того, чтобы вытащить информацию о местоположении маркера из БД (посредством все того AJAX). За основу возьмем код из предыдущего урока. Что ж, приступим…

Шаг #1: Создаем форму для добавления местоположения

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

<form id="add-point" action="map-service.php" method="post">
<input type="hidden" name="action" value="savepoint" id="action">
<fieldset>
<legend>Добавить точку на карту</legend>
<div class="error" style="display:none;"></div>
<div class="input">
<label for="name">Название</label>
<input type="text" name="name" id="name" value="">
</div>
<div class="input">
<label for="address">Адрес</label>
<input type="text" name="address" id="address" value="">
</div>
<button type="submit">Добавить точку</button>
</fieldset>
</form>

Немного о форме:

  • Форма будет обрабатываться на сервере скриптом map-service.php;
  • Скрытое поле <input type="hidden" name="action" value="savepoint" id="action"> будет выступать в роли флага, который будет указывать скрипту на наше желание сохранить информацию в БД;
  • Пустой блок <div class="error" style="display:none;"></div> будет использоваться для вывода ошибок.

Шаг #2: Стилизуем форму

Добавим несколько строк CSS-кода:

#add-point { float:left; }
div.input { padding:3px 0; }
label { display:block; font-size:80%; }
input, select { width:150px; }
button { float:right; }
div.error { color:red; font-weight:bold; }

Шаг #3: Геокодируем введенный адрес

3a) Отключаем стандартную отправку формы

На этом шаге мы отключим стандартную отправку формы. Для этого будем использовать метод submit() jQuery с произвольной функцией внутри.

$("#add-point").submit(function(){
geoEncode();
return false;
});

3b) Добавляем геокодирование

Определим статусные сообщения геокодера:

var geo = new GClientGeocoder();

var reasons=[];
reasons[G_GEO_SUCCESS] = "Информация успешно добавлена";
reasons[G_GEO_MISSING_ADDRESS] = "Неверный адрес";
reasons[G_GEO_UNKNOWN_ADDRESS] = "Неизвестный адрес";
reasons[G_GEO_UNAVAILABLE_ADDRESS]= "Недоступный адрес";
reasons[G_GEO_BAD_KEY] = "Неверный API-ключ";
reasons[G_GEO_TOO_MANY_QUERIES] = "Слишком много запросов";
reasons[G_GEO_SERVER_ERROR] = "Ошибка на сервере";

3с) Получаем координаты из адреса

function geoEncode() {
var address = $("#add-point input[name=address]").val();
geo.getLocations(address, function (result){
if (result.Status.code == G_GEO_SUCCESS) {
geocode = result.Placemark[0].Point.coordinates;
savePoint(geocode);
} else {
var reason="Код "+result.Status.code;
if (reasons[result.Status.code]) {
reason = reasons[result.Status.code]
}
$("#add-point .error").html(reason).fadeIn();
geocode = false;
}
});
}

Шаг #4: Отправляем информацию на сервер

function savePoint(geocode) {
var data = $("#add-point :input").serializeArray();
data[data.length] = { name: "lng", value: geocode[0] };
data[data.length] = { name: "lat", value: geocode[1] };
$.post($("#add-point").attr('action'), data, function(json){
$("#add-point .error").fadeOut();
if (json.status == "fail") {
$("#add-point .error").html(json.message).fadeIn();
}
if (json.status == "success") {
$("#add-point :input[name!=action]").val("");
var location = json.data;
addLocation(location);
zoomToBounds();
}
}, "json");
}

jQuery-метод $.post принимает следующие параметры:

  • URL для отправки информации: $(this).attr('action') принимает значение атрибута action формы;
  • Data – информация для отправки на сервер. Она представляет собой пары вида { name: "inputname", value: "inputvalue" }, которые создаются методом serializeArray() из элементов :input нашей формы;
  • Function – функция, которая сработает после получения ответа сервера. Она принимает один параметр, в котором, собственно, и содержится ответ от сервера;
  • Type of data – формат данных. Мы будем использовать JSON.

Шаг #5: Используем PHP для обработки формы

На этом шаге мы будем обрабатывать информацию, переданную на сервер при помощи jQuery.

5a) Проверяем переданные данные

Тут мы проверяем, действительно ли значением скрытого поля action является "savepoint". Затем, проверяем на корректность название места. Если что-то не так, то возвращаем сообщение об ошибке, которое будет показано в следующем подшаге.

<?php
if ($_POST['action'] == 'savepoint') {
$name = $_POST['name'];
if(preg_match('/[^\w\s]/i', $name)) {
fail('Неверное название.');
}
if(empty($name)) {
fail('Введите название.');
}
}
?>

Все это мы пишем в файл map-service.php.

5b) Возвращаем сообщение об ошибке как JSON-объект

function fail($message) {
die(json_encode(array('status' => 'fail', 'message' => $message)));
}

Для того, чтобы остановить выполнение скрипта при появлении ошибки, используем функцию die(). В качестве аргумента передаем ей результат выполнения функции json_encode() (которая, кстати, есть только в PHP версии 5.2+).

Шаг #6: Отображаем сообщение об ошибки при помощи jQuery

Пришло время написать обработчик ошибок на стороне клиента. Для начала скроем блок с ошибкой, если он уже был показан: для этого используем метод hide() jQuery. Далее, проверяем статус ответа от сервера (json.status) и показываем блок с ошибкой, если json.status имеет значение "fail". Для отображения блока с ошибкой испльзуем методы html() (который помещает текст ошибки в контейнер div с классом "error") и fadeIn() (который, собственно, этот контейнер потом отображает).

$("#add-point .error").hide();
if (json.status == "fail") {
$("#add-point .error").html(json.message).fadeIn();
}

Шаг #7: Создаем БД и сохраняем введенную информацию

Используя SQL, создадим таблицу со следующими полями: "name" (название места), "latitude" (широта), "longitude" (долгота) и "id" (идентификатор).

7a) Создаем таблицу

CREATE TABLE `locations` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) DEFAULT NULL,
`lat` FLOAT(15,11) DEFAULT NULL,
`lng` FLOAT(15,11) DEFAULT NULL,
PRIMARY KEY (`id`)
)

7b) Добавляем информацию в созданную таблицу

$query = "INSERT INTO locations SET name='$_POST[name]', lat='$lat', lng='$lng'";
$result = map_query($query);

if ($result) {
success(array('lat' => $_POST['lat'], 'lng' => $_POST['lng'], 'name' => $name));
} else {
fail('Невозможно добавить точку.');
}

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

Функция map_query():

function map_query($query) {
mysql_connect('MYSQL_HOST', 'MYSQL_USER', 'MYSQL_PASSWORD')
OR die(fail('Невозможно подключиться к БД.'));
mysql_select_db ('MYSQL_DATABASE');
return mysql_query($query);
}

Теперь – функция, которая срабатывает при успешном выполнении запроса:

function success($data) {
die(json_encode(array('status' => 'success', 'data' => $data)));
}

Шаг #8: Добавляем маркер на карту

Пришло время написать обработчик положительного ответа от сервера:

if (json.status == "success") {
$("#add-point :input[name!=action]").val("");
var location = json.data;
addLocation(location);
zoomToBounds();
}

Если сервер вернул положительный ответ, то мы должны очистить форму, чтобы предотвратить повторную отправку данных. Далее, срабатывает функция addLocation(), рассмотренная нами в прошлом уроке:

function addLocation(location) {
var point = new GLatLng(location.lat, location.lng);
var marker = new GMarker(point);
map.addOverlay(marker);
bounds.extend(marker.getPoint());

$("<li />")
.html(location.name)
.click(function(){
showMessage(marker, location.name);
})
.appendTo("#list");

GEvent.addListener(marker, "click", function(){
showMessage(this);
});
}

Шаг #9: Загружаем информацию из БД и отображаем ее

Как только страница будет загружена, нам необходимо отобразить на карте все сохраненные нами точки. Самый простой способ это сделать – передать серверу GET-параметр, чтобы тот, в свою очередь, вернул соответствующий JSON-объект. Для этого используем метод $.getJSON:

$.getJSON("php/map-service.php?action=listpoints", function(json) {
// Тут будет функция из шага #11
});

Шаг #10: Получаем информацию из БД

if ($_GET['action'] == 'listpoints') {
$query = "SELECT * FROM locations";
$result = map_query($query);
$points = array();
while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
array_push($points, array('name' => $row['name'], 'lat' => $row['lat'], 'lng'=> $row['lng']));
}
echo json_encode(array("Locations" => $points));
exit;
}

Тут тоже все очень и очень просто: проверяем правильность GET-параметра и, если все нормально, то отдаем в качестве ответа JSON-объект, в котором содержится список всех точек, сохраненных в БД.

Шаг #11: Отображаем маркеры, соответствующие точкам

Обходим циклом по JSON-списку, который вернул сервер в качестве своего ответа:

if (json.Locations.length > 0) {
for (i=0; i<json.Locations.length; i++) {
var location = json.Locations[i];
addLocation(location);
}
zoomToBounds();
}

Шаг #12: Устанавливаем такой уровень зума, чтобы были видны все объекты

Определяем координаты границ, в которых находятся маркеры, отображаемые на карте:

var bounds = new GLatLngBounds();

Теперь, определяем метод zoomToBounds(), который будет устанавливать такой уровень зума, чтобы все маркеры отображались в видимой области карты:

function zoomToBounds() {
map.setCenter(bounds.getCenter());
map.setZoom(map.getBoundsZoomLevel(bounds)-1);
}

Ну вот и все. Ох уж мне эта динамика… А вот и пример. :)