Главная страница «Первого сентября»Главная страница журнала «Информатика»Содержание №23/2008


Педагогический университет

Основы web-программирования для школьного “сайтостроительства”. Лекция 7.

Соображаем на троих: MySQL + PHP + Javascript

Здравствуйте, уважаемые коллеги! Сегодня нам предстоит пройти непростой, но, надеюсь, интересный путь.

В реальных проектах чаще всего приходится иметь дело не с одной технологией, а с целым комплексом. Некий опыт такой интеграции мы уже имеем. Вспомните: начинали мы с HTML+PHP. В пятой лекции была задействована СУБД MySQL. Сегодня же нам потребуется использовать возможности Javascript. Скажу сразу: знакомство с Javascript не входит в программу нашего курса. Желающим углубить свои познания в технологиях клиентского программирования на Javascript могу еще раз посоветовать (я уже делал это в первой лекции) познакомиться с материалами А.А. Дуванова. Это лучшее, что можно порекомендовать учителю. (Уж поверьте: что касается материалов по web-программированию, я читал все или почти все.)

Ну, начнем… Начнем мы с того, на чем закончили предыдующую лекцию, — с визуализации страницы журнала на web-странице. Помните?

Рис. 1

Учебный план

№ газеты

ЛЕКЦИЯ

17/2008

Лекция 1. “Пролетая над миром web-программирования”. Границы возможностей статического HTML. Два мира, две системы: программирование на стороне клиента и программирование на стороне сервера. Идем от реальности: текущие предложения хостинг-провайдеров. Инструментарий: пакет Denwer, установка. Первая программа на PHP “Здравствуй, мир web-программирования”.

18/2008

Лекция 2. Пример задачи автоматизации: доска объявлений школьного сайта. SSI, основные возможности и директивы. SSI-версия школьной доски объявлений. PHP-версия доски объявлений. Совершенству нет предела: выводим все объявления из каталога, помечаем важные. Передача параметров в программы на PHP. Обзор синтаксиса PHP: типы данных, операции, основные алгоритмические конструкции.

19/2008

Лекция 3. Обработка форм на стороне сервера. Формы и элементы управления HTML: однострочное и многострочное поля ввода, флажки, радиокнопки, списки. Методы GET и POST, кодирование URL. Обзор синтаксиса PHP: функции, массивы.

20/2008

Лекция 4. Усовершенствованная доска объявлений с разделом администратора. Обзор синтаксиса PHP: файлы. Законченный мини-проект: административный интерфейс для доски объявлений на файловом “движке”.

Контрольная работа № 1. Разработка теста с обработкой результатов тестирования на стороне сервера.

21/2008

Лекция 5. Введение в использование баз данных в задачах web-программирования. Зачем нужна СУБД? Теория реляционных баз данных: основные термины. SELECT — “главная команда” SQL. Что такое MySQL? Взаимодействие с сервером MySQL из программ на PHP.

22/2008

Лекция 6. База данных “Страница школьного журнала”: от проектирования до визуализации. Практическое применение базы данных для автоматизации школьного сайта и школьной жизни.

Контрольная работа № 2. Доработка базы данных “Страница школьного журнала”.

23/2008

Лекция 7. “На троих”. Задействуем трех основных “игроков”: MySQL+PHP+Javascript. Проверка данных форм на стороне клиента перед отправкой на сервер.

24/2008

Лекция 8. Рисование средствами PHP. Генерирование графических данных “на лету”. Построение графиков и диаграмм. Графическая визуализация данных школьного журнала.

Итоговая работа

Во второй контрольной работе вам было предложено дописать код для вывода правой таблицы — правого “разворота” журнала. Надеюсь, вы с этой задачей успешно справились.

Вы, конечно, помните, что базу данных этого журнала мы заполняли “руками”, посредством phpMyAdmin. Для целей отладки это было вполне приемлемо, но нельзя же ожидать, что учителя — пользователи нашего журнала будут использовать его так же. Тогда уж лучше по-старинке, на бумаге! Наша задача — создать для них удобный интерфейс для выполнения типичных операций в журнале. К таковым можно отнести две — запись уроков и заполнение клеточек журнала. Можно было, конечно, начать с записи уроков, но… Честно говоря, я, конечно, реализовал обе задачи. И понял, что разобрать две в рамках лекции не получится из соображений объема, а содержательность задачи заполнения клеточек журнала существенно выше (правда, и сложность тоже…). Поэтому мы рассмотрим именно операцию заполнения клеточек журнала.

Что значит удобный интерфейс? Ох… я перечитал на эту тему массу книг и многое из них почерпнул.
Но еще я много лет проектировал и программировал интерфейсы программ, в том числе и web-интерфейсы. И некоторые из них переделывал (иногда принципиально, с нуля!) раз по N (в ряде случаев N было двузначным). Окончательную оценку интерфейсу выставляет пользователь и… некоторое время. Время важно потому, что переход на новый интерфейс практически у всех сопряжен с неудобствами. Даже если старый вариант интерфейса всех “достал” и на каждом шагу поминается недобрым словом, он “родной” и привычный. В первый момент даже очень опытные и дисциплинированные пользователи нередко упрямятся и капризничают. Поэтому и нужно некоторое время, чтобы оценить возможности интерфейса, обеспечиваемую им производительность и надежность. Мы не имеем возможности поставить соответствующий эксперимент, поэтому я предложу вариант интерфейса, который с колокольни моего опыта кажется удобным.

Ключевая идея: возьмем привычную визуализацию журнала и в каждую клеточку поместим кнопку. Вот так:

 

Рис. 2

Для заполнения клеточки журнала оператор (учитель) должен нажать на кнопку в этой клеточке (фактически на саму клеточку). При этом открывается отдельная страница редактирования содержимого данной клеточки. Нажмем, например, на клеточку “2/3” (Грейпфрут Гарик, урок 3/09).

 

Рис. 3

Вы видите, что на этой странице можно отметить пропуск в разделе “Был/не был”, а также отредактировать оценки — поставить новую и удалить существующую. Важно, что поставить оценку можно лишь в том случае, если ученик присутствовал на уроке. Если мы отметим пункт “Пропустил урок”, страница немедленно перезагрузится и станет такой:

 

Рис. 4

При этом для обеспечения непротиворечивости базы данных будут удалены и все оценки, полученные учеником за данный урок (если они есть). Поэтому, если мы снова отметим, что Гарик был на данном уроке, оценок у него за этот урок уже не будет.

 

Рис. 5

Я попросил нескольких коллег-учителей “поиграть” с этим интерфейсом, и они оценили его как вполне приемлемый. Приступим к реализации!

Взаимодействие скриптов

Спроектированный интерфейс состоит из двух файлов. На рис. 2 показан результат выполнения скрипта edit.php. Фактически это та же визуализация журнала, которой мы закончили предыдущую лекцию и с которой начали эту, только с активными кнопочками в клетках. При нажатии на каждую кнопочку (что бы на ней не было написано — ничего, буква “н” или оценки) мы переходим на страницу редактирования клеточки, которая реализована скриптом cell.php. При этом в скрипт cell.php передаются два параметра, определяющих конкретную клеточку, — номер ученика и номер урока.

Прежде всего скопируйте скрипт, рассмотренный в предыдущей лекции (он наверняка у вас есть — вы обязательно должны были набрать его, если не при изучении лекции, то при выполнении контрольной работы), в файл с именем edit.php, модификацией которого мы сейчас займемся.

Узнай, где у него кнопка!

Где кнопка… где кнопка… узнать бы, где он сам!

“Приключения Электроника”

Для вывода ячейки — клеточки журнала в скрипте, рассмотренном на предыдущем занятии, использовался простой код:

//И, наконец, выведем, что у нас накопилось в $td

echo "<td align='center'>$td</td>";

Для вывода в этом месте кнопки мы заменим этот оператор на следующий:

echo "<td align='center'>

<input type='button' value='$td'

onclick='document.location=\"cell.php?student_id=$st_id&lesson_id=$l_id\"'></td>";

Здесь вместо одного строкового значения переменной $td в ячейку таблицы помещается кнопка, кодируемая тегом input c параметром type='button'. Надпись на кнопке задается параметром value='$td', но самым важным является то, что при нажатии на кнопку срабатывает обработчик события onclick и посредством короткого кода на Javascript производится переход на страницу cell.php с параметрами student_id=$st_id и lesson_id=$l_id.

Здесь мне пришлось использовать один технический прием, который часто применяется в PHP: экранирование кавычки внутри строки. Суть его в следующем: если внутрь строки, ограниченной, например, двойными кавычками, требуется поместить двойную кавычку, то ее необходимо экранировать символом “слеш”. Вот так, например, "Курс \"Основы web-программирования\"".

На этом изменения в файле edit.php закончены. Далее мы перейдем к рассмотрению файла cell.php — основного скрипта этой лекции, но сначала необходимо познакомиться с теми средствами MySQL и Javascript, которые нам понадобятся. Их довольно много, но среди них нет ни сложных, ни экзотических. Напротив: я постарался собрать в этом примере те дополнительные средства, которые на практике чаще всего бывают полезны.

Джентльменский набор операторов SQL: SELECT+INSERT+DELETE+UPDATE

Помните, в прошлой лекции мы дали пользователю webuser права выполнять в базе данных page операции SELECT, INSERT, DELETE и UPDATE? Не помните? Обязательно освежите в памяти соответствующий фрагмент лекции 6. А я сейчас расскажу, зачем пользователю webuser такие права.

Какие типичные операции приходится чаще всего выполнять в базе данных? Наиглавнейшая операция (по частоте использования), конечно, выборка. В языке SQL для этого есть команда SELECT. Но, чтобы было что выбирать, надо сначала данные добавить. Мы это делали “руками”, но даже в этом случае для добавления данных использовалась команда INSERT. Наконец, время от времени данные приходится удалять. Это можно сделать посредством команды DELETE. В принципе этими командами можно, наверное, было и ограничиться, но нередко возникает потребность в изменении данных. Изменять данные, каждый раз удаляя старые и добавляя новые, — занятие, мягко говоря, неэффективное. Поэтому для этого есть специальная команда UPDATE.

Перечисленные команды INSERT, DELETE и UPDATE имеют различные синтаксические формы, но я в каждом случае ограничусь рассмотрением одной — наиболее наглядной.

Команда INSERT

Наиболее наглядный (на мой вкус) синтаксис команды INSERT следующий:

INSERT INTO имя_таблицы SET имя_поля=значение, имя_поля=значение,…

Например, если нам захочется добавить еще одного ученика в таблицу student, это можно сделать посредством следующей команды:

Рис. 6

С другим синтаксисом команды INSERT вы можете познакомиться, например, заглянув в файл sampdb.sql, который использовался в лекции 5.

Команда DELETE

Команда DELETE — страшная штука! С ее помощью можно, в частности, “снести” все данные в таблице и глазом не моргнуть. Вот так, например (НЕ ДЕЛАЙТЕ ЭТОГО!):

DELETE FROM student

Как правило, удалять приходится все же не все данные в таблице, а конкретную запись/записи. Поэтому чаще всего команда DELETE используется с условием WHERE. Вот так:

Рис. 7

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

Команда UPDATE

Если вы заполнили таблицу lesson, основываясь на моем примере, домашнее задание после первого урока в ней выглядело, как “затычка”: “Параграф такой-то, задание такое-то”. Давайте изменим его на более пристойное посредством команды UPDATE:

Рис. 8

Как и команда DELETE, UPDATE чаще всего используется с условием WHERE. Без этого условия она модифицирует все строки таблицы (что иногда даже хуже, чем DELETE!).

Полезный ключик AUTO-INCREMENT

Вернемся к команде INSERT и добавим в таблицу grade еще одну оценку. Допустим, поставим Гарику (student_id=4) пятерку за второй урок (lesson_id=2). Для этого нам сначала надо выяснить идентификатор последней оценки в таблице grade:

Рис. 9

И теперь, зная идентификатор последней оценки, можно добавить новую (используя следующий идентификатор):

Рис. 10

В ряде случаев нам действительно нужно знать идентификатор добавляемой записи. Например, номер ученика в журнале — важный параметр, и при добавлении нового ученика его хочется задавать явно. Но вот номер оценки… Зачем нам эта лишняя головная боль — смотреть номер последней оценки, высчитывать следующую… Было проще просто добавить оценку, а какой у нее должен быть номер, пусть решает сама база данных. Для этого у ключевого поля можно указать специальное свойство AUTO_INCREMENT (русскоязычный Access называет это “счетчик”). Вот, смотрите, что и как нужно сделать.

Первое: надо кликнуть по таблице grade и нажать на кнопку “Изменить” (карандаш) в поле grade_id.

Рис. 11

Второе: в разделе “Дополнительно” надо выбрать пункт auto_increment и нажать на кнопку “Сохранить”.

Рис. 12

Третье: все сделано!

Рис. 13

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

Рис. 14

Шапка-невидимка: поля hidden в формах

Еще один важный вспомогательный вопрос, который нам надо рассмотреть, — поля hidden в формах. Вспомним материалы лекции 3, в которой мы рассматривали различные типы элементов управления. Среди них не было элемента управления input с параметром type=hidden, поскольку в тот момент я не мог привести наглядного и полезного примера, в котором бы такие элементы понадобились. Теперь, наконец, мы добрались до соответствующего материала. Поля типа hidden, как правило, имеют следующий вид (я сразу привожу пример, применительно к нашей задаче):

<input type='hidden' name='student_id' value='1'>

Особенность этих полей заключается в том, что они никак не визуализируются браузером и не видны пользователю, но данные из них передаются вместе со всеми данными формы. Нам эти поля понадобятся для формы добавления новой оценки известному нам ученику за известный нам урок.

И снова шапка-невидимка: параметр display в языке CSS

Это короткий и чисто технический вопрос, который я хочу напомнить перед рассмотрением кода скрипта cell.php. Посредством CSS можно спрятать или показать блок на HTML-странице. При этом в CSS имеются два похожих свойства: visibility и display. Посредством первого можно только прятать блок (место для него при этом резервируется), посредством второго — вообще временно убрать блок со страницы. Я использовал свойство display (в данном случае это дело вкуса). Если свойство display имеет значение block, блок виден, если значение none — не виден, убран со страницы.

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

“Граждане, прошедшие регистрацию, приглашаются на посадку через зону спецконтроля”

Переводим: данные из заполненных элементов формы отправляются на сервер через функцию onsubmit.

Обработчик onsubmit в реальных проектах играет важную роль при взаимодействии клиента и сервера. Простой пример: вы случайно поставили ученику за урок оценку 0 (я допускаю, что бывают и такие дети, но, формально говоря, нули все же ставить нельзя). Конечно, можно в серверном скрипте, получив такую оценку, не ставить ее, а выдать пользователю соответствующее сообщение. Но еще разумнее просто не отправлять на сервер данные, которые являются заведомо неправильными, и этот факт может быть установлен на стороне клиента. Для этого и используется обработчик onsubmit.

Он вызывается до отправки данных формы на сервер и может делать/проверять что угодно, но обязан вернуть логическое значение true или false. Если onsubmit вернет true, данные будут отправлены, если false — не будут.

Проверить допустимость оценки (она должна быть от 2 до 5) мы способны и на стороне клиента, для этого не нужно отправлять данные на сервер. Это мы и будем делать в нашем примере.

Код скрипта cell.php — главного скрипта этой лекции

<?php

//Соединимся с базой данных (имя и пароль мы задали в прошлой лекции)

mysql_connect("localhost", "webuser", "123456789");

mysql_select_db("page");

 

//В переменной $present могут быть переданы значения 0 или 1

// 0 -- ученик пропустил данный урок

// 1 -- ученик был на данном уроке

if (isset($present)) {

//Если ученик был на данном уроке,

//уберем отметку о пропуске из таблицы absence

if ($present) mysql_query("delete from absence

where student_id=$student_id and lesson_id=$lesson_id");

else {

//Если же ученик пропустил урок...

// ... уберем все его оценки за данный урок из таблицы grade

mysql_query("delete from grade

where student_id=$student_id and lesson_id=$lesson_id");

// ... добавим отметку о пропуске в таблицу absence

mysql_query("insert into absence

set student_id=$student_id, lesson_id=$lesson_id");

}

}

if (isset($grade)) {

//Переменная $grade будет определена в том случае,

//если мы ставим ученику оценку за урок. В этом случае добавим

//новую запись в таблицу grade

mysql_query("insert into grade

set grade=$grade, student_id=$student_id, lesson_id=$lesson_id");

}

//Переменная $delete будет определена в том случае,

//если мы удаляем оценку ученика за данный урок

if (isset($delete)) {

mysql_query("delete from grade where grade_id=$delete");

}

//Функция DayMonth использовалась в предыдущей лекции.

//Она преобразует вид даты к более привычному

function DayMonth($dt) {

return substr($dt,8,2)."/".substr($dt,5,2);

}

?>

<html>

<head>

<meta http-equiv="content-type" content="text/html; charset=windows-1251">

<title>Редактирование клеточки страницы классного журнала</title>

<style>

h1,h2 {color:gray;}

</style>

<script type="text/javascript">

//Функция check() используется в обработчике onsubmit для

//проверки допустимости оценки

function check() {

if ((document.forms["fgrade"].grade.value>="2")&&

(document.forms["fgrade"].grade.value<="5")) return true;

else {

alert("В качестве оценки допустимы только цифры от 2 до 5");

return false;

}

}

</script>

</head>

<body>

<h1>Редактирование клеточки страницы классного журнала</h1>

<?php

$result=mysql_query("select student_id,name,surname

from student where student_id=$student_id");

$student=mysql_fetch_assoc($result);

$result=mysql_query("select lesson_id,lesson_date,subject

from lesson where lesson_id=$lesson_id");

$lesson=mysql_fetch_assoc($result);

echo "<p>Ученик: <b>".$student['name']." ".$student['surname'].

"</b> (№ ".$student['student_id']." в журнале)";

echo "<p>Урок: <b>".DayMonth($lesson['lesson_date']).

"</b> по теме <i>".$lesson['subject']."</i>";

?>

<h2>Был/не был</h2>

<?php

$result=mysql_query("select * from absence

where student_id=$student_id and lesson_id=$lesson_id");

if (mysql_fetch_assoc($result)) {

$wasnt="checked";$was=""; //Эти переменные используются, чтобы

//отметить соответствующую радиокнопку

$grade_display="none"; //Эта переменная определяет видимость

//блока с оценками

} else {

$was="checked";$wasnt="";

$grade_display="block";

}

echo "<p><input type='radio' name='absence' value='0' $was

onclick='document.location=\"cell.php?present=1&

student_id=$student_id&lesson_id=$lesson_id\"'>

&nbsp;Был на уроке";

echo "<p><input type='radio' name='absence' value='0' $wasnt

onclick='document.location=\"cell.php?present=0&

student_id=$student_id&lesson_id=$lesson_id\"'>

&nbsp;Пропустил урок";

 

//Ниже -- блок с оценками. Он может быть виден или не виден

echo "<div id='grades' style='display:$grade_display'>";

?>

<h2>Оценки за урок</h2>

<form name="fgrade" action="cell.php" onsubmit="return check();">

<?php

//Два спрятанных поля, которые не видит пользователь

echo "<input type='hidden' name='student_id' value='$student_id'>";

echo "<input type='hidden' name='lesson_id' value='$lesson_id'>";

?>

<table border="1">

<?php

$result=mysql_query(«select grade_id,grade from grade

where student_id=$student_id and lesson_id=$lesson_id");

while ($grade=mysql_fetch_assoc($result)) {

echo "<tr>";

echo "<td>".$grade['grade']."</td><td><input type='button' value='Удалить'

onclick='document.location=\"cell.php?delete=".

$grade['grade_id']."&student_id=$student_id&lesson_id=$lesson_id\"'>

</td>";

echo "</tr>";

}

?>

<tr><td><input type='text' name='grade' size='2'></td><td>

<input type='submit' name='sent' value='Поставить'></td></tr>

</table >

</div>

<p><a href="edit.php">На страницу журнала</a>

</div>

</form>

</body>

</html>

  Вопросы и задания

1. Лекции у нас получаются очень насыщенными. Поэтому, как и ранее, я настоятельно прошу вас выполнить в обязательном порядке только одно, зато самое важное задание. Пожалуйста, скачайте на странице курса в разделе “Учебные материалы” комплект файлов, рассмотренных в лекциях 6 и 7. (Обратите внимание на то, что часть, отвечающая за визуализацию правого разворота журнала, в них отсутствует, поскольку еще не вышел срок для отправки второй контрольной работы.) Установите у себя скрипты и тщательно разберите их.

2*. Второе задание к этой лекции “со звездочкой”. В начале лекции я упомянул о двух типичных операциях, которые приходится выполнять учителю: записывать уроки и выставлять оценки. Со вторым мы разобрались. А с первым вы можете попробовать разобраться самостоятельно. Спроектируйте web-интерфейс для записи уроков в наш журнал. Весь технический инструментарий для этого у вас имеется.

Се. Ль. Островский

TopList