su13@pochta.ru

| Первый | Второй | Третий | Четвёртый | Пятый | Шестой | Седьмой |


Глава №10.

Perl

Язык программирования Perl превратился из инструмента, используемого преимущественно администраторами Unix-систем, в наиболее распространенную платформу разработки для World Wide Web. Perl не предназначался изначально для Web, но простота его использования и мощные функции для работы с текстом сделали естественным его применение для CGI-программирования. Сходным образом, когда mSQL впервые появилась на сцене, исключительные компактность и скорость выполнения сделали ее очень привлекательной для разработчиков Web, которым требовалось обслуживать ежедневно тысячи операций. MySQL со своей высокой скоростью и расширенными возможностями стала еще более привлекательным средством для веб-разработчиков. Естественно поэтому, что был разработан интерфейс Perl к обеим базам - MySQL и mSQL, - объединив таким образом их достоинства.

В то время, когда пишется эта книга, существуют два интерфейса между Perl и MySQL с mSQL. Более ранний состоит из специализированных интерфейсов Myaql.pm и Msql.pm, которые работают только с MySQL и mSQL соответственно. Другой, более новый интерфейс является подключаемым модулем в комплекте DBI (DataBase Independent) - независимых от базы данных модулей. DBI является попыткой обеспечить общий Perl API для доступа к любым базам данных и предоставления более высокой переносимости. Интерфейс DBI стал наиболее надежным и стандартным, и разработчики MySQL рекомендуют пользоваться только DBI, поскольку дальнейшая разработка модулей Mysql.pm и Msql.pm прекращена. Однако многие унаследованные системы все еще используют их, поэтому мы расскажем здесь и о них.

DBI

Рекомендуемым методом доступа к базам данных MySQL и mSQL из Perl является интерфейс DBD/DBI. DBD/DBI означает DataBase Dependent/DataBase Independent (Зависимый от базы данных/Независимый от базы данных). Название связано с двухъярусной реализацией интерфейса. В нижнем ярусе находится зависимый от базы данных уровень. На нем существуют свои модули для каждого типа базы данных, доступного из Perl. Поверх этого уровня находится независимый от базы данных уровень. Это тот интерфейс, которым вы пользуетесь при доступе к базе данных. Выгода такой схемы в том, что программисту нужно знать только один API уровня независимости от базы данных. Когда появляется новая база данных, кому-нибудь нужно лишь написать для нее модуль DBD (зависимый), и она станет доступна всем программистам, использующим DBD/DBI.

Как и в любом модуле Perl, для получения доступа нужно указать DBI в директиве use:

#!/usr/bin/perl -w

use strict;

use CGI qw(:standard);

use DBI;

При запуске программ Perl для MySQL/mSQL следует всегда задавать аргумент командной строки -w. Благодаря этому DBI будет перенаправлять все специфические для MySQL и mSQL сообщения об ошибках на STDERR, и вы сможете увидеть ошибки, вызванные работой с базой данных, не прибегая к явной проверке их в программе.

Всякое взаимодействие между Perl, с одной стороны, и MySQL и mSQL - с другой, производится с помощью объекта, известного как описатель базы данных (handle). Описатель базы данных (database handle) - это объект, представленный в Perl как скалярная ссылка и реализующий все методы, используемые для связи с базой данных. Одновременно можно открыть любое число описателей базы данных, ограничение накладывают только ресурсы системы. Метод connect() использует для создания описателя формат соединения DBI:servertype:database:hostname:port (имя узла и порта необязательны), дополнительными аргументами служат имя пользователя и пароль:

my $dbh = DBI->connect( 'DBI:mysql:mydata ', undef, Lindef);

my $dbh = DBI->connect( 'DBI:mSQL:mydata:myserver', undef, undef);

my $dbh = DBI->connect( 'DBI:mysql:mydata', 'me', 'mypass")',

Атрибут servertype является именем специфического для базы данных DBD-модуля, в нашем случае «mysql» или «mSQL» (обратите внимание на точное использование регистра). В первом варианте создается соединение с сервером MySQL на локальной машине через сокет Unix. Это наиболее эффективный способ связи с базой данных, который должен использоваться при соединении на локальном сервере. Если указано имя узла, оно используется для соединения с сервером на этом узле через стандартный порт, если только не задан и номер порта. Если при соединении с сервером MySQL вы не указываете имя пользователя и пароль, то пользователь, выполняющий программу, должен обладать достаточными привилегиями в базе данных MySQL. Для баз данных mSQL имя пользователя и пароль не должны указываться.

В Perl 5 используются два соглашения по вызову модулей. В объектно-ориентированном синтаксисе для ссылки на метод определенного класса используется символ стрелки «->» (как в DBI->connect). Другой метод - использование непрямого синтаксиса, в котором за именем метода следует имя класса, а затем - аргументы. В последнем примере метод connect следовало бы записать как connect DBI 'DBI:mysql:mydata', "me', ' mypass . В ранних версиях Msql.pm использовался исключительно непрямой синтаксис, и требовалось придерживаться метода использования заглавных букв, обусловленного mSQL С API. Поэтому значительная часть старого кода MsqlPerl содержит строки типа SelectDB $dbh ' test' там, где можно было бы написать проще: $dbh->selectdb(' test') . Если вы еще не догадались, то сообщаем, что мы неравнодушны к объектно-ориентированному синтаксису, хотя бы потому, что использование стрелки делает ясной связь между классом и методом.

Как только вы соединились с сервером MySQL или mSQL, описатель базы данных - во всех примерах этого раздела $dbh - становится шлюзом к базе данных. Например, так готовится запрос SQL:

$dbh->prepare($query);

При работе с mSQL для одного описателя базы данных можно одновременно выбрать только одну базу данных, это ограничение накладывается сервером mSQL. Однако в любой момент можно сменить текущую базу данных, повторно вызвав connect . При работе с MySQL можно включать в запрос другие базы данных, явно указывая их имена. Кроме того, и в MySQL, и в mSQL при необходимости одновременного доступа к нескольким базам данных можно создать несколько описателей базы данных и использовать их совместно.

В главе 21 «Справочник по Perl», описаны все методы и переменные, содержащиеся как в DBI, так и в Mysql.pm и Msql.pm.

Для иллюстрации использования DBI рассмотрим следующие простые программы. В примере 10-1 datashow.cgi принимает в качестве параметра имя узла; при отсутствии параметра принимается имя «local-host». Затем программа выводит список всех баз данных, имеющихся на этом узле.

Пример 10-1. Программа CGI datashow.cgi показывает все базы данных, имеющиеся на сервере MySQL или mSQL

#!/usr/bin/perl -w

use strict;

use CGI qw( standard);

use CGI::Carp;

# Использовать модуль DBI use DBI; CGI::use_named_parameters(1);

my ($server, $sock, $host);

my $output = new CGI;

$server = param('server') or Sserver = '';

# Подготовить DBD-драйвер для MySQL

my $driver = DBI->install_driver('mysql');

my @databases = $driver->func($server, '_ListDBs');

# Если параметр @databases неопределен, предполагаем,

# что на этом узле не запущен

# сервер MySQL. Однако это может быть вызвано

# другими причинами. Полный текст сообщения об ошибке

# можно получить, проверив $DBI::errmsg.

if (not @databases) {

print header, start_html('title'=>"Данные no Sserver", 'BGCOLOR'=>'white');

print<<END_OF_HTML; <H1>$server</h1>

Ha Sserver , по-видимому, не запущен сервер mSQL. </body></html> END_OF_HTML

exit(0); }

print header, start_html('title'=>" Данные по $host",

'BGCOLOR'=>'white'); print <<END_OF_HTML; <H1>$host</h1>

<P>

Соединение с $host на сокете $sock.

<p>

Базы данных:<br>

<UL>

END_OF_HTML

foreach(@databases) {

print "<LI>$_\n"; }

print <<END_OF_HTML;

</ul>

</body></html>

HTML

exit(0)

В примере 10-2 tableshow.cgi принимает в качестве параметров имя сервера базы данных (по умолчанию «localhost») и имя базы данных на этом сервере. Затем программа показывает все таблицы, имеющиеся в этой базе данных.

Пример 10-2. Программа CGI tableshow.cgi выводит список всех таблиц в базе данных

#!/usr/bin/perl -w

use strict;

use CGI qw(:standard);

use CGI::Carp;

# Использовать модуль Msql.pm use DBI; CGI::use_named_parameters(1);

my ($db);

my $output = new CGI;

$db = param('db')'or die("He указана база данных!");

# Connect to the requested server.

my $dbh = DBI->connect("DBI:mysql:$db;$server", undef, undef);

# Если не существует $dbh, значит, попытка соединения с сервером

# базы данных не удалась. Возможно, сервер не запущен,

# или не существует указанной базы данных, if (not $dbh) {

print header, start_html('title'=>"Данные по $host => $db", 'BGCOLOR'=>'white');

print <<END_OF_HTML; <H1>$host</h1> <H2>$db</h2>

Попытка соединения не удалась по следующей причине:<BR> $DBI::errstr

</body></html>

END_OF_HTML

exit(0); }

print header, start_html('title'=>"Данные по $host => $db", 'BGCOLOR'=>'white'); print <<END_OF_HTML; <H1>$host</h1> <H2>$db</h2>

<р>

Таблицы:<br>

<UL>

END_OF_HTML

# $dbh->listtable возвращает массив таблиц,

# имеющихся в текущей базе данных.

my ©tables = $dbh->func( '_ListTables' );

foreach (@tables) {

print "<LI>$_\n"; }

print <<END_OF_HTML; </ul>

</body></html> END_OF_HTML

exit(0);

И наконец, пример 10-3 показывает, как вывести все сведения о некоторой таблице.

Пример 10-3. Программа CGI tabledump.cgi выводит сведения об указанной таблице

#!/usr/bin/perl -w

use strict;

use CGI qw(:standard);

use CGI::Carp;

# Использовать модуль DBI use DBI; CGI::use_named_parameters(1);

my ($db,Stable);

my Soutput = new CGI;

$server = param('server') or $server = ";

$db = param('db') or die("He указана база данных !");

# Соединиться с указанным сервером.

my $dbh = DBI->connect("DBI:mysql:$db:$server", undef, undef);

# Готовим запрос к серверу, требующий все данные

# таблицы.

my $table_data = $dbh->prepare("select * from Stable");

# Посылаем запрос серверу.

$table_data->execute;

# Если возвращаемое значение не определено, таблица не существует

# или пуста; мы не проверяем, что из двух верно.

if (not $table_data) {

print header, startjtml( 'title'=>

"Данные по $host => $db => Stable", 'BGCOLOR'=>'white');

prin<<END_OF_HTML;

<H1>$host</h1>

<H2>$db</h2>

Таблицы'Stable' нет в $db на $host.

</body></html>

END_OF_HTML

exit(0); }

# Теперь мы знаем, что есть данные для выдачи. Сначала выведем

# структуру таблицы.

print header, start_html( title'=>"Данные по $host => $db => $table",

'BGCOLOR'=>'white');

print <<END_OF_HTML; <H1>$host</h1> <H2>$db</h2> <H3>$table</h3>

<P>

<TABLE BOROER> <CAPTION>Пoля</caption> <TR>

<ТН>Поле<ТН>Тип<ТН>Размер<ТН>МОТ NULL </tr> <UL> END_OF_HTML

If $table_data->name возвращает ссылку

# на массив полей таблицы.

my ©fields = @{$table_data->NAME};

# $table_data->type возвращает ссылку на массив типов полей.

# Возвращаемые типы имеют стандартные обозначения SQL,

# а не специфические для MySQL.

my @types = @{$table_data->TYPE};

# $table_data->is_not_null возвращает ссылку на массив типа Boolean,

# указывающий, в каких полях установлен флат 'NOT NULL'.

my @>not_null = @{$table_data->is_not_null};

# $table_data->length возвращает ссылку на массив длин полей. Они

фиксированные

# для типов INT и REAL, но переменые (заданные при создании

# таблицы) для CHAR.

my @length = @{$table_data->length};

# Все перечисленные выше массивы возвращаются в одном и том же порядке,

# поэтому $fields[0], $types[0], $ndt_null[0] and $length[0] относятся к одному полю.

foreach $field (0..$#fields) {

print "<TR>\n";

print "<TD>$fields[$field]<TD>$types[$field]<TD>";

print $length[$field]

if $types[$field] eq 'SQL_CHAR';

print "<TD>";

print 'Y' if ($not_null[$field]);

print "</tr>\n"; }

print <<END_OF_HTML; </table>

<P>

<B>Data</b><br>

<OL>

END_OF_HTML

# Теперь мы будем построчно перемещаться по данным с помощью DBI::fetchrow_array().

# Мы сохраним данные в массиве в таком же порядке, как и в информационных

# массивах (§fields, @types, etc,), которые мы создали раньше.

while(my(@data)=$table_data->fetchrow_array) {

print "<LI>\n<UL>";

for (0..$#data) {

print "<LI>$fields[$_] => $data[$_]</li>\n"; }

print "</ulx/li>"; }

print «END_OF_HTML;

</ol>

</body></html>

END_OF_HTML

Пример приложения, использующего DBI

DBI допускает любые SQL-запросы, поддерживаемые MySQL и mSQL. Например, рассмотрим базу данных, используемую в школе для ведения учета учащихся, состава классов, результатов экзаменов и т. д. База данных должна содержать несколько таблиц: одну для данных о предметах, другую для данных об учащихся, таблицу для списка экзаменов и по одной таблице для каждого экзамена. Возможность MySQL и mSQL выбирать данные из нескольких таблиц, используя объединение таблиц, позволяет совместно использовать таблицы как согласованное целое для создания приложения, облегчающего работу учителя.

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

CREATE TABLE test (

id INT NOT NULL AUTO_INCREMENT, name CHAR(100),

subject INT, num INT

Для каждого отдельного экзамена структура таблицы такая:

CREATE TABLE t7 (

id INT NOT NULL,

q1 INT,

q2 INT,

q3 INT,

q4 INT,

total INT

)

К имени таблицы t присоединен идентификатор экзамена из таблицы test. При создании таблицы пользователь определяет количество вопросов. Поле total содержит сумму баллов по всем вопросам.

Программа, обрабатывающая данные экзаменов, называется test.cgi. Эта программа, текст которой следует ниже, позволяет только добавлять новые экзамены. Просмотр экзаменов и их изменение не реализованы и оставлены в качестве упражнения. Дополнить этот сценарий будет несложной задачей, если воспользоваться в качестве справочного материала другими сценариями, приведенными в данной главе. Этот сценарий хорошо показывает возможности DBI:

#!/usr/bin/perl -w

use strict; require my_end;

use CGI qw(:standard);

my Soutput = new CGI;

use_named_parameters(1);

# Использовать модуль DBI. use DBI;

# DBI::connect() использует формат 'DBI:driver:database', в нашем случае

# используется драйвер MySQL и открывается база данных 'teach',

my $dbh = DBI->connect('DBI:mysql:teach');

# Операция добавления распределена между тремя отдельными функциями. Первая функция, add,

# выводит пользователю форму шаблона для создания нового экзамена,

sub add {

$subject = param('subject')

if (param('subjects'));

$subject = ""

if $subject eq 'all';

print header, start_html('title'=>'Create a New Test',

'BGCOLOR'=>'white'); print <<END_OF_HTML;

<Н1>Создание нового экзамена</п1> <FORM ACTION="test.cgi" METHOD=POST>

<INPUT TYPE=HIDDEN NAME="action" VALUE="add2"> Предмет: END_OF_HTML

my @ids = (); my %subjects = ();

my $out2 = $dbh->prepare("select id,name from subject order by name" $out2->execute;

# DBI: :fetchrow_array() совершенно аналогична Msql: :fetchrow()

while(my($id,$subject)=$out2->fetchrow_array) {

push(@ids,Sid); $subjects{"$id"} = Ssubject; }

print popup_menu('name'=>'subjects', 'values'=>[@ids], 'default'=>$subject, 'labels'=>\%subjects);

print <<END_OF_HTML; <br>

Число вопросов: <INPUT NAME="num" SIZE=5><br> Название или идентификатор (например, дата) экзамена:

<INPUT NAME="name" SIZE=20>

<Р>

<INPUT TYPE=SUBMIT VALUE=" Следующая страница ">

<INPUT TYPE=RESET> </form></body></html>

END_OF_HTML }

Эта функция выводит форму, позволяющую пользователю выбрать предмет для экзамена, а также количество вопросов и название. Для вывода списка имеющихся предметов выполняется запрос к таблице предметов. При выполнении в DBI запроса SELECT он должен быть сначала подготовлен, а затем выполнен. Функция DBI::prepare полезна при работе с некоторыми серверами баз данных, позволяющими осуществить операции над подготовленными запросами, прежде чем выполнить их. Для MySQL и mSQL это означает лишь запоминание запроса до вызова функции DBI:: execute .

Результаты работы этой функции посылаются функции add2, как по-: казано ниже:

sub add2 {

my Ssubject = param('subjects');

[

my $num = param('num');

$name = param('name') if param('name');

my $out = $dbl»prepare("select name from subject where id=$subject");

$out->execute;

my (Ssubname) = $out->fetchrow_a.rray;

print header, start_html('title'=>"Создание экзамена по предмету $subname", ' BGCOLOR'=>'white');

print <<END_OF_HTML;

<H1> Создание экзамена по предмету $subname</h1> <h2>$name</h2>

<P>

<FORM ACTION="test.cgi" METHOD=POST>

<INPUT TYPE=HIDDEN NAME="action" VALUE="add3">

<INPUT TYPE=HIDDEN NAME="subjects" VALUE="$subject">

<INPUT TYPE=HIDOEN NAME="num" VALUE="$num">

<INPUT TYPE=HIDDEN NAME="name" VALUE="$name">

Введите количество баллов за каждый правильный ответ.

Сумма баллов не обязательно должна равняться 100.

<Р> END_OF_HTML

for (1..$num) {

print qq%$_: <INPUT NAME="q$_" SIZE=3> %; if (not.$_ % 5)

{ print "<br>\n"; } } print <<END_OF_HTML;

<P>

Введите текст экзамена:<br>

<TEXTAREA NAME="test" ROWS=20 COLS=60> </textarea> <p>

<INPUT TYPE=SUBMIT VALUE="Ввести экзамен ">

<INPUT TYPE=RESET> </form></body></html>

END_OF_HTML }

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

sub add3 {

my $subject = para'm( 'subjects');

my $num = param('num');

$name = param('name') if param('name');

my $qname;

($qname = $name) =" s/'/\\'/g;

my $q1 = "insert into test (id, name, subject, num) values ( '.'-, '$qname', $subject, $num)";

my Sin = $dbh->prepare($q1); $in->execute;

# Извлечем значение ID , которое MySQL создал для нас

my $id = $in->insertid;

my $query = "create table t$id ( id INT NOT NULL,

my $def = "insert into t$id values ( 0, ";

my $total = 0;

my @qs = grep(/^q\d+$/,param);

foreach (@qs) {

$query .= $_ . " INT,\n";

my $value = 0;

$value = param($_) if param($_);

$def .= "lvalue, ";

$total += $value; }

$query .= "total INT\n)"; $def .=-"$total)";

my $in2 = $dbh->prepare($query);

$in2->execute;

my $in3 = $dbh->prepare($def);

$in3->execute;

# Обратите внимание, что мы запоминаем экзамены в отдельных файлах. Это

# полезно при работе с mSQL, поскольку он не поддерживает BLOB.

# (Тип TEXT, поддерживаемый в mSQL 2, можно использовать,

# но это неэффективно.)

# Поскольку мы используем MySQL, можно с таким же успехом

# поместить весь экзамен в BLOB.

open(TEST,">teach/tests/$id") or die("A: $id $!");

print TEST param('test'), "\n";

close TEST;

print header, start_html('title'=>'Экзамен создан', 'BGCOLOR'=>'white');

print <<END_OF_HTML;

<Н1>Экзамен создан</h1> <P>

Экзамен создан.

<р>

<А HREF=".">Перейти</а> на домашнюю страницу 'В помощь учителю'.<br>

<А HREF="test.cgi">nepeimi</a> на главную страницу экзаменов.<br>

<А HREF="test.cgi?actio,n=add">Добавить</a> следующий экзамен.

</body></html>

END_OF_HTML

}

Теперь введем информацию об экзамене в базу данных. При этом мы шагнем дальше обычного ввода данных, который видели до сих пор. Данные по экзаменам достаточно сложны, поэтому каждый экзамен лучше хранить в собственной таблице. Вместо добавления данных в существующую таблицу мы создадим совершенно новую таблицу для каждого экзамена. Сначала мы создадим ID для нового экзамена с помощью функции автоинкрементирования MySQL и введем название и ID экзамена в таблицу с именем test. Эта таблица является просто указателем на экзамены, по ней можно легко найти ID любого экзамена. Затем мы создадим одновременно два запроса. Первый будет запросом CREATE TABLE, который определит наш новый экзамен. Второй запрос будет иметь тип INSERT и запишет в нашу таблицу максимальные баллы по каждому вопросу. Эти запросы будут отправлены серверу базы данных, что завершит весь процесс (после вывода пользователю страницы с сообщением об успешном завершении). Позднее, после сдачи экзамена учащимися, для каждого учащегося будет создана запись в таблице экзамена. Эти записи могут быть сравнены с максимальными значениями для определения оценки учащегося.

Msql.pm

Модуль Msql.pm является изначальным интерфейсом Perl к mSQL. Хотя его заменили модули DBI, все же осталось много сайтов, основанных на этом старом интерфейсе. Чтобы продемонстрировать использование Msql.pm, мы продолжим работу с нашим примером «помощника учителя».

Поскольку нам требуется определить школьные классы, в которых будут проводиться экзамены, рассмотрим таблицу предметов. Ее структура выглядит так:

CREATE TABLE subject (

id INT NOT NULL,

name CHAR(500),

teacher CHAR(100) )

CREATE UNIQUE INDEX idxl ON subject (

id,

name,

teacher

)

CREATE SEQUENCE ON subject

Число id является уникальным идентификатором школьного класса, а поля name и teacher являются наименованием курса и фамилией преподавателя соответственно. Все три поля проиндексированы, что ускоряет выполнение запросов. И наконец, мы определили последовательность для таблицы. Эта последовательность генерирует ID.

CGI-программа для обработки этих данных должна выполнять несколько функций:

Используя мощь Perl и mSQL, можно без труда объединить все эти функции в одном файле, subject.cgi. Для каждой из операций мы создадим свою функцию. Центральной частью программы будет своего рода коммутатор, распределяющий поступающие запросы по соответствующим функциям. Сами операции мы опишем позднее.

#Выбор нужной части сценария осуществляется в зависимости

# от параметра 'action'.

# Если 'action' не задан, вызывается функция defaultQ.

&default if not param('action');

# Этот прием из Camel 2 имитирует переключатель 'switch' в языке С. foreach[A04] (param('action')) {

/view/ and do { Sview; last; };

/add$/ and do { &add; last; };

/add2/ and do { Sadd2; last; };

/add3/ and do { &add3; last; };

/add4/ and do { &add4; last; };

/schange$/ and do { &schange; last; };

/schange2/ and do { &schange2; last; };

/lchange$/ and do { &lchange; last; };

/Ichange2/ and do { &lchange2; last; };

/IchangeS/ and do { &lchange3; last; };

/delete/ and do { Sdelete; last; };

&default; }

Пункты «add», «schange» и «Ichange» должны иметь завершающий «$», чтобы не быть спутанными со сходными. Если бы не было «$», то «add» соответствовал бы также add2, add3 и add4. Альтернативный прием - поместить «add», «schange» и «Ichange» после всех остальных функций, чтобы вызываться при отсутствии совпадений с предыдущими пунктами. Однако если впоследствии добавятся другие пункты, могут возникнуть ошибки. Третий способ - полностью устранить неоднозначность, используя /^view$/, /*add$/ и т. д. При этом придется ввести несколько больше символов, но возможность ошибки будет полностью устранена.

Остается лишь проработать детали, реализовав каждую функцию.

Функция default выводит исходную форму, которую видит пользователь, позволяющую ему выбрать тип операции. Эта функция вызывается, если CGI-программа вызывается без параметров, например, как http://www.myserver.com/teach/subject.cgi, или если параметр ACTION не соответствует ни одной из существующих функций. Можно было бы также создать функцию, выводящую сообщение об ошибке, если параметр ACTION неизвестен.

sub default {

print header, start_html('title'=>'Subjects', 'BGCOLOR'=>'white'):

print «END_OF_HTML; <h1>Предметы</h1>

<р>Выберите операцию и предмет (если это допустимо).

<FORM ACTION="subject.cgi" METHOD=POST>

<p>

<SELECT NAME="action">

<OPTION VALUE="view">npocмотp предмета

<OPTION VALUE="аdd">Добавление предмета

<OPTION VALUE="schange">Изменение предмета

<OPTION VALUE="lchange" SELECTED>Изменить список классов

<OPTION VALUE="delete">Удалить предмет </select>

END_OF_HTML

# См. ниже 'sub print_subjects'.

&print_subjects;

print «END_OF_HTML;

<P>

<INPUT TYPE=SUBMIT VALUE="Выполнить операцию">

<INPUT TYPE=RESET>

</form></body></html>

HTML

}

Основных операций пять: «view» (просмотр), «add» (добавление), «schange» (изменение данных о предмете), «Ichange» (изменить список классов по предмету) и «delete» (удалить). Например, мы подробно рассмотрим операцию «add». Она разбита на четыре отдельные функции, потому что диалог с пользователем приходится проводить до четырех раз. Для передачи данных от одной формы к другой используются скрытые переменные, пока, в конце концов, не будет создан класс.

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

sub add {

my (%fields);

foreach ('name','size','teacher') {

if (param($_)) { $fields{$_} = param($_); } else { $fields{$_} = ""; } }

print header, start_html('title'=>'Add a Subject','BGCOLOR'=>'white');

print «END_OF_HTML; <H1>Add a Subject</h1> <form METHOD=POST ACTION="subject.cgi"> <P>

Название предмета:

<input size=40 name="name" value="$fields{'name'}"><br>

Фамилия учителя:

<input size=40 name="teacher" value="$fields{'teacher'}"><br>

Число учащихся в классе:

<input size=5 name="size" value="$fields{'size'}">

<P>

<INPUT TYPE=HIDDEN NAME="action" VALUE="add2">

<INPUT TYPE=SUBMIT VALUE="Следующая страница ">

<INPUT TYPE=RESET>

</form>

<P>

<A HREF="subject.cgi">Перейти</a> назад к главной странице предметов.<br>

<А HREF=". ">Перейти</а> к домашней странице Помощи учителю.

</body></html>

END_OF_HTHL

}

Функция проверяет, не имеют ли какие-либо поля предустановленные значения. Это придает функции дополнительную гибкость, позволяя использовать ее как шаблон для классов со значениями по умолчанию, возможно, генерируемыми какой-либо другой CGI-программой.

Значения, полученные в первой части процесса добавления, передаются обратно CGI-программе для использования в функции add2. Функция add2 сначала проверяет, существует ли уже класс. Если существует, то пользователю посылается сообщение об ошибке, и он может изменить название класса.

Если класс еще не существует, функция проверяет, какое число учащихся введено. Если никакое, то класс создается без учащихся, их можно добавить позднее. Если нее число задано, создается класс и выводится форма, в которую можно ввести данные о каждом учащемся.

sub add2 {

my $name = param('name');

# Нам нужна копия имени, которая кодируется для URL.

my $enc_name = &cgi_encode($name);

# Нам также нужна копия имени, которую можно спокойно цитировать для

# ввода в базу. Msql использует с этой целью функцию Msql::quote().

my $query_name = $dbh->quote($name);

# Строим запрос для проверки существования предмета,

my $query ="select id, name, teacher from subject where name=$query_name";

#Если пользователь ввел фамилию учителя, отдельно проверяем фамилию,

# поскольку могут быть два курса с одинаковым названием, но

# разными учителями.

if (param('teacher')) {

$teacher = param('teacher');

$enc_teacher = &cgi_encode($teacher);

my $query_teacher = $dbh->quote($teacher);

$query .= " and teacher=$query_teacher"; }

# Теперь посылаем запрос серверу mSQL

my $out = $dbh->query($query);

ft Проверяем значение $out->numrows, чтобы узнать, были ли возвращены

# какие-либо строки. Если были, и пользователь не задал параметр 'override'

# (переопределить), то мы выходим с сообщением, что класс уже

# существует, и давая пользователю возможность все-таки ввести класс

# (повторно передав форму с установленным параметром 'override',

if ($out->numrows and not param('override')) { # Печать страницы 'Класс уже существует'.

} else {

# Теперь вводим данные в базу.

# Сначала нужно выбрать новое число из

# последовательности для таблицы.

$out = $dbh->query("select _seq from subject");

my ($id) = $out->fetchrow;

# Теперь вводим информацию в базу данных, используя

# полученное из последовательности число в качестве ID.

$query = "INSERT INTO subject (id, name, teacher)

VALUES ($id, '$name', 'Steacher')"; $dbh->query($query);

# Если пользователь не задал размер класса, выходим

# с сообщением о том, что пользователь может добавить

# число учащихся позже, if (not param('size')) {

# Вывод страницы с сообщением об успехе.

} else { \

# Теперь выводим форму, позволяющую пользователю

# ввести имена всех учащихся в классе.

print header, start_html('title'=>'Create Class List',

'BGCOLOR'=>'white'); print <<END_OF_HTML;

<Н1>Создать список класса</h1> <P>

<B>$name</b>

добавлен в базу данных. Теперь можно ввести фамилии учащихся в этом классе. Добавить или исключить учащихся можно позднее с главной страницы предметов

<а href="subject.cgi"> </a>.

<Р>

<FORM METHOD=POST ACTION="subject.cgi">

<INPUT TYPE=HIDDEN NAME="action" VALUE="add3">

<INPUT TYPE=HIDDEN NAME="id" VALUE="$id">

<TABLE BORDER=0>

<ТР><ТН><ТН>Имя<ТН>Отчество/Инициал

<ТН>Фамилия<ТН>мл.,ст.,III,и т.д.

</tr>

END_OF_'HTML

for $i (1.,$size) {

print <<END_OF_HTML;

<TR><TD>$i<TD><INPUT SIZE=15 NAME="first$i"><TD>

<lNPUT SIZE=15 NAME="middle$i">

<TD><INPUT SIZE=15 NAME="last$i"><TD>

<INPUT SIZE=5 NAME="ext$i"></tr>

END_OF_HTML

}

print <<END_OF_HTML; </table>

<INPUT TYPE=SUBMIT VALUE=" Ввести список класса ">

<INPUT TYPE=RESET> </form>

</body></html>

END_OF_HTML

} } }

Обратите внимание, что функция использовала три копии параметра name. Для использования в составе URL все специальные символы должны быть заменены особым образом. Для этого в коде примера используется функция cgi_encode . Кроме того, чтобы ввести строку в базу данных mSQL, вместо некоторых символов нужно использовать управляющие символы. Интерфейс MsqlPerl предоставляет для этого функцию quote, доступную через любой описатель базы данных. Наконец, при выводе на экран используется непреобразованный вариант переменной.

При добавлении класса в базу данных удобно использовать такую функцию mSQL, как последовательности. Вспомним, что в таблице class была определена последовательность. Ее значения используются в качестве уникального идентификатора для каждого класса. Благодаря этому два класса могут иметь одинаковые названия (или одного и того же учителя и т. д.) и все же быть различными. Это также окажется удобным при дальнейших изменениях в классе. Пока между формами передается уникальный ID, все прочие данные о классе можно свободно изменять.

Наконец, отметим, что выводимая этой функцией форма для ввода учащихся генерируется динамически. Для вывода формы с правильным числом записей используется введенное раньше число учащихся. Помните, что CGI-программа полностью управляет генерируемым HTML. Любая часть, включая формы, может быть создана программным образом.

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

sub add3 {

if (not param('id')) { &end("Требуется числовой ID"); }

my $id = param( 'id');

my ©list = &find_last_student;

my ($ref_students,$ref_notstudents) = &find_matching_students(@list);

@students = @$ref_students

if $ref_students;

@notstudents = @$ref_notstudents

if $ref_notstudents;

if (@notstudents) {

# Вывести форму, говорящую пользователю, что в списке

# есть несуществующие учащиеся. Пользователь может автоматически

# создать учащихся или вернуться и исправить опечатки.

} else {

&update_students($id,@students);

#Вывести форму успешного завершения работы.

} }

В этой функции основная часть работы выполняется другими функциями. Это обусловлено тем, что в других частях CGI-программы возникают сходные задачи, которые полезно решать с помощью совместно используемых функций. Первая такая функция - f ind_last_student , которая изучает данные формы и возвращает список имеющихся в форме номеров, не связанных с ID в базе данных, всех введенных пользователем учащихся. Это необходимо, поскольку, как упоминалось раньше, предыдущая форма генерируется динамически и нет возможности непосредственно узнать, сколько учащихся включено.

sub find_last_student {

my @params = param; my @list = (); foreach (@params) {

next if not param($_);

# Исключить все 'пустые' поля

if (/-(first|middle|last|ext)(\d+)/) {

my $num = $2;

if (not grep(/"$num$/,@list)) { push(@list,$num); } } }

@list = sort { $a <=> $b} @list; return @list;

}

Обратите внимание, что функция возвращает все числа, а не только последнее, которое, предположительно, будет числом введенных учащихся. Хотя предыдущая форма вывела число запрошенных пользователем записей, нет гарантии, что пользователь заполнил их все. Он мог пропустить строку, которая не будет включена в данные формы. Поэтому необходимо выявить все введенные числа. Результаты работы этой функции пересылаются следующей вспомогательной функции find_matching_students , как показано ниже:

sub find_matching_students { my §list = @_;

my ($i,@students,@notstudents); §students = ();

@notstudents = ();

if (@list) {

foreach $i (@list) {

my @query = ();

# Строим запрос, который ищет заданного учащегося,

my $query = "select id, subjects from student where ";

foreach ('first','middle','last', 'ext') {

if (param("$_$i")) {

my $temp = param("$_$i");

# В mSQL и MySQL одиночные кавычки служат ограничителями

# имен полей, и им должен предшествовать

# управляющий символ "\",

# который управляет и сам собой,

# чтобы быть введенным буквально.

$temp =~ s/7\\'/g;

push(@query, "$_ = '$temp'"); } }

$query = join(" and ",§query);

# Посылаем запрос базе данных.

my $out = $dbh->query($query);

# Если база данных ничего не возвращает, добавляем

# учащегося к массиву @notstudents.

if (not $out->numrows) {

push(@notstudents, [ param("first$i"), param("middle$i"), param("last$i"), param("ext$i") ]);

# В противном случае добавляем студента в массив ©students.

} else {

my ($id,$subjects) = $out->fetchrow;

push(@students,[$id,$subjects]); } } }

return(\§students,\@notstudents); }

Эта функция пробегает по всем заданным именам учащихся и проверяет, есть ли уже они в базе данных. Если они существуют, данные о них записываются в массив с именем ©students , в противном случае - в массив @notstudents . Данные о каждом учащемся хранятся в безымянном массиве, создавая своего рода объект учащегося. В итоге функция возвращает ссылки на оба массива. Она не может возвратить данные как обычный массив, поскольку будет невозможно определить, где закончился один массив и начался другой.

И последняя вспомогательная функция - update_students , которая добавляет класс к списку классов для каждого существующего учащегося.

sub update_students {

my $id = shift;

my ©students = @_;

foreach (©students) {

my($sid, $subjects)=©$_;

if (not Ssubjects) { Ssubjects = ":$id:"; }

elsif (Ssubjects !" /:$id:/)

{ Ssubjects .= "$id:"; }

my $query = "update sti/dent set subjects='Ssubjects'

where id=$id";

$dbh->query($query); } }

Эта функция осуществляет запрос к таблице student, совершенно независимой от таблицы subject. В пределах одной CGI-программы можно работать с любым числом различных таблиц одной базы данных. Можно переключаться с одной базы данных на другую, но одновременно может быть активна только одна база данных. Эта функция извлекает список предметов для каждого учащегося и добавляет к нему новый предмет, если его еще нет в списке.

Функция обрабатывает все возможные случаи, кроме одного, когда к предмету приписаны учащиеся, которых еще нет в таблице student. В этом случае список новых учащихся передается функции add4, как показано ниже:

sub add4 {

#получить список ©students и @notstudents

&update_students($id,@students) if @students;

&insert_students($id,@notstudents) if @notstudents;

# Вывести страницу успешного завершения. }

Эта функция разделяет список учащихся на существующих и несуществующих тем же способом, что и add3. Затем она обновляет список существующих учащихся с помощью функции update_students , показанной раньше. Несуществующие учащиеся посылаются новой вспомогательной функции insert_students :

sub insert_students { foreach $i (@list) {

# Производится выбор очередного числа из последовательности,

# определенной в таблице. Зто число используется как ID учащегося,

my $out = $dbh->query('select _seq from student');

my($sid) = $out->fetchrow;

# Для включения в базу данных все строки

# нужно процитировать.

my ($first, $middle, $last, $ext) = (

$dbh->quote(param("first$i")),

$dbh->quote(param("middle$i")),

$dbh->quote(param("last$i")),

$dbh->quote(param("ext$i")) );

my $query = "insert into student (id, first, middle, last,

ext, subjects) VALUES ($sid, $first, $middle,

$last, $ext, ':$id:')";

$dbh->query($query); } }

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

MysqIPerl

Монти Видениус, автор MySQL, написал также и интерфейс Perl к MySQL, Mysql.pm. Он основывался на модуле Msql.pm для mSQL, поэтому интерфейсы двух модулей почти идентичны. На практике мы недавно преобразовали целый сайт из mSQL в MySQL, выполнив команду «perl -e 's/^Msql/Mysql/>> *.cgi» в каждом каталоге, содержащем CGI. Это составило 95% всей работы. Разумеется, при этом вы не получаете преимуществ MySQL, но таким путем можно быстро и легко встать на путь использования MySQL. Mysql.pm входит составной частью в пакет msql-mysql-modules Йохена Видмана (Jochen Wiedmann).

Одним из самых больших различий между MySQL и mSQL является их работа с последовательностями. В mSQL последовательность определяется в таблице командой CREATE SEQUENCE on tablename . Значение последовательности можно получать после этого, как обычное поле таблицы командой SELECT _se.q from tablename . В MySQL к первичному ключу добавляется флаг AU-TO_INCREMENT . При попытке ввода null в это поле оно автоматически инкрементируется. Как MySQL, так и mSQL допускают в каждой таблице только одну последовательность. Подробное обсуждение последовательностей в MySQL и mSQL см. в главе 6 «Диалект SQL, используемый в MySQL и mSQL».

Чтобы показать некоторые функции Mysql.pm, вернемся к примеру с экзаменами. Разобравшись с subject.cgi, займемся таблицей сведений об учащихся. Ее структура такова:

CREATE TABLE student (

id INT NOT NULL auto_increment,

first VARCHAR(50),

middle VARCHAR(50),

last VARCHAR(50),

ext VARCHAR(50),

subjects VARCHAR(100),

age INT,

sex INT,

address BLOB,

city VARCHAR(50),

state VARCHAR(5),

zip VARCHAR(10),

phone VARCHAR(10),

PRIMARY KEY (id)

)

В этой таблице находятся все данные, используемые программой subject, cgi, a также другие сведения, которые относятся к учащемуся. Программа student.cgi, работающая с этой таблицей, должна выполнять все те функции, которые программа subject.cgi выполняла в отношении таблицы subject.

Нельзя работать с базой данных mSQL через модуль Mysql.pm, как и с базой MySQL через Msql.pm. Программа stu-dent.cgi предполагает, что таблица предметов находится в базе данных MySQL. Аналогично, программа subject.cgi рассчитывает на mSQL-версию таблицы учащихся.

Чтобы продемонстрировать, как работает Mysql.pm, мы подробно изучим ту часть student.cgi, которая позволяет пользователю изменять сведения об учащемся. Так же как операция «add» (добавление) в примере для Msql.pm была разбита на четыре отдельные функции, операция «change» (изменение) разбита здесь на три отдельные функции.

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

sub change {

print header, start_html('title'=>'Поиск учащегося для изменения денных'

'BGCOLOR'=>'white');

&print_form('search2', Поиск учащегося для изменения данных',1);

print <<END_OF_HTML; <р>

<INPUT TYPE=HIDDEN NAME="subaction" VALUE="change2">

<INPUT TYPE=SUBMIT VALUE=" Искать учащегося ">

<INPUT TYPE=SUBMIT NAME="all" VALUE=" Просмотр всех учащихся ">

<INPUT TYPE=RESET>

</form></body></html>

END_OF_HTML }

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

sub print_form {

my ($action,$message,$any) = @_;

print <<END_OF_HTML;

<FORM METHOD=post ACTION="students.cgi">

<INPUT TYPE=HIDDEN NAME="action" VALUE="$action">

<H1>$message</h1>

END_OF_HTML

if ($any) {

print <<END_OF_HTML; <р>Поиск

<SELECT NAME="bool">

<OPTION VALUE="ог">любой

<OPTION VALUE="and">Bce </select> выбранные вами.

END_OF_HTML

У

print <<END_OF_HTML; <P>

Имя:

<INPUT NAME="first" SIZE=20>

Отчество:

<INPUT NAME="middle" SIZE=10>

Фамилия:

<INPUT NAME="last" SIZE=20>

МЛ./III/И т.д..:

<INPUT NAME="ext" SIZE=5> <br>

Адрес:

<INPUT NAME="address" SIZE=40><br>

Город:

<INPUT NAME="city" SIZE=20>

Штат:

<INPUT NAME="state" SIZE=5>

Почтовый индекс:

<INPUT NAME="zip" SIZE=10><br>

Телефон:

<INPUT NAME="phone" SIZE=15><br>

Возраст:

<INPUT NAME="age" SIZE=5>

Пол:

<SELECT NAME="sex">

END_OF_HTML

if ($any) {

print <<END_OF_HTML; <OPTION VALUE="">He имеет значения

END_OF_HTML

}

print <<END_OF_HTML;

<OPTION VALUE="1">Myжской

<OPTION VALUE="2">Женский

</select><br>

<P>

Записан на:<br>

END_OF_HTML

&print_subjects("MULTIPLE SIZE=5");

}

Благодаря использованию трех параметров эта функция настраивает шаблон формы так, что может использоваться в самых различных целях. Обратите внимание, что эта вспомогательная функция использует другую вспомогательную функцию, print_subjects . Последняя выводит список всех имеющихся предметов из таблицы subject, как в примере Msql.pm.

sub print_subjects { my $modifier = "";

$modifier = shift if @_;

print qq%<SELECT NAME="subjects" $modifier>\n%;

my $out = $dbh->query("select * from subject order by name");

while(my(%keys)=$out->fetchhash) {

print qq%<OPTION VALUE="$keys{'id'}">$keys{'name'}\n%;

}

print "</select>\n";

}

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

к следующей функции изменения, change2. Для этого мы ввели в форму скрытую переменную subaction=change2 . Она сообщает search2, куда отправить пользователя дальше:

sub search2 {

my $out = $dbh->query(&make_search_query);

my $hits = $out->numrows;

my $subaction = "view";

$subaction = param('subaction')

if param('subaction');

print header, start_html('title'=>'Результаты поиска учащихся', 'BGCOLOR'=>'white');

if (not Shits) {

print <<END_OF_HTML;

<Н1>Учащихся не найдено</h1>

<P>

He найдено учащихся, удовлетворяющих вашему критерию.

END_OF_HTML } else {

print <<END_OF_HTML;

<H1> Найдено $hits учащихся </h1> <р>

<UL>

END_OF_HTML

while(my(%fields)=$out->fetchhash) {

print qq%<LI>

<A HREF="students.cgi?action=$subaction&id=$fields

{'id'}">$fields{'first'} $fields{'middle'} $fields{'last'}%;

print ", $fields{'ext'}" if $fields{'ext'};

print "\n</a>"; } }

print <<END_OF_HTML; </ul> <p>

<A HREF="students.cgi?action=search">HcKaTb</a> снова.

</body></html>

END_OF_HTML }

С помощью функции make_search_query эта функция сначала ищет учащихся, отвечающих критериям поиска. Затем она выводит список найденных записей, среди которых пользователь может сделать выбор. ID выбранной записи передается функции change2, как показано ниже:

sub change2 {

my $out = $dbh->query

("select * from student where id=$id");

my($did,Ifirst,$middle,$last,

$ext,Ssubjects.Sage,$sex,$address,

$city,$state,$zip,$phone) = $out->fetch row;

my ©subjects = split(/:/,$subjects);

shift,©subjects;

my $name = "$first $tmiddle $last";

if ($ext) { $name .= ", $ext"; }

print header, start_html('title'=>"$name", 'BGCOLOR'=>'white');

print <<END_OF_HTML;

<H1>$name</h1> <p>

<FORM ACTION="students.cgi" METHOD=POST>

<INPUT TYPE=HIDDEN NAME="action" VALUE="change3">

<INPUT TYPE^HIDDEN NAME="id" VALUE="$id">

Имя:

<INPUT NAME="first" VALUE="$first" SIZE=20>

Отчество:

<INPUT NAME="middle" VALUE="$middle" SIZE=10>

Фамилия:

<INPUT NAME="last" VALUE="$last" SIZE=20>

МЛ./III/И т.д.

<INPUT NAME="ext" VALUE="$ext" SIZE=5> <br>

Адрес:

<INPUT NAME="address" VALUE="$address" SIZE=40><br>

Город:

<INPUT NAME="city" VALUE="$city" SIZE=20>

Штат:

<INPUT NAME="state" VALUE="$state" SIZE=5>

Почтовый индекс:

<INPUT NAME="zip" VALUE="$zip" SIZE=10xbr>

Телефон:

<INPUT NAME="phone" VALUE="$phone" SIZE=15><br>

Возраст:

<INPUT NAME="age" VALUE="$age" SIZE=5>

Пол:

END_OF_HTML

my %sexes = ( '1' => 'Мужской',

'2' => 'Женский' );

print popup_menu('name'=>'Пол', 'values'=>["!', '2'], 'default'=>"$sex", ' labels'=>\%sexes);

print <<END_OF_HTML; <p>

Записан на:<br>

END_OF_HTML

my @ids = ();

my %subjects = ();

my $out2 = $dbh->query("select id,name from subject order by name");

while(my($id,$subject)=$out2->fetchrow) { push(@ids,Sid);

$subjects{"$id"} = $subject; }

print scrolling_list('name'=>'subjects', 'values'=>[@ids], 'default'=>[@subjects], 'size'=>5, 'multiple'=>'true', 'labels'=>\%subjects);

print <<END_OF_HTML;

<р>

<INPUT TYPE=SUBMIT VALUE=" Изменить данные об учащемся ">

<INPUT TYPE=SUBMIT NAME="delete" VALUE=" Удалить учащегося ">

<INPUT TYPE=RESET>

</form></body></html>

END_OF_HTML

}

Главная задача этой функции - вывести форму, очень похожую на ту, которую порождает print^from. Однако значениями по умолчанию в этой форме должны быть те, которые соответствуют выбранному учащемуся. В результате пользователь может редактировать одни поля, оставляя другие неизменными.

Несколько функций, предоставляемых модулем CGI.pm, оказываются очень удобными при выводе формы со значениями, установленными по умолчанию, особенно функция CGI: :scrolling_list , выводящая блок HTML <SELECT> с заданными параметрами. Среди прочих, функция принимает параметры values, default и labels, относящиеся к значениям каждого тега <OPTION> , задающие выбираемые по умолчанию пункты и их метки, видимые пользователю.

Эта функция выводит заполненную форму, как если бы это была форма для добавления. Разница в том, что это данные для учащегося, уже имеющегося в базе данных. Функция changes принимает данные и обновляет данные в базе, как показано ниже:

sub changes {

if (param('delete')) { &delete2($id); } else {

my Squery = "update student set "; my @query = ();

foreach ('first', 'middle', 'last', 'ext', 'address', 'city', 'state', 'zip', 'phone') {

if (param($_)) { push(@query,"$_ = ".

$dbh->quote(param($_))); } }

push(@query,"age=".param('age')) if param('age');

push(@query,"sex=".param('sex')) if param('sex');

my $subjects = "':";

$subjects .= join(":",param('subjects'));

$subjects .= ":" unless $subjects eq "':";

$subjects .= "'";

push(@query, "subjects=$subjects");

Squery .= join(", ",@query) . " where id=$id"; $dbh->query($query);

print header, start_html('title'=>'Данные об учащемся изменены',

'BGCOLOR'=>'white'); # Вывести форму с сообщением об успехе

}

}

Обратите внимание, что при выборе кнопки «Delete» на странице изменения функция автоматически передает управление функции удаления. Это одно из главных преимуществ объединения в одной программе нескольких функций. Если вмешательства пользователя не требуется, то можно перескакивать между функциями без посылки пользователю сообщений о перенаправлении.

Оставшаяся часть этой функции довольно проста. Данные об учащемся собираются в запросе UPDATE, который отправляется серверу MySQL. Затем пользователю посылается страница с сообщением об успешном завершении работы.

Оглавление

GNU OCXE GNU LINUX
Сайт управляется системой uCoz