Pull to refresh

Работа с Google Fusion Tables — JS и PHP

Website development *
Sandbox
Summary: Возникла задача — создать высоконагруженное веб-приложение при крайне ограниченных ресурсах сервера.
В качестве хранилища данных был выбран Fusion Tables от Google.
Что описано в статье: работа с Fusion Tables из
а) Javascript — только выборка данных;
б) PHP (Zend Framework) — выборка, добавление и обновление;
в) пользовательский интерфейс от Google — создание таблиц и представлений.
Чего нет в статье:
а) нагрузочного тестирования;
б) подробного описания приложения — упор делается именно на использование Fusion Tables в своих PHP+Js проектах.

У компании Google есть большое количество различных продуктов. — без банальностей.
Только ленивый веб-программист не использовал в своих проектах карты от Google. Просто вставляется, красиво выглядит, не грузит собственный сервер, удобный, хорошо документированный (по крайней мере для v.2) API, высокая по крайней мере, не низкая скорость работы — плюсы есть. «Возможно, FT тоже будет приятным» — подумал я. Правда, не проверял. И вот результат.


Недавно у меня появилась идея написать какое-нибудь приложение для всем известной социальной сети — просто в качестве эксперимента. После небольшого раздумья выбор пал на простую доску объявлений — место, где можно предложить кому-нибудь ненужный тебе хлам. Приложение я здесь описывать не буду, ссылку не дам, пока оно на модерации. С флешем я, к сожалению, «на Вы», поэтому писал на стандартной связке PHP + Js. Необычного в данном приложении то, что вместо стандартного mySQL в качестве хранилища был выбран сервис от Google — Fusion Tables. Почему? Очень просто — нет никаких гарантий монетизации. Под рукой свободных серверных мощностей нет — есть старенький, плохо настроенный девелоперский сервер. Делать какие-либо вложения на начальном этапе не хотелось — напомню: проект писался просто ради интереса. А во время поиска ответа на какой-то вопрос по Google Maps API наткнулся на статью про Fusion Tables.

Немного про права доступа.


Запрашивать данные из таблицы будет напрямую яваскрипт. Т.е. придется сделать таблицу общедоступной (по крайней мере для чтения). Я не знаю, каким образом можно обфусцировать js настолько, чтобы человек, желающий взломать таблицу не смог из скрипта выудить логин и пароль.
Соответственно, давать всем подряд права на запись в таблицу нет желания. Поэтому запись данных в таблицу будет происходить только на серверной стороне. Т.е. пользователь добавляет объявление, js передает введенные пользователем данные на сервер, php-скрипт их подхватывает, авторизируется на Google FT и вставляет данные в таблицу.

Заранее согласен со всеми упреками к предыдущему абзацу! Повторюсь — я просто не знаю, как в js можно скрыть логин/пароль.

Итак, поехали:

Шаг 1 — создание таблиц


Через пользовательский интерфейс FT создается таблица. Интерфейс интуитивно понятен, не требует каких-то специфических знаний. Субъективно — чем-то напоминает MS Access.
Там же можно вставить несколько строк в таблицу. Обычно, при работе с mySQL, я для этого использую phpMyAdmin
На видео пример создания простой таблицы с тремя полями.


Да, скажу сразу: в FT имена таблицы — числа. К примеру, для созданной таблицы это 596524. «Data» — всего лишь псевдоним, его нельзя использовать в запросах.

Сразу забегу наперед: для простых таблиц поле id (первичный autoincrement-ключ) создавать не требуется, у каждой таблицы появится «скрытое» поле ROWID. Создавать id имеет смысл только в том случае, если Вы планируете делать JOIN по нескольким таблицам. Как это сделать? В верхнем меню на странице таблицы есть кнопка «Merge». Появившийся диалог позволит Вам создать что-то вроде представления (view в терминах mySQL).

Теперь нужно расшарить таблицу. Для того чтобы из js без авторизации получать данные. Таблица будет доступна только для чтения для всех. Изменять/добавлять данные сможет только автор.


Таблица готова. Вот ссылка на нее.

Шаг 2 — отображение данных — JavaScript


На моем почти сервере хранится html-страница и js файл. При загрузке страницы данные для отображения (список объявлений) загружается непосредственно с серверов Google.

Отображение данных из таблицы производится с помощью модуля visualization. Взято вот отсюда.
Для этого в заголовок index.html добавляем ссылку на javascript — www.google.com/jsapi, а в файл default.js строку
google.load('visualization', '1');

Более подробно о загрузке модулей Google API в Javascript можно почитать вот здесь.

Помимо этого нужно выполнить запрос к таблице и указать функцию — обработчик ответа.
//формирование запроса
        var queryText = 'SELECT id, name FROM 596524;';
//url запроса
        var reqUri = 'http://www.google.com/fusiontables/gvizdata?tq='  + encodeURIComponent(queryText);
//создаем объект visualization
        var query = new google.visualization.Query(reqUri);
//посылаем запрос и указываем функцию - обработчик ответа
        query.send(displayData);


UPD:Как заметил dkukushkin, при использовании IE (вплоть до 9-го) при наличии в выборке кириллицы (наверняка не только!) пользователь видит "?" вместо символов. Посему убираем Google.vizualization и делаем запрос своими силами — хвала jQuery, нет нужды писать страшный XMLHttpRequest руками
        var queryText = 'SELECT id, name FROM 596524;';
	$.ajax({
		url:'http://tables.googlelabs.com/api/query?sql='+queryText+'&jsonCallback=?',
		type: 'GET',
		success:function(resp){
			displayData(resp);
		},
		dataType:'json'
	});

У меня после этого баг прошел, и все сразу стало красиво. Хотя я планировал долгую битву — работа с заголовками например так, но ie на этот раз удивил и отобразил кириллицу нормально. Формат полученных данных немного отличается от того, который был при использовании Vizualization, так что функция displayData должна быть изменена тоже. Но там ничего страшного, возвращается обычный JSON объект, у которого есть параметр table, содержащий 2 массива cols и rows — все просто и понятно.

Как уже было написано выше — в качестве имен таблиц в запросах используются их ID (кстати, можно увидеть интересный факт — 26 февраля я создал таблицу с ID = 505 xxx, а созданная сегодня таблица data получила ID 596 524 — можно сделать вывод: ~ 91 000 таблиц меньше чем за месяц было добавлено на FT).

И, наконец, функция вывода результатов на странице
function displayData(response) {
 
  //более детальная информация об объекте response доступна здесь
  //http://code.google.com/apis/visualization/documentation/reference.html#QueryResponse
  numRows = response.getDataTable().getNumberOfRows();
  numCols = response.getDataTable().getNumberOfColumns();
  
  //собираем результат в строку
  fusiontabledata = "<b>";
  for(i = 0; i < numCols; i++) {
    fusiontabledata += response.getDataTable().getColumnLabel(i) + ",";
  }
  fusiontabledata += "</b><br />";
  
  for(i = 0; i < numRows; i++) {
    for(j = 0; j < numCols; j++) {
      fusiontabledata += response.getDataTable().getValue(i, j) + ", ";
    }
    fusiontabledata += "<br />";
  }  
  //вывод на странице
   document.getElementById('echoer').innerHTML = fusiontabledata;
}


Пример на jsFiddle

Теперь ложка дегтя. FT использует язык запросов SQL. Подробнее можно почитать здесь. Но! Язык этот имеет несколько неожиданных ограничений — например нет оператора OR в условиях команды SELECT (появился, смотри UPD2) (и даже нет возможности заменить его чем-то вроде (a | b) = !( !a & !b ), поскольку нет оператора NOT), на эту тему даже создали proposal). Хотя есть встроенные функции для работы с координатами. FT вообще направлен на поддержку карт от Google.

Во избежание кеширования запросов (я не разбирался где именно они кешируются — в браузере, visualization или сам FT) на реальном проекте имеет смысл добавить в условие запроса что-то вроде «AND name not equal to 234567», где 234567 — случайное число (см. Math.getRandomInt()).

По-хорошему конечно нужно было бы написать свой собственный класс для отправки запросов и обработки ответов. Если присмотреться — нет ничего сложного. SELECT — отправляется GET-запросом по специальному адресу, ответ от сервера получаем в формате JSON. Я так и поступил — получил довольно сырой код, который здесь выкладывать стыдно. А буквально сегодня наткнулся на замечательный топик от trurl123 и понял, что нужно еще поизучать MVC в js и, возможно, отказаться от своих наработок.

Шаг 3 — добавление данных — PHP + Zend Framework


Когда я взялся за эту часть работы — ничего не предвещало беды. Zend Framework содержит целый API для работы с сервисами от Google. Но не все оказалось так радужно. В общем, класс для работы с FT пришлось писать самому, взяв за основу Zend_Gdata_Base и воспользовавшись нерабочим решением под Drupal.

Файл с классом можно скачать здесь. Стыдно признаться, файл не соответствует стандарту Zend, он содержит 3 класса, плохо документирован. Но он работает.

Для того чтобы его использовать, необходимо подключить собственно сам файл, а также Zend/Gdata/ClientLogin.php.

Пример:
//авторизация (по крайней мере данные для авторизации указываются здесь)
$client = Zend_Gdata_ClientLogin::getHttpClient('your_login_here@gmail.com', 'your_pass_here', 'fusiontables');
//создание экземпляра класса
$base = new Zend_Gdata_Fusion($client);

//запрос на выборку
$sql = "SELECT ROWID FROM 596524 WHERE id = 1;";
$rowdata =  $base->query($sql)->get_array();
print_r($rowdata);
//вставка строк - согласно API необходимо перечислить все столбцы таблицы
$newRowId = $base->insertRow('596524',array(
	'id' => time(),
	'name' => 'trird row',
	'added' => date('n/j/y'),
) );
//обновление строки
$base->updateRow(
    '596524', //ID таблицы
    array('name' => 'new first row'), //ассоциативный массив значений
    $rowdata[1][0] //ROWID полученный в запросе на выборку
);


Если дойдут руки, доведу файл до ума и залью на Zend Proposal.

Еще один момент: если нет нужды выполнять INSERT/UPDATE команды, можно использовать Zend_Base, например как описано здесь. Дело в том что по спецификации через GET запрос можно выполнить только SELECT ( http://code.google.com/intl/ru/apis/fusiontables/docs/developers_guide.html#Updating "… To update a row, send an authenticated POST request..." )

На этом все. Любые камни замечания приветствуются. Спасибо.

PS: и конечно, я понимаю, что нет смысла держать коммерческий проект на шаровых услугах, но, если мое приложение станет коммерческим (т.е. если у него появится достаточное количество пользователей и я придумаю как на них заработать), я немедленно мигрирую на собственный сервер. А на данный момент я имею:
— достаточно красивое и быстро работающее приложение при небольших начальных затратах;
— веру что все будет работать так же быстро и под нагрузкой.

PPS: Да, я надеюсь что code highlighter глючит только при предпросмотре

Ссылки:
Fusion Tables API — http://code.google.com/intl/ru/apis/fusiontables/docs/developers_guide.html
Fusion Tables SQL syntax — http://code.google.com/intl/ru/apis/fusiontables/docs/developers_reference.html
Fusion Tables UI — http://tables.googlelabs.com/
Zend Gdata — http://framework.zend.com/manual/en/zend.gdata.html
PHP-класс для работы с FT — http://barahlo.semero.com/description/Zend_Gdata_Fusion.zip
UPD2: это просто праздник какой-то! сегодня ночью обновили proposal и теперь появился аналог OR! Сам еще не тестировал, но надеюсь все будет ок.
Tags:
Hubs:
Total votes 38: ↑35 and ↓3 +32
Views 13K
Comments Comments 28