Деловая неделя

Простая php-функция, принимающая на входе текст запроса mysql, а на выходе выдающая его результат на экран в виде html-таблицы.

ЕЖЕНЕДЕЛЬНАЯ РЕКЛАМНАЯ ГАЗЕТА ДЛЯ ПРЕДПРИЯТИЙ «Деловая неделя» (Иркутск)

Идеальная визуализация БД (PHP, MySQL)

Большинство PHP-программистов наизусть помнят некоторые часто встречающиеся на практике примеры работы с MySQL из Справочного руководства:

while ($row = mysql_fetch_assoc($result)) {
 echo $row["userid"];
 echo $row["fullname"];
 ...
}

В практике работы по автоматизации офиса, однако, часто возникает необходимость вывести на экран фрагмент mysql-таблицы (или результатов запроса) «напрямую», «как есть», то есть именно в виде таблицы, примерно так, как она хранится в БД. Обычно это бывает нужно для отладки, но всё чаще требуется и для пользовательского интерфейса. И постепенно программист начинает подуставать от бесконечного рисования всех этих $row["userid"] руками.

И тогда, порывшись в справочном руководстве или в чужих скриптах, программист начинает оптимизацию. Для начала надо вывести в заголовке таблицы названия полей. В отладочных целях нужны точные, латинские наименования, как в БД (как их превратить потом для пользователя в русские мы расскажем позже). Для этого есть функция mysql_field_name() – она может из результата запроса извлечь имя любого поля (колонки, столбца). Но нам надо не любого, а всех. Функция mysql_field_name() выдаёт наименования полей по их порядковому номеру. Мы не знаем этих порядковых номеров, да нам это и не надо, надо лишь знать общее количество полей, тогда мы можем просто перебрать все числа от 0 до общего количества в цикле. Общее количество полей запроса даёт функция mysql_num_fields(). Собрав всё вместе, получим нечто вроде:

echo "<table><tr>";
$f=mysql_num_fields ($result);
for ($i=0; $i<$f; $i++) {
 $name=mysql_field_name($r, $i);
 echo "<th>$name</th>";
}
echo "</tr>";

Затем, воспользовавшись той же идеей и той же переменной $f, выведем в цикле все значения таблицы:

while ($row = @mysql_fetch_array($r, MYSQL_NUM)) {
 echo "\n<tr>";
 for ($i=0; $i<$f; $i++) {
  $value=$line[$i];
  echo "<td>$value</td>";
 }
 echo "</tr>";
}

Такие конструкции часто встречаются если и не в справочных руководствах, то уж во многих популярных скриптах точно. Это довольно удобно: на входе текст запроса mysql, а на выходе – его результат в виде таблицы. Только в начале обработки обычно добавляют ещё стандартную проверку на существование строк в запросе (если строк нет, выводить ничего не надо):

$sql="select * from `users`";
$result=mysql_query($sql);
$n=mysql_num_rows($r);
if (!$n) exit("В запросе \"$sql\" нет строк.");

Посмотреть, что получилось, можно на странице http://dn.ir2.ru/mysql0.php (правда, там не совсем так, как в статье, – обработка вывода запроса на экран помещена в отдельную функцию sqlres). Там можно вставить в поле запроса фразу "show create table `img`" и потом экспериментировать сколько угодно. Впрочем, результат запроса "show..." выглядит не очень удобным для восприятия. Поэтому надо наш код немного подправить – добавить в место вывода значений на экран обработку концов строк:

 for ($i=0; $i<$f; $i++) {
  $value=preg_replace("/(\r?\n)+/i","<br>",$line[$i]);
  echo "<td>$value</td>";
 }

Результат (на странице http://dn.ir2.ru/mysql1.php) выглядит намного приличнее. Но чего-то всё-таки не хватает. Например, что делать, если нужен запрос на обновление? Ну, то есть понятно, что если вы сейчас его быстро-быстро запустите, просто-напросто получите ошибку "у пользователя dntest нет прав". Но на своём-то хостинге при работе с базой данных вы можете назначить любые права. И запрос выполнится, но количество затронутых им строк вы не узнаете (mysql_num_rows тут не поможет).

Второй вопрос: как быть с группой запросов? Ведь часто надо перед выполнением основного запроса, например, выбрать базу данных. А PHP сам в функции mysql_query("...") не делит запрос на части, а всегда рассматривает его как один. Поэтому если вы введёте что-то вроде "show create table `img`; select * from img limit 0,1", получите ошибку.

Кстати, о limit: если вы нечаянно запустите запрос "select * from img", то можете и не дождаться его окончательного вывода на экран – в таблице img иногда бывает до 10М картинок. Так что выражение "limit 0,30" надо обязательно добавлять в конце каждого запроса, если пользователь сам не указывает limit. Добавление же limit порождает ещё одну проблему – подсчёта строк запроса (общего количества, а не "отмеренного" пользователем с помощью limit).

Далее, таблицы сайтов обычно содержат HTML-данные для страниц – как их выводить: как видит пользователь в браузере или в виде исходного кода? Ответ неочевиден, так как запросы бывают разного назначения: в случае приведённого выше "show create table `img`" как раз понадобился именно HTML (мы его сами добавили при выводе на экран). Следовательно, у пользователя должен быть выбор. То же и с разделителями запросов (пользователю бывает необходимо заменить стандартный ";" на что-то другое).

Украшение функции вывода результатов на экран

Но вначале надо доработать код, который уже есть. Украсить его – в смысле, не "приукрасить", а наоборот, сделать более красивым, убрать лишнее. Там есть один сомнительный момент – "рисование" шапки таблицы с помощью mysql_field_name. Ведь функция mysql_fetch_array возвращает массив, в котором могут содержаться наименования полей (если функцию использовать с константой MYSQL_ASSOC, а не MYSQL_NUM, как у нас). Сложность возникнет только со временем, последовательностью вывода на экран. Но её можно обойти, не выводя сразу все получаемые в цикле значения на экран, а помещая их в переменные (используя обыкновенную логику). Примерно так:

 $num=0;
 $head="<table><tr><th>N</th>";
 while ($line = mysql_fetch_assoc($r)) {
  $num++;
  $str="<tr><td class=\"num\">$num</td>";
  foreach ($line as $key=>$val) {
   $val=preg_replace("/(\r?\n)+/i","<br>",$val);
   $str.="<td>$val</td>";
   if (1==$num) {
    $head.="<th>$key</th>";
   }
   else $head="";
  }
  $head.="$str</tr>\n";
  print $head;
 }
 print "</table>";

Результат – на странице http://dn.ir2.ru/mysql2.php. Код стал короче и красивее. Правда, вы не сможете использовать эту страницу для тестирования, так как там значение переменной $sqlpost (стр.9) по умолчанию установлено в "1", а при таком значении (в любом из файлов примеров данной статьи) выводится код PHP, а не пользовательская страница (стр. 13-18 кода).

Добавление некоторых удобств

Для обработки нескольких запросов, присланных вместе, нужно сначала разделить эти запросы. Мы передаём из формы в качестве разделителя не символ, а его код (а то некоторые символы, например, табуляция, не видны на экране), по умолчанию – 59 (точка с запятой). Но мы учитываем для разделения запросов не один символ, а в сочетании с концом строки. Поэтому выражение для разделения запросов получается достаточно сложное: "/\;\s*(\r?\n)+/i".

Для вычисления общего количества строк (без limit) в функцию добавлены две переменные (они инициализируются в файле configbase.php):

$calc="SQL_CALC_FOUND_ROWS";
$foundrows="SELECT FOUND_ROWS()";

Всё остальное достаточно очевидно, да и в коде есть комментарии: http://dn.ir2.ru/mysql3.php. Для изучения работы разных типов запросов можно запускать этот скрипт на другом сайте – http://soft.infodisk.info/mysql3.php, там у пользователя mysql полные права на тестовую базу данных (только сайт infodisk.info не всегда доступен).

Добавлена лишь одна неочевидная "оптимизация" – исключено использование mysql_num_rows, так как функция mysql_affected_rows более универсальна: она выдаёт количество затронутых строк для любых запросов – и на выборку, и на обновление (добавление, удаление).

И последнее. Как заменить латинские наименования полей таблицы на русские, подходящие для пользователя (а не разработчика)? Тут, к сожалению, не могу сказать ничего утешительного: всё придётся делать руками. Сначала создать таблицу Mysql (посмотреть, как она выглядит, можно, запустив на странице mysql3.php запросы "show create table fields_ru" и "select * from fields_ru", не забудьте поставить галочку HTML). Вроде бы ничего сложного. Но надо добавить запись о каждом поле всех выводимых таблиц (это можно сделать и скриптом), а потом каждому латинскому (английскому) наименованию сделать подходящий с вашей точки зрения перевод.

Когда таблица готова, нужно включить несколько дополнительных строк кода в файл конфигурации сайта:

$fields_ru=array();
$r=mysql_query("select * from `fields_ru`");
$n=mysql_num_rows($r);
if ($n) {
 while ($line = mysql_fetch_array($r, MYSQL_ASSOC)) {
  $f=$line["name"];
  $fr=$line["ru"];
  if (trim($fr)) $fields_ru["$f"]=$fr;
 }
}

После чего на каждой странице сайта будет доступен массив $fields_ru, и в коде наших примеров можно будет заменить латинские наименования на русские:

строчку $head.="<th>$key</th>"; надо будет заменить на $head.="<th>{$fields_ru["$key"]}</th>";

Не забудьте также добавить в функцию sqlres строчку global $fields_ru.

© 2009, «Деловая неделя», Михаил Гутентог

Читать все комментарии (6)

85. D.M., admin

Менее идеальный скрипт

На странице http://dn.ir2.ru/mysql4.php добавлено одно излишество – выбор языка для отображения названий полей, но зато там добавлена также одна очень необходимая вещь – история sql-запросов. Можно выбрать из списка любой старый запрос и вставить его щелчком по кнопке ins в поле для отправки. Это порождает целую кучу технических проблем, решение которых надо описывать в отдельной статье.

12.10.2009 21:36:17

479. D.M., admin

Менее идеальный скрипт

На странице http://dn.ir2.ru/mysql4.php добавлено одно излишество – выбор языка для отображения названий полей, но зато там добавлена также одна очень необходимая вещь – история sql-запросов. Можно выбрать из списка любой старый запрос и вставить его щелчком по кнопке ins в поле для отправки. Это порождает целую кучу технических проблем, решение которых надо описывать в отдельной статье.

12.10.2009 21:36:17

480. D.M., admin

Тот же скрипт доступен и на http://infodisk.info/mysql4.php – там, как говорилось в статье, можно выполнять запросы на обновление.

12.10.2009 21:52:29

86. D.M., admin

Тот же скрипт доступен и на http://infodisk.info/mysql4.php – там, как говорилось в статье, можно выполнять запросы на обновление.

12.10.2009 21:52:29

141. Alexander

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

16.08.2010 16:11:40

142. D.M., admin

Alexander,

ну, что делать. В чём-то вы правы. "Чудовищные" – возможно, не буду спорить. "Понадёргано" – неправда, всё только моё. "Не тестировано" – верно отчасти: вначале всё было хорошо, но потом какой-то недоброжелатель (более энергичный, чем вы)вставил мне в тестовую таблицу 2Г, после чего я запретил "insert", и теперь скрипты mysql4.php действительно не работают.

Но главное - всё это вчерашний день. Я уже говорил, что через месяц-другой большинство моих скриптов (и идей) начинают казаться мне говнокодом. Ничего страшного в этом нет. Хуже, если вы любуетесь своими скриптами трёхлетней давности.

Последняя идея по визуализации (и редактированию) БД - MySQL mini editor, описывать пока лень, но там есть небольшой help.

P.S. Ну, и я с удовольствием перейду по вашей ссылке на страницу с описанием хорошего, красивого PHP-кода. И даже оставлю там комментарий, если код понравится.

17.08.2010 02:25:40

Добавить комментарий:

*Автор:
E-Mail:
*Текст: