ТаблицейБлоками.

Приложение Д: javascript php файл скрипт HTML

Последняя модификация: 10.08.2014 г

Страница загружена с адреса: http://webdesign.site3k.ru/conjuncture/append/d/price2.html

Моя студия веб-дизайна

Обработка баз данных сервером

  1. Считывание данных в WEB-страницу
  2. Фильтрация данных
  3. Окончательный вариант исходного кода
  4. Браузеры без JavaScript

Считывание данных в WEB-страницу

CSV-Файлы получили настолько широкое распространение, что их поддержку включили даже в PHP. Так что, можно обрабатывать на сервере те же базы данных, что были предназначены для обработки браузером посетителя (см. «Обработка баз данных браузером посетителя»).

Для обработки CSV-файлов в PHP введена функция:

fgetcsv($FILE, LEN, DELIM)

Эта функция читает одну строку из файла, заданного дескриптором $FILE, с максимальной длиной, заданной параметром LEN, разбивает ее по символу DELIM (параметр DELIM должен обязательно состоять из одного символа, в противном случае принимается во внимание только первый) на отдельные элементы, и возвращает их в массив. Таким образом, получивший значение, возвращаемое функцией fgetcsv, массив содержит 1 запись из базы данных, готовую к дальнейшей обработке и ее не приходится дополнительно делить на поля с помощью функций explode() или split(). После считывания строки, позиция в файле меняется, так, что при следующем считывании, функция прочтет следующую строку1.

Функция возвращает false, если строки кончились. Пустые строки в файле не игнорируются, а возвращаются как список из одного элемента – пустой строки.

Используя функцию fgetcsv, напишем простой PHP-скрипт для считывания CSV-файла (по сути, самого обычного текстового файла, в котором элементы разделены неким, удобным для нас символом):

<!--Таблица, как в Обработка баз данных браузером посетителя -->
<table id="TabProduct" border=1 rules=all cellspacing=0 cellpadding=0 style="background:#FFFFF0">
  <CAPTION><span id="coment"><EM>Продажа кондиционеров и сплит-систем с доставкой и установкой</EM></span></CAPTION>
  <COL style='white-space:nowrap'><COL align=center><COL align=center><COL align=center><COL align=center><COL align=right>
  <thead>
     <tR style="background:#ccffcc">
        <tH>Модель
        <tH>Холод (кВт.)
        <tH>Тепло (кВт.)
        <tH>Мощность (Ват)
        <tH>Площадь (М<sup>2</sup>)
        <tH>Цена ($)
    </tr>
  </thead>
  <tbody>

<?php // во внутрь тела таблицы помещаем PHP-скрипт, который наполнит ее содержанием
$filename = "price.csv"; // Занесем имя файла в переменную
if (is_readable($filename)) { // если файл читается
  $f = fopen($filename,"r") or die("Ошибка открытия базы"); // Открываем файл и заносим его указатель в переменную
  for($i=0; $data=fgetcsv($f, 1000, ";"); $i++) // Читаем по одной строке из файла, занося значения в переменную $data, пока fgetcsv не вернет False
  {
     if($i != 0) {  // Если это не первая строка, в которой содержится название столбцов,
        list($mark, $model, $cool, $hot, $power, $place, $price) = $data; // перебросим массив в переменные (можно было бы обращаться к элементам массива как $data[1], $data[2] и т. д., но удобней по именам
                 echo "<tr><TD>$model<TD>$cool<TD>$hot<TD>$power<TD>$place<TD>$price</tr>\n";
 // добавим на страницу HTML-код, содержащий ячейки таблицы, со значениями из базы данных
                @$kolichectvo++ ; // подсчитаем количество записываемых строк
     }
  }
  fclose($f); // закроем файл по окончании чтения
} 
?>

  </tbody><!--Закончим таблицу -->
</table>

<SCRIPT LANGUAGE='JavaScript'><!-- // запишем в заголовок таблицы
if (<?=@$kolichectvo ?> < 1)
 {coment.innerHTML="<B><font color=red>Кондиционеров с указаными параметрами не найдено</font></b>";}
else
 {coment.innerHTML='<B>Отобранно '+ <?=@$kolichectvo ?> + SetString(<?=@$kolichectvo ?>) +'</b>' ;} 
// для корректного подбора склонения слова «Модель», используем функцию SetString(), описанную в «Обработка баз данных браузером посетителя»
//--></SCRIPT>

Фильтрация данных

Добившись отображения на странице данных из CSV-файла, можно добавить к ним фильтры.

В начале создадим для них элементы управления:

<FORM name="frm" ACTION="<?=$_SERVER["SCRIPT_NAME"]?>" method="get">
// пусть сервер сам впишет путь и название файла. Мало ли куда его засунем?
<table>
<TR><td>Цене до...($)<TD><input type="TEXT" size="5" name="edit1" value="<?=@$edit1 ?>" title="Выбрать кондиционеры и сплит-системы по цене"></TR>
// пусть сервер впишет value, чтобы при обновлении, оно не менялось
<TR><TD>Площади от... (м<sup>2</sup>)<TD><input type="TEXT" size="5" name="edit2" value="<?=@$edit2 ?>" title="Выбрать кондиционеры и сплит-системы по площади"></TR>
<TR><TD>Фирме-производителю<TD>
  <select name="menu"> 
 <option selected>Все
 <option>Akai
 <option>Akira
 <option>Fujitsu 
 <option>Hitachi
 <option>Midea
 <option>Mitsubishi
 <option>Samsung
 <option>Sharp
 <option>Toshiba
 <option>Yoko
  </select>
  </tr> 
</table>

<p style="text-align:center">
<INPUT TYPE='submit' VALUE='Показать' title="Отфильтровать кондиционеры и сплит-системы">
// по нажатию этой кнопки значения полей будут переданы серверу в HTTP-запросе, после вопросительного знака
<a href="<?=$_SERVER["SCRIPT_NAME"]?>" style="TEXT-DECORATION:none"><input type='reset' value='Снять фильтр' onClick="window.location ='<?=$_SERVER["SCRIPT_NAME"]?>'; return false;" title="Показать все кондиционеры и сплит-системы"></a>
// поскольку INPUT TYPE RESET только переводит к исходному значения полей, но не перезагружает данные, для снятия фильтра нужно считать базу заново
// реакция на действие мыши и помещение кнопки во внутрь ссылки применяется для того, чтобы сработало наверняка (были прицеденты)
<P>&nbsp;</P>
<P>&nbsp;</P>
</FORM>

Если на вашем сервере GET-переменные скриптам недоступны, вместо использования переменных $edit1, $edit2 и т. д., следует использовать массив GET-переменных. Например: $_GET['edit1'], $_GET['edit2'], $_GET['menu'].

 

Теперь можно усовершенствовать код PHP, чтобы он учитывал посылаемые ему фильтры. Вместо простого вывода на страницу, сразу за функцией LIST, добавим условия, при которых должен происходить вывод:

        if(!$menu || UrlDecode($menu) == 'Все' || $mark == UrlDecode($menu)) {
           if (!$edit1 || $price <= UrlDecode($edit1)) {
              if (!$edit2 || $place >= UrlDecode($edit2)) {
                 echo "<tr><TD>$model<TD>$cool<TD>$hot<TD>$power<TD>$place<TD>$price</tr>\n";
                 @$kolichectvo++ ;
              }
           }
        }

Код проверяет, не является ли переменная пустой (и тогда проверяет следующую строку), если она не пустая, равна ли она, после перекодировки (а все неанглийские символы кодируются при передаче параметров в HTTP-запросе) значению «Все», что так же, как и пустота переменной, означает переход к следующей строке, или может быть (опять же, после перекодировки, на всякий случай), равна марке из базы данных. При несовпадении ни одного из условий, строка из базы не выводится на страницу и начинается следующий цикл, с чтением следующей строки. Если одно из условий выполняется, начинается следующая проверка: не установлена ли переменная edit1, а если установлена, то не будет ли она меньше или ровна чем цена в базе. Если условие не выполняется, считывается следующая строка, если выполняется, начинается последняя проверка: задано ли ограничение по площади (edit2) и не соответствуют ли ему данные из базы.

В конце концов, пройдя три уровня фильтров, данные выводятся на страницу. Если мы выберем фильтр, задав торговую марку, предел цены, минимум расчетной площади и нажмем на кнопку «Показать», отфильтрованные данные отобразятся в таблице, заголовок которой покажет их точное количество. Поле для ввода цены, благодаря конструкции value="<?=@$edit1 ?>", вставляющей на место <?=@$edit1 ?> результат работы PHP-кода (а он просто впишет туда значение переменной edit1, полученной до обновления данных) будет отображать максимальное значение цены, а поле для ввода площади, по аналогичной причине, будет отображать минимальную площадь… Вот только список, почему-то, будет показывать «Все», несмотря на выбор конкретной торговой марки.

Происходит это потому что, после посылки формы, страница перегружается, и все ее переменные устанавливаются в исходное состояние. Конструкция value="<?=@$edit1 ?>" позволяла сохранить значения полей ввода, принудительно назначая их новой версии страницы. Аналогично можно было бы поступить и с выпадающим списком, каждый раз переписывая его заново с помощью PHP, но это слишком громоздко.

Удобней, на мой взгляд, сделать это, написав дополнительный JavaScript:

for(i=1;i<frm.menu.length;i++) // Нулевой и так станет по умолчанию
{
     a=frm.menu[i].innerHTML;
     if (a== "<?=UrlDecode(@$menu) ?>") 
// Можно былобы и так (найти значение переменной, передаваемой последней, в HTTP-запросе):
//document.location.search.substring(document.location.search.indexOf('menu')+5))
// но фрагмент PHP получается компактней
     {
          frm.menu.selectedIndex=i;
          break; // как только дойдем до нужного, остановить
     }
}

Конструкция, в целом не хитрая. Объявляется цикл по всему списку, так что, изменение списка не требует коррекции скрипта. Внутри цикла переменной «а» передается текст элемента списка и сравнивается со значением, выбранным ранее. Если совпало, пункт списка объявляется выбранным и цикл прекращается. Вот только Опера и Мазила с этим не работают...

Оказалось, Опера и Мазила в innerHTML включают больше текста, чем требовалось. Опера считает своим долгом добавить в него завершающий пробел, даже если он и без того есть, а Мазила – вообще, все подряд до следующего тега. Но «Hitachi» и «Hitachi » – это разные вещи. И если Эксплорер автоматически отрезает завершающие пробелы, то другие браузеры – нет. И никаких функций для этого не предусмотрено. Так что, сразу за a=frm.menu[i].innerHTML, следует добавить код, обрезающий все завершающие знаки табуляции, возврата каретки, новой строки и пробелы:

     for(e=a.length;e>0;e=e-1)
     {
          if (a.substr(e-1,1)==unescape('%09') || a.substr(e-1,1)==unescape('%0A') || a.substr(e-1,1)==unescape('%0D') || a.substr(e-1,1)==' ')
                 a=a.substr(0,e-1); // укоротить строку на символ
	  else {break;} // Прервем, как только дойдем до нормального символа (вдруг пробелы внутри строки?)
     }

Теперь выбор данных работает в любом браузере2. Выполнить сортировку, как предлагалось в «Обработка баз данных браузером посетителя» нельзя, так как PHP не обладает функциями сортировки данных для простых текстовых файлов, но в большинстве случаев фильтров вполне достаточно.

Окончательный вариант исходного кода

Поскольку вы никоим образом не сможете увидеть истинный код страницы напичканной PHP-скриптами, приведу его здесь:

А вот результат его выполнения: Смотреть.

Браузеры без JavaScript

Поскольку подавление скриптов, благодаря Windows XP SP2 стало популярным, стоит отметить что, приведенный выше код будет работать и без JavaScript, только количество отобранных записей не будет отображаться и, текущее положение в выпадающем списке, при обновлении страницы установится в положение по умолчанию. Не самое лучшее поведение, которое можно было себе представить, но эти недостатки вполне терпимы.

Но кто сказал, что их нужно терпеть?

Делать совместимую страницу, так уж делать ее совместимой по полной программе!

Но как добится сохнанения положения ввыпадающем списке без JavaScript? Неужели перед каждым пунктом писать кодпроверки на соответствие текущему положению и, в зависимости от результата, вписывать его то с "selected", то без? Ведь это громоздко и крайне неудобно, в случае необходимости редактировать меню?

Но способ есть: Если меню хранить в отдельном файле, то, с случае обработки браузером, его можно включать в страницу по <!--#include virtual="этот_файл" --> (см.  документацию по SSI-директивам). В PHP-варианте, этот файл можно открыть и добавить построчно, циклически проверяя каждую строку: не нужен ли ей атрибут "selected". Таким образом, при необходимости изменения списка (в данном случае, торговых марок), придется менять один этот файл, а не две страницы, его использующие.

Реализация функции пометки, при хранении списка опций в отдельном файле очень проста и не зависит от их количества.

Помещаем такой код, в том месте, где должен объявлятся список:


<?
if(UrlDecode(@$menu) == 'Все' || !@$menu) { // если в меню выбрано "все" или меню еще не определено
      echo "<option selected>Все\r\n";
} else {
      echo "<option>Все\r\n";
}
$filename = "price_option.inc"; // Занесем имя файла в переменную
if (is_readable($filename)) { // если файл читается
  $f=file($filename);      // считать содержимое файла в массив (автоматически откроется)
  for($i=0; $i < count($f); $i++) {
      $optvalue=substr($f[$i],strpos($f[$i],">")+1);
      if (trim($optvalue) == UrlDecode(@$menu)) {
	$thisoption="<OPTION selected>";
      }else { 
	$thisoption="<OPTION>";
      }
      echo "$thisoption$optvalue";
  }
} 
?>

Теперь хорошо бы перевести на язык PHP и счетчик записей – элементарно:


<? // запишем количество столбцов в заголовок таблицы (хоть он и расположен внизу, отобразится. как положено)
if ($kolichectvo < 1) {
  echo "<CAPTION><B><font color=red>Кондиционеров с указаными параметрами не найдено</font></b></CAPTION>";
} else  {

  function SetString($NumString) {
     if (substr($NumString,strlen($NumString)-2,1)==1) // 10 -19 моделей, 110 - 119, и т. д.
     {
	$WriteText=' моделей';
     } elseif (substr($NumString,strlen($NumString)-1,1)==1) // последний символ = 1
     {
	$WriteText=' модель'; // заканчивается на 1
     } elseif (substr($NumString,strlen($NumString)-1,1) > 1 && substr($NumString,strlen($NumString)-1,1) < 5) // заканчивается на 2 или 3, или 4
     {
	$WriteText=' модели';
     } else {
	$WriteText=' моделей'; // все остальные
     }
     return @$WriteText;
  }

  echo "<CAPTION><B>Отобранно: $kolichectvo".SetString($kolichectvo)."</b></CAPTION>";
}
?>

Поскольку количество столбцов скрипт узнает только по окончании заполнения таблицы, CAPTION придется помещать перед ее закрытием.

Это означает что, сумарную информацию мы получим под таблицей?

О, нет. Это же CAPTION, она все равно отобразится на верху!

Отстался один маленький скриптеныш – на кнопке возвращения к исходному состоянию, событие onClick. И его можно убрать, но только не везде кнопка без него будет работать. Но это можно пережить. Эту кнопку вообще можно выкинуть, а вместо нее, добавить на страницу еще одну форму:


<FORM action="<?=$_SERVER["SCRIPT_NAME"]?>" method="get"><INPUT TYPE=SUBMIT VALUE="Снять фильтр"></FORM>

Со скриптом, или с формой, нажатие кнопки «Снять фильтр» означает для браузера возврат к исходной странице, на которую не устанавливались ни какие фильтры, и он просто возьмет ее из кеша, что произойдет очень быстро. Более медленный, но более академический вариант – поместить в скрипт вторую кнопку типа SUBMIT, дав ей некое имя, напрмер «button2», а перед выводом формы, если эта кнопка нажата, очистить все переменные фильтров:


if(@$button2)
{
   $edit1="";
   $edit2="";
   $menu="";
}

Получится вот такой исходный код:

А вот результат его выполнения: Смотреть.


1 Конечно, можно было бы, и это было бы более традиционно, считать базу в массив и, затем обрабатывать массив, но при величине прайса в сотню строк (позиций), выизрыш в скорости не будет заметен, а при больших размерах... Представим себе, что, в среднем, строка прайса имеет длинну 100 символов. Стало быть, прайс в сто строк имеет размер в 10 килобайт. Если на каждом из тысячи сайтов на сервере вашего провайдера открыто по десять таких прайсов или чего-то еще, в этом духе, сто мегабайт оперативной памяти тратятся только на такие массивы. А сколько еще на всякие переменные и процессы? А хватит ли памяти на сервере? Не стоит уповать на мощьности компьютеров – следует делать экономичные программы. Файлы до килобайта, конечно, лучше считать в массив, свыше ста килобайт, лучше использовать обращение к базам, имеющим ключевые поля, базы более мегабайта, лучше обрабатывать через SQL-запросы. А файлы среднего размера (каждый сам решит, что такое средний размер), следует обрабатывать либо по ключам, либо построчно. НО НЕ СЧИТЫВАТЬ ВЕСЬ ФАЙЛ В МАССИВ! Представьте, десять посетителей на каждом из тысячи сайтов, открывших мегабайтную базу в массив – когда это делаете вы в одиночку, на своем компьютере – это ерунда, но на сервере потребуется 10 ГИГАБАЙТ оперативной памяти!

2 Вместо frm.menu[i].innerHTML можно было применить frm.menu[i].innerText, но он не будет работать в Mozilla. Другой способ – frm.menu[i].text – будет работать везде и, более того, не потребует удаления лидирующих пробелов, однако для примера специально выбран более сложный способ, чтобы, заодно, продемонстрировать полезную функцию усечения строки.


 

 

Комментарии к странице (всего 1)

 

 

 


На главную страницу сайта