Практика применения Perl, PHP, Apache, MySQL для активных Web-сайтов (С) Александр Фролов, Григорий Фролов, 2002 Основные операции с базами данных Установка соединения с сервером СУБД Обработка результатов выполнения команды SQL Команды изменения таблиц базы данных Просмотр содержимого таблицы в сценарии PHP Обычная техника использования шаблонов Специальные методы класса Template Редактирование и просмотр дерева Приложение для редактирования дерева Сценарии PHP редактирования дерева Редактирование заголовка и текста узла Приложение для просмотра дерева
Сценарии PHP никогда бы не приобрели большой популярности, если бы в них не было предусмотрено никаких средств работы с базами данных. К счастью, PHP-приложениям доступен простой и удобный набор функций интерфейса с СУБД MySQL. Связка PHP-MySQL представляет собой отличную платформу для создания Web-приложений как в среде операционной системы Linux, так и в среде Microsoft Windows. Что же касается доступа к СУБД других типов, то здесь дела обстоят не так хорошо, как в программах Perl. В то время как программам Perl доступна обширная библиотека модулей CPAN, включающая драйверы практически всех наиболее популярных баз данных, сценарии PHP вынуждены обходиться только СУБД тех типов, которые были встроены в интерпретатор PHP. Однако в большинстве случаев указанный недостаток не играет особой роли, так как подавляющее большинство довольно сложных Web-приложений можно реализовать с применением бесплатной СУБД MySQL. В этой главе мы рассмотрим применение функций PHP, предназначенных для доступа к MySQL, при решении наиболее типичных задач, встающих перед разработчиком Web-приложений. Основные операции с базами данных Прежде чем приступить к работе с базой данных, сценарий PHP должен установить соединение с сервером СУБД и выбрать нужную базу данных в качестве текущей. Далее можно выдавать к выбранной базе данных запросы SQL и получать результаты их выполнения. В частности, можно создавать и обновлять таблицы, выбирать из них нужные записи в соответствии с заданным критерием и т.д. Перед завершением своей работы сценария PHP необходимо закрыть установленное ранее соединение с сервером СУБД. Расскажем обо всем этом подробнее. Установка соединения с сервером СУБД Сценарий PHP может установить соединение с сервером MySQL двух типов — обычное и перманентное (постоянное). Обычное соединение закрывается автоматически при завершении работы сценария PHP, а перманентное сохраняется для повторного использования другими сценариями, запускаемыми на том же сервере. Это повышает производительность работы Web-приложения при большом количестве посетителей в единицу времени. Чтобы установить обычное соединение с сервером MySQL, необходимо использовать функцию mysql_connect: resource mysql_connect ([string server [, string username [, string password]]]) В качестве первого параметра функции mysql_connect передается доменное имя или адрес IP узла, на котором запущен сервер MySQL. Если Web-сервер и сервер СУБД работают на одном и том же узле, то адрес сервера MySQL может быть указан как localhost. При необходимости Вы можете задать стандартный или нестандартный порт для доступа к серверу MySQL в виде такой строки: server='localhost:3306' Можно также задать в первом параметре сокет домена Linux, на котором сервер MySQL воспринимает запросы, с помощью строки ":/path/to/socket". Второй и третий параметры функции mysql_connect задают, соответственно, идентификатор и пароль доступа к базе данных. Заметим, что все три параметра необязательные. Если вызвать функцию mysql_connect без параметров, то будут использованы параметры по умолчанию. В частности, для первого параметра будет использовано значение localhost:3306, а для второго — идентификатор пользователя, запустившего сценарий. Для доступа к базе данных будет использован пустой пароль. С целью повышения устойчивости Web-приложения к «взлому», мы рекомендуем при настройке MySQL создать один или несколько идентификаторов пользователя, наделив их минимально необходимыми правами, достаточными для работы Web-приложения. Для каждого такого пользователя следует указать свой пароль. При успешной установке соединения функция mysql_connect возвращает идентификатор соединения, а при неудаче — значение FALSE. Вот пример подключения к базе данных trudogolik, рассмотренной нами ранее в главе 8 этой книги: $conn=mysql_connect("localhost", "trudogolik_adm", "admin"); При возникновении ошибки в процессе подключения сценарий завершает свою работу при помощи функции exit. Ниже мы рассмотрим более корректный способ обработки ошибок, возникающих при обращении сценариев PHP к базе данных. Для установки постоянного соединения с сервером MySQL, необходимо использовать функцию mysql_pconnect: resource mysql_pconnect ([string server [, string username [, string password]]]) Назначение параметров и возвращаемое значение этой функции такие же, что и у только что рассмотренной функции mysql_connect. Когда сценарий PHP вызывает функцию mysql_pconnect, она проверяет, было ли ранее установлено требуемое соединение. Если соединение с указанной базой данных, идентификатором пользователя и паролем уже установлено, функция mysql_pconnect возвращает идентификатор существующего соединения, а не создает новое. На этом экономится время, что и приводит к повышению производительности Web-приложения. Для того чтобы закрыть соединение, установленной функцией mysql_pconnect, сценарий PHP должен вызвать функцию mysql_close, о которой мы расскажем позже в этой главе. Чтобы можно было устанавливать постоянные соединения с сервером MySQL, интерпретатор PHP следует установить в сервере Apache как модуль, а не как программу CGI. Подробности Вы найдете в разделе «Настройка Apache для работы с PHP» предыдущей главы нашей книги. При использовании PHP совместно с сервером Microsoft Internet Information Server следует установить PHP как расширение ISAPI (см. раздел «Настройка Microsoft IIS для работы с PHP» предыдущей главы). Применять постоянные соединения следует с осторожностью, так как они могут вызвать различные проблемы. Например, если количество возможных соединений с СУДБ ограничено, то рано или поздно все свободные соединения будут исчерпаны. После этого Web-приложение перестанет работать. Такая же ситуация может появиться в результате наличия ошибок в сценариях PHP, приводящих к «повисанию» открытых соединений или взаимным блокировкам таблиц. После установки соединения с базой данных сценарий PHP может выбрать базу данных в качестве текущей. При этом все последующие команды SQL, выдаваемые с помощью функции mysql_query, будут направляться в выбранную базу данных (функция mysql_query будет рассмотрена в следующем разделе). Заметим, что существует и другая возможность — указывать базу данных непосредственно при выдаче команд. В этом случае выбирать текущую базу данных не нужно, но для работы с ней потребуется использовать функцию mysql_db_query. Эта функция отмечена в документации на PHP версии 4 как устаревшая и не рекомендуемая к использованию. Чтобы выбрать текущую базу данных, следует вызвать функцию mysql_select_db: bool mysql_select_db(string database_name [, resource link_identifier]) В качестве первого параметра функции mysql_select_db необходимо передать строку имени базы данных, которая должна стать текущей. Второй необязательный параметр задает идентификатор соединения с базой данных, установленного ранее функцией mysql_connect или mysql_pconnect. Если этот параметр не указан, используется то соединение, с которым сценарий PHP работал в последний раз. для избежания трудно обнаруживаемых ошибок мы, однако, рекомендуем всегда указывать значения необязательных параметров подобного рода. Если выбор данных произведен успешно, функция mysql_select_db возвращает значение TRUE, а при ошибке — значение FALSE. Вот пример использования функции mysql_select_db: if(!mysql_select_db("trudogolik",
$conn)) Здесь мы делаем текущей базу данных с именем trudogolik. Выдача команд SQL Как мы уже говорили, после выбора текущей базы данных к ней можно выдавать команды SQL при помощи функции mysql_query: resource mysql_query(string query [, resource link_identifier]) Через первый параметр этой функции передается строка запроса SQL, а через второй — идентификатор соединения с сервером базы данных. Заметим, что передаваемая таким способом строка запроса SQL не должна завершаться символом точка с запятой. Если строка SQL была выполнена без ошибок, функция mysql_query возвращает идентификатор ресурса, а если с ошибкой — значение FALSE. Вот пример использования команды mysql_query для выборки всех записей таблицы city: $sql="SELECT id, title FROM
city"; Аналогичным образом выдаются и другие команды SQL. Обработка результатов выполнения команды SQL Способ дальнейшего использования идентификатора ресурса, полученного от только что описанной функции mysql_query при успешном выполнении строки SQL, зависит от типа выполненной команды SQL. Команды изменения таблиц базы данных Если выполнялась команда изменения таблицы базы данных, такая как DELETE, INSERT, REPLACE, или UPDATE, сценарий PHP может узнать количество строк таблицы, измененных в результате выполнения команды SQL. Для этого он может воспользоваться функцией mysql_affected_rows: int mysql_affected_rows([resource link_identifier]) В качестве параметра функции передается идентификатор установленного соединения с сервером базы данных. Заметим, что Вы не можете использовать функцию mysql_affected_rows для анализа результатов выполнения команды SELECT. Для этой цели предназначена другая функция, которую мы рассмотрим в следующем разделе. Существует один особый случай — при удалении всех строк таблицы командой DELETE функция mysql_affected_rows вернет нулевое значение вне зависимости от количества удаленных строк. Если при выполнении команды SQL возникли ошибки, функция mysql_affected_rows вернет значение –1. Команда SELECT В результате выполнения команды SELECT из таблицы выбираются строки, количество которых может изменяться от нуля до полного количества строк в таблице. Функция mysql_num_rows позволит Вам узнать, сколько именно строк было выбрано при выполнении команды SELECT: int mysql_num_rows(resource result) В качестве параметра этой функции нужно передать значение, полученное от функции mysql_query. Для того чтобы получить строки, выбранные из таблицы в результате выполнения команды SELECT, следует использовать такие функции, как mysql_fetch_object, mysql_fetch_array и аналогичные. После извлечения данных необходимо освободить ресурсы, полученные при выполнении команды, с помощью функции mysql_free_result. Ниже мы привели прототип функции mysql_fetch_object: object mysql_fetch_object(resource result [, int result_type]) В качестве первого параметра эта функция должна получить ссылку на ресурс, возвращенную функцией mysql_query при выполнении команды SELECT. Второй, необязательный параметр позволяет указать тип получаемого результата и для этой функции обычно не указывается. Функция mysql_fetch_object возвращает объект, свойствами которого являются значения полей очередной строки результата. К этим свойствам можно обращаться по имени столбцов таблицы, что очень удобно. В следующем разделе мы рассмотрим практическое применение функции mysql_fetch_object. Описание остальных функций, предназначенных для извлечения результатов выполнения команды SELECT, Вы найдете в документации, поставляющейся в составе дистрибутива интерпретатора PHP. Просмотр содержимого таблицы в сценарии PHP Теперь, когда мы описали основные функции PHP, предназначенные для работы с базами данных MySQL, приведем пример простого сценария, позволяющего просмотреть содержимое таблицы списка городов city из базы данных trudogolik. Эта база данных была описана ранее в главе 8 с названием «Виртуальное кадровое агентство Трудоголик.Ру». Полный исходный текст сценария PHP Вы найдете в листинге 11-1. Листинг 11-1 Вы найдете в файле chap10\www.php.test\root\mysql\sample1\index.php на прилагаемом к книге компакт-диске. <html> Как видите, строки сценария PHP встроены непосредственно в текст документа HTML и разделяют его на два фрагмента. В верхнем фрагменте документа сценарий, прежде всего, пытается установить соединение с базой данных при помощи функции mysql_connect: $conn=mysql_connect("localhost",
"trudogolik_adm", "admin"); Так как Web-сервер работает на том же узле, что и сервер СУБД, в качестве первого параметра мы передаем функции mysql_connect строку localhost. Остальные два параметра задают, соответственно, идентификатор и пароль для подключения к базе данных trudogolik. Если по какой-либо причине соединение установить не удается, сценарий записывает в текст формируемого документа HTML соответствующее сообщение об ошибке и завершает свою работу при помощи функции exit. На следующем этапе сценарий выбирает базу данных trudogolik в качестве текущей базы данных, вызывая для этого функцию mysql_select_db: if(!mysql_select_db("trudogolik",
$conn)) В качестве первого параметра мы передаем функции mysql_select_db название базы данных trudogolik, а в качестве второго — идентификатор соединения с сервером MySQL, полученный от функции mysql_connect. Если при выборе текущей базы данных возникает ошибка, то перед тем как завершить свою работу, сценарий закрывает соединение с базой данных при помощи функции mysql_close. Как мы уже говорили, это необязательная операция, так как при завершении работы сценария все связанные с ним ресурсы должны освобождаться автоматически. Тем не менее, для того чтобы не возникло проблем, связанных с недостатком ресурсов из-за ошибок в программном обеспечении, мы рекомендуем Вам освобождать все ненужные ресурсы явным образом. Далее, во втором фрагменте сценария, при помощи функции mysql_query выдается команда SELECT: $sql="SELECT id, title FROM
city"; Выборка результата выполнения этой команды и оформление его в виде строк таблицы выполняется в цикле при помощи функции mysql_fetch_object, описанной ранее: while($row=mysql_fetch_object($result)) Обратите внимание, что для получения значений из полей id и title мы обращаемся к соответствующим свойствам объекта row. Этот объект представляет собой очередную строку результата выполнения запроса, извлеченную функцией mysql_fetch_object. Перед завершением своей работы сценарий освобождает ресурсы, полученные для хранения результата выполнения команды SELECT, а затем закрывает соединение с сервером базы данных: mysql_free_result($result); Внешний вид страницы, созданной только что описанным сценарием, показан на рис. 11‑1.
Рис. 11-1. Просмотр содержимого таблицы средствами PHP Как правило, все функции MySQL возвращают признак, по которому можно судить об успехе их выполнения. Однако чтобы получить расширенную информацию об ошибках, нужно использовать другие, специально предназначенные для этого функции. Функция mysql_error возвращает текстовое описание ошибки: string mysql_error([resource link_identifier]) Если она вызывается без параметров, то возвращает строку сообщения для только что выполненной функции MySQL. При необходимости Вы можете передать ей в качестве единственного параметра идентификатор соединения, для которого нужно получить сообщение об ошибке. Если проверяемая функция MySQL завершилась без ошибок, функция mysql_error возвращает пустую текстовую строку. С помощью функции mysql_errno, вызываемой аналогично только что рассмотренной функции mysql_error, можно получить числовой код ошибки: int mysql_errno([resource link_identifier]) Вот пример использования функции mysql_error: if(!mysql_select_db("trudogolik",
$conn)) Применение шаблонов HTML В только что рассмотренном примере строки сценария PHP были встроены непосредственно в текст документа HTML. Как мы уже говорили, такая техника оправдана только для создания относительно простых Web-приложений, содержащих немного страниц. Если же Вы занимаетесь разработкой сложных Web-проектов, рассмотрите возможность использования шаблонов HTML. Это позволит отделить дизайн страниц от программного кода. В этом разделе мы расскажем об использовании класса Template, описанного нами в предыдущей главе и предназначенного для работы с шаблонами HTML, применительно к базам данных MySQL. Мы рассмотрим как обычную технику использования шаблонов при решении задачи просмотра содержимого таблицы, так и методы, определенные в классе Template специально для работы с базами данных MySQL. Обычная техника использования шаблонов Покажем, как можно сформировать страницу просмотра содержимого таблицы (рис. 11-1) с использованием классической техники шаблонов HTML. Для этого примера мы подготовили файл шаблона и файл со сценарием PHP. В листинге 11-2 приведен исходный текст шаблона, предназначенного для формирования страницы. Как можно заметить, в этом шаблоне нет ни одной строки программного кода PHP.
Листинг 11-2 Вы найдете в файле chap10\www.php.test\root\mysql\sample2\template.html на прилагаемом к книге компакт-диске. <html> В начале файла шаблонов мы определили два условных шаблона с именами dberror1 и dberror2: <if NAME="dberror1"> Эти шаблоны вступают в действие при возникновении ошибок, имеющих отношение к базе данных. Первый шаблон вставляет сообщение об ошибке, возникающей при подключении сценария к серверу СУБД, а второй — при невозможности установить текущую базу данных. Далее следует еще один, третий условный шаблон с именем dbok: <IF NAME="dbok"> Он используется в том случае, если операции открытия соединения и выбора текущей базы данных прошли без ошибок. Внутри условного шаблона dbok находится определение таблицы: <table border="1"
cellspacing="3" cellpadding="3"> Как видите, строки таблицы формируются с помощью циклического шаблона city. Мы уже рассказывали Вам об использовании циклических шаблонов в предыдущей главе. Файл сценария PHP Исходный текст файла сценария PHP, заполняющего только что описанный шаблон, приведен в листинге 11-3. Листинг 11-3 Вы найдете в файле chap10\www.php.test\root\mysql\sample2\index.php на прилагаемом к книге компакт-диске. <?php В соответствии с поставленной задачей отделения дизайна страницы от программного кода, в файле сценария нет тегов HTML. Получив управление, сценарий инициализирует переменные $dberror1, $dberror2 и $dbok: $dberror1=FALSE; Эти переменные будут привязаны к условным шаблонам, поэтому после такой инициализации все условные шаблоны будут в выключенном состоянии. Далее сценарий открывает соединение с сервером базы данных: $conn=@mysql_connect("localhost",
"trudogolik_adm", "admin"); Если при этом возникает ошибка, сценарий не завершает работу, как это было в предыдущем примере, а выводит в браузер посетителя сформированное Вами сообщение об ошибке. Такое поведение сделает Ваше приложение более профессиональным, так как оно не будет озадачивать посетителей непонятными ему системными сообщениями. Обратите внимание на префикс @, которым мы снабдили имя функции mysql_connect. Напомним, что этот префикс отключает вывод системных сообщений об ошибках, которые лучше «спрятать» от посетителей Web-узла, заменив более понятными и конструктивными сообщениями или рекомендациями. Для заполнения шаблона мы использовали методы класса Template, описанные в предыдущей главе. Вначале с помощью оператора new создается объект класса Template. Далее методом load_file загружается файл шаблона. Для заполнения циклического шаблона необходим метод parse_loop, а для заполнения условного шаблона — метод parse_if. Подготовленный шаблон отправляется посетителю при помощи метода pprint. Продолжим описание сценария. В том случае, если сценарию удалось установить соединение с сервером базы данных, он выбирает текущую базу данных при помощи функции mysql_select_db: if(!mysql_select_db("trudogolik",
$conn)) При возникновении ошибки в переменную $dberror2 заносится значение TRUE, после чего соединение с базой данных закрывается. Далее сценарий выведет сообщение о невозможности выбора текущей базы данных в окно браузера. Если же текущая база данных была выбрана успешно, сценарий выдает запрос SQL, выбирая содержимое таблицы city с помощью команды SELECT: $dbok=TRUE; После этого сценарий извлекает результат запроса в цикле, заполняя циклический шаблон, как это показано ниже: $city = array(); Использованная здесь техника заполнения шаблона была описана в разделе «Циклические шаблоны» предыдущей главы. После завершения цикла сценарий освобождает ресурсы, связанные с выполнением запроса, при помощи функции mysql_free_result. Далее он отправляет заполненный шаблон в браузер посетителя. Специальные методы класса Template Так как огромное количество обращений к базам данных выполняется Web-приложениями с целью получения табличного результата, создатель класса Template предусмотрел специальные методы для упрощения данной задачи. Это методы parse_sql и parse_pgsql. Первый из них предназначен для СУБД MySQL, а второй — для СУБД Postgre SQL, рассмотрение которой выходит за рамки нашей книги. Основная идея применения методов parse_sql и parse_pgsql, имеющих одинаковый набор параметров, состоит в привязке результата запроса SQL к циклическому шаблону <LOOP>. В результате такой привязки отпадает необходимость в явном заполнении циклического шаблона, которое мы делали в предыдущем примере. По сравнению с предыдущим примером мы немного изменили файл шаблона (листинг 11-4). Листинг 11-4 Вы найдете в файле chap10\www.php.test\root\mysql\sample3\template.html на прилагаемом к книге компакт-диске. Изменения касаются только циклического шаблона: <LOOP NAME="result"> Мы изменили имя, а также названия простых переменных шаблона для заполнения столбцов. Теперь имя циклического шаблона задано таким же, как и имя переменной $result, хранящей результат запроса. Что же касается названий простых переменных шаблона для заполнения столбцов, то они такие же, что и соответствующие имена столбцов отображаемой таблицы city (а именно, id и title). Файл сценария PHP Исходный текст файла сценария, заполняющего наш шаблон, Вы найдете в листинге 11-5. Листинг 11-5 Вы найдете в файле chap10\www.php.test\root\mysql\sample3\index.php на прилагаемом к книге компакт-диске. Сравнивая его с исходным текстом сценария, описанного в предыдущем примере (листинг 11-3), мы отметим два важных отличия. Первое касается способа обработки результата запроса: $conn=@mysql_connect("localhost",
"trudogolik_adm", "admin"); Как видите, после выдачи команды SELECT сценарий закрывает соединение с базой данных, никак не обрабатывая полученный и хранящийся в переменной $result результат выполнения этой команды. Однако далее, при подготовке шаблона, сценарий вызывает метод parse_sql, передавая ему в качестве второго параметра имя переменной $result: $tpl = new template; После заполнения циклического шаблона результатами выполнения команды SELECT наш сценарий освобождает ресурсы, выделенный для хранения результатов запроса, вызывая функцию mysql_free_result. Заметим, что эта функция вызывается только в том случае, если в переменной $result действительно хранился результат запроса. Если же при установке соединения с сервером базы данных или при выборе текущей базы данных произошла ошибка, запрос к базе данных не выдавался. Поэтому в этом случае функцию mysql_free_result вызвать нет необходимости. Как видите, использование методов класса Template заметно упрощает обработку запросов SQL с командами SELECT. Сокращается листинг программы и, следовательно, уменьшается вероятность допущения ошибок при ее составлении. Редактирование и просмотр дерева При создании сложных Web-приложений, таких, например, как Интернет-магазины, каталоги и т.п часто встает необходимость работы с иерархически организованными структурами данных. Например, каталог товаров Интернет-магазина обычно имеет древовидную структуру. Для хранения иерархически организованных данных Вы можете использовать обычные реляционные таблицы, поэтому задача создания, просмотра и редактирования деревьев может быть решена с использованием рассмотренных нами ранее методов работы с базами данных. В этом главе мы расскажем об экспертном дереве решений, предназначенном для оказания быстрой помощи пользователям компьютера, испытывающим трудности с аппаратным или программным обеспечением. В узлах такого дерева находятся описания проблем. Отвечая на вопросы и перемещаясь по экспертному дереву от корня до вершины, пользователь выйдет на узел с описанием возможного решения проблемы. Разумеется, такое дерево можно использовать и для решения проблем, не связанных с компьютерами, например, медицинских. В этом случае по симптомам заболевания можно быстро найти врача-специалиста, к которому следует обратиться за помощью в данной ситуации. Страница просмотра нашего экспертного дерева, наполненного демонстрационными данными, показана на рис. 11-2. Рис. 11-2. Просмотр экспертного дерева решений С каждым узлом нашего дерева связано название и некоторый текст. Корневой узел дерева называется «Проблемы с компьютером». Связанный с этим узлом текст объясняет, как пользоваться деревом для решения проблем — достаточно просто выбирать нужные симптомы из списка, щелкая ссылки, расположенные внизу страницы. При этом Вы будете перемещаться по дереву. Например, если Вы полагаете, что проблемы связаны с диском, щелкните ссылку Ошибки при работе с диском (рис. 11-2). При этом в окно браузера будет загружено содержимое узла дерева со списком возможных проблем, имеющих отношение к дисковой памяти компьютера (рис. 11-3). Ссылки [Назад] и [В начало] позволяют, соответственно, перейти к просмотру родительского и корневого узла. Рис. 11-3. Просмотр узла «Ошибки при работе с диском» Достигнув конца ветви, Вы увидите текст рекомендаций по решению данной проблемы. Например, если проблема проявляет себя таким образом, что Вы не можете прочитать дискету, щелкните ссылку Не читается дискета (рис. 11-3). После этого в окне браузера появится текст с рекомендациями на этот случай (рис. 11-4). Рис. 11-4. Рекомендации в случае, если не читается дискета В табл. 11-1 мы привели структуру таблицы experttree, хранящей данные нашего экспертного дерева. Записи этой таблицы соответствуют узлам дерева. Таблица 11-1. Таблица experttree
Как видно из этой таблицы, каждый узел дерева имеет свой уникальный идентификатор id. Для организации обратных ссылок по дереву мы храним идентификаторы родительских узлов в поле ParentNodeID. У корневого узла в этом поле хранится нулевое значение, не являющееся идентификатором ни одного другого узла. Заметим, что каждый узел дерева может иметь несколько дочерних узлов. Поэтому в таблице experttree могут существовать группы записей, имеющих одинаковые значения поля ParentNodeID. В то же время мы допускаем существование только одного корневого узла. Поля Title и Text — текстовые. Первое из них определяет заголовок узла, отображающийся в верхней части страницы просмотра содержимого узла. Например, на рис. 11‑3 заголовком узла является строка «Ошибки при работе с диском», а на рис. 11-4 — строка «Не читается дискета». Второе поле содержит текст с описанием симптомов (рис. 11-3) или рекомендаций (рис. 11-4) и отображается сразу после заголовка узла. Ниже мы привели исходный текст сценария SQL, при помощи которого можно создать таблицу experttree: CREATE TABLE experttree ( Как видите, поле id является уникальным и играет роль первичного ключа. Для него задан режим автоматического увеличения значения при добавлении записи в таблицу. Приложение для редактирования дерева Для создания и редактирования дерева мы подготовили страницу, показанную на рис. 11-5. Как видите, она очень напоминает страницу просмотра дерева (рис. 11-2), но предоставляет дополнительные возможности. Рис. 11-5. Страница редактирования дерева Во-первых, рядом с заголовком узла в скобках отображается его числовой идентификатор, взятый из поля id. Во-вторых, на странице имеются две формы, с помощью которых можно изменять заголовок и текст узла, а также создавать новые дочерние узлы для текущего узла. И, наконец, в третьих, при помощи ссылок [Удалить] можно уничтожить узел дерева, не имеющий дочерних узлов. Заметим, что наше приложение не реализует функцию перемещения дочерних узлов. При необходимости Вы можете дополнить его этой функцией самостоятельно. При помощи ссылок [Назад] и [В начало] можно перейти к просмотру, соответственно, родительского и корневого узла. Сценарии PHP редактирования дерева В листинге 11-6 мы привели исходный текст сценария PHP, при помощи которого создается и отображается страница редактирования дерева, показанная на рис. 11-5. Листинг 11-6 Вы найдете в файле chap10\www.php.test\root\tree\admin\admin.php на прилагаемом к книге компакт-диске. Рассмотрим его в деталях. В начале файла сценария мы подключаем класс шаблонов, так как он необходим для отображения страницы: include('class.template.inc'); При запуске сценария ему методом GET передается номер отображаемого узла или строка «Root», если нужно отобразить корневой узел дерева. Получив управление, сценарий извлекает этот номер и сохраняет его в переменной $CurrentNodeID: $CurrentNodeID=$HTTP_GET_VARS['CurrentNodeID']; Если сценарию передается нулевой номер узла, он заменяется строкой «Root»: if($CurrentNodeID == 0) Далее сценарий записывает значение FALSE в переменные, предназначенные для обработки ошибок, возникающих при обращении к базе данных: $dberror1=FALSE; На следующем этапе сценарий пытается установить соединение с сервером базы данных и сделать текущей базу данных datarecovery, содержащую таблицу с данными нашего дерева: $conn=@mysql_connect("localhost",
"trudogolik_adm", "admin"); Если текущая база была выбрана без ошибок, сценарий формирует строку SQL, необходимую для выбора просматриваемого узла дерева: if(!strcmp($CurrentNodeID,
"Root")) Для выборки корневого узла мы используем условие ParentNodeID=0, так как только корневой узел отмечен нулевым значением в поле ссылки на родительский узел. В остальных случаях в строке запроса используется заданный номер узла. Далее запрос выполняется при помощи функции mysql_query: $result=@mysql_query($sql); Когда сценарий запускается в первый раз и таблица с данными дерева пустая, мы выполняем начальную инициализацию, создавая корневой узел дерева: if(!@mysql_num_rows($result)
&& !strcmp($CurrentNodeID,
"Root")) Здесь мы просто создаем одну запись в таблице experttree, записывая в поле ParentNodeID нулевое значение. Поля заголовка и текста, хранящегося в узле, инициализируются, соответственно, строками «'Заголовок корневого узла» и «'Текст корневого узла». Впоследствии Вы сможете отредактировать эти строки. В том случае, когда таблица уже содержит записи, наш сценарий отображает их на странице при помощи шаблонов. Предварительно результат запроса извлекается при помощи функции mysql_fetch_object: $row=mysql_fetch_object($result); Далее сценарий получает список дочерних узлов текущего узла для отображения в нижней части страницы редактирования дерева: $sql="SELECT id, Title, Text FROM
experttree WHERE ParentNodeID=$f_id"; После выполнения всех операций сценарий закрывает соединение с базой данных: @mysql_close($conn); Далее сценарий создает и отображает шаблон страницы: $tpl = new template; Здесь используется файл шаблона admin_template.html, исходный текст которого Вы найдете в листинге 11-7. Листинг 11-7 Вы найдете в файле chap10\www.php.test\root\tree\admin\admin_template.html на прилагаемом к книге компакт-диске. В самом начале файла находятся простые шаблоны, предусмотренные для отображения сообщений об ошибках при работе с базой данных: <if NAME="dberror1"> Сразу после заголовка страницы в файле имеются две ссылки ([Назад] и [Перейти в корень дерева]), первая из которых предназначена для просмотра родительского узла, а вторая — корневого узла дерева: <h1>Редактирование дерева
решений</h1> Для перехода к просмотру родительского узла вызывается сценарий admin_goto_ParentNode.php, о котором мы расскажем ниже в этой главе, а для перехода к просмотру корневого узла — только что рассмотренный сценарий admin.php. При этом ему передается параметр CurrentNodeID со значением Root. Далее в файле шаблона расположена форма, с помощью которой можно отредактировать заголовок и текст данного узла: <h2>Текущий узел ({f_id})</h2> Как видите, редактирование выполняется сценарием admin_update_node.php, рассказ о котором также еще впереди. Помимо строк заголовка и текста узла этому сценарию через скрытые поля передается идентификатор обновляемого узла. Продолжим изучение файла шаблона. В нижней части файла располагается циклический шаблон, предназначенный для отображения всех дочерних узлов данного узла: <h2>Список дочерних
узлов</h2> Обратите внимание, что в теле этого шаблона создаются ссылки на сценарий admin_goto_node.php, предназначенные для перехода к просмотру дочерних узлов, а также ссылки на сценарий admin_delete_node.php, предназначенный для удаления узлов дерева. И, наконец, в самом конце файла шаблона имеется форма, предназначенная для создания дочерних узлов в текущем узле дерева: <h2>Новый дочерний узел</h2> Здесь для создания дочернего узла вызывается сценарий admin_create_node.php, которому помимо заголовка и текста узла передается идентификатор текущего узла. Теперь, когда мы рассмотрели основной сценарий, создающий и отображающий главную страницу редактирования дерева, перейдем к дополнительным сценариям, вызываемым из этой страницы. Сценарий admin_goto_ParentNode.php нужен для переходя к просмотру узла, заданного своим идентификатором. Исходный текст этого сценарий приведен в листинге 11-8. Листинг 11-8 Вы найдете в файле chap10\www.php.test\root\tree\admin\admin_goto_ParentNode.php на прилагаемом к книге компакт-диске. Идентификатор узла, к просмотру которого необходимо перейти, передается сценарию методом GET через параметр ID. Сценарий сохраняет этот параметр в переменной $form_ID: $form_ID=$HTTP_GET_VARS['ID']; Далее сценарий устанавливает соединение с сервером базы данных и выбирает в качестве текущей базу данных datarecovery: $dberror1=FALSE; $dberror2=FALSE; Далее сценарий выбирает из таблицы experttree идентификатор родительского узла для узла с заданным идентификатором: $sql="SELECT ParentNodeID FROM
experttree WHERE id=$form_ID"; Если в результате запроса не было найдено ни одной подходящей записи, сценарий загружает в браузер страницу с содержимым корневого узла дерева: if(!@mysql_num_rows($result)) В противном случае извлекается идентификатор родительского узла, который затем используется для загрузки при помощи сценария admin.php: else Редактирование заголовка и текста узла Для редактирования заголовка и текста узла используется сценарий admin_update_node.php, исходный текст которого приведен в листинге 11-9. Листинг 11-9 Вы найдете в файле chap10\www.php.test\root\tree\admin\admin_update_node.php на прилагаемом к книге компакт-диске. Получив управление, этот сценарий извлекает строки заголовка и текста узла, преобразуя их при помощи функций trim и htmlspecialchars: $form_title=htmlspecialchars(trim($HTTP_POST_VARS['Title']),
ENT_QUOTES); Первая из этих функций удаляет лишние пробелы в начале и на конце строк, а вторая заменяет специальные символы символьными объектами HTML. Это необходимо для правильного отображения текста, содержащего кавычки, угловые скобки и другие специальные символы HTML. Идентификатор редактируемого узла сохраняется в переменной $form_ID: $form_ID=$HTTP_POST_VARS['ID']; Далее наш сценарий устанавливает соединение с базой данных и обновляет содержимое нужной строки таблицы с помощью оператора UPDATE: $dberror1=FALSE; $dberror2=FALSE; После этого в браузер загружается содержимое обновляемого узла: header("Location: admin.php?CurrentNodeID=$form_CurrentNodeID"); Дочерни узел создается при помощи сценария admin_create_node.php, приведенного в листинге 11-10. Листинг 11-10 Вы найдете в файле chap10\www.php.test\root\tree\admin\admin_create_node.php на прилагаемом к книге компакт-диске. Прежде всего, сценарий извлекает заголовок и текст узла, а также идентификатор текущего узла, сохраняя все это в соответствующих переменных: $form_title=htmlspecialchars(trim($HTTP_POST_VARS['title']),
ENT_QUOTES); Перед сохранением заголовок и текст узла обрабатывается функциями trim и htmlspecialchars, о которых мы только что говорили. Далее сценарий открывает соединения с базой данных и добавляет запись в таблицу experttree с помощью оператора INSERT: $dberror1=FALSE; $dberror2=FALSE; После выполнения этой операции в окно браузера загружается текущий узел: header("Location: admin.php?CurrentNodeID=$form_CurrentNodeID"); Удаление выбранного узла дерева выполняется сценарием admin_delete_node.php (листинг 11‑11). Заметим, что этот сценарий удаляет только узлы, не имеющие дочерних узлов. Листинг 11-11 Вы найдете в файле chap10\www.php.test\root\tree\admin\admin_delete_node.php на прилагаемом к книге компакт-диске. Получив управление, сценарий извлекает идентификатор удаляемого узла и сохраняет его в переменной $form_ID: $form_ID=$HTTP_GET_VARS['ID']; Далее он устанавливает соединение с базой данных и выбирает текущую базу данных: $dberror1=FALSE; $dberror2=FALSE; Если эта операция была выполнена без ошибок, сценарий проверяет наличие дочерних узлов у текущего узла. Для этого используется запрос следующего вида: $sql="SELECT id FROM experttree
WHERE ParentNodeID=$form_ID"; Если таблица не содержит узлов, у которых в качестве родительского в поле ParentNodeID указан удаляемый узел, выполняется удаление: if(!@mysql_num_rows($result)) Далее сценарий закрывает соединение с базой данных и загружает в браузер страницу, отображающую содержимое текущего узла: @mysql_close($conn); Приложение для просмотра дерева Внешний вид страниц Web-приложения, предназначенного для просмотра экспертного дерева решений, был представлен ранее на рис. 11-2, 11-3 и 11-4. Исходные тексты этого приложения во многом повторяют исходные тексты только что рассмотренного приложения, позволяющего редактировать дерево. В листинге 11-12 приведен исходный текст сценария PHP, предназначенного для просмотра узлов экспертного дерева. Листинг 11-12 Вы найдете в файле chap10\www.php.test\root\tree\user\index.php на прилагаемом к книге компакт-диске. Для создания страницы просмотра мы используем шаблоны HTML, поэтому в начале файла сценария включаем определение класса class.template.inc: include('class.template.inc'); Получив управление, наш сценарий извлекает методом GET идентификатор просматриваемого узла и сохраняет его в переменной $CurrentNodeID: $CurrentNodeID=@$HTTP_GET_VARS['CurrentNodeID']; Если значение этого идентификатора равно нулю, необходимо просмотреть корневой узел. В этом случае мы заменяем нулевое значение на строку Root: if($CurrentNodeID == 0) Далее сценарий устанавливает соединение с базой данных и выбирает текущую базу данных: $dberror1=FALSE; В случае успеха этой операции сценарий формирует строку запроса SQL, выбирающую информацию о текущем узле и запускает ее на выполнение: if(!strcmp($CurrentNodeID,
"Root")) Полученные в результате выполнения запроса идентификатор, заголовок и текст узла сохраняется в соответствующих переменных: $row=mysql_fetch_object($result); На следующем шаге сценарий извлекает из базы данных список дочерних узлов текущего узла. $sql="SELECT id, Title, Text FROM
experttree WHERE ParentNodeID=$f_id"; Полученная информация используется для заполнения шаблона HTML страницы просмотра содержимого узла template.html: $tpl = new template; Исходный текст только что упомянутого шаблона Вы найдете в листинге 11-13. Листинг 11-13 Вы найдете в файле chap10\www.php.test\root\tree\user\template.html на прилагаемом к книге компакт-диске. В начале файла находятся простые шаблоны, предназначенные для отображения сообщений об ошибках при работе с базой данных: <if NAME="dberror1"> Далее следуют ссылки [Назад] и [В начало] на сценарии PHP, с помощью которых выполняется переход к просмотру родительского узла дерева и корневого узла дерева: <h2>Экспертное дерево решений</h2> Первая из этих задач решается с помощью сценария goto_ParentNode.php, который мы рассмотрим ниже в этой главе, а вторая — с помощью ранее рассмотренного сценария index.php (листинг 11-12). Ниже ссылок после разделительной черты располагаются шаблоны заголовка и текста узла: <hr> И, наконец, в самом низу файла имеется циклический шаблон, с помощью которого на странице отображается список дочерних узлов текущего узла: <hr> Для перехода к просмотру дочернего узла здесь используется сценарий goto_node.php, о котором мы расскажем ниже. Исходный текст сценария goto_node.php, предназначенного для просмотра содержимого дочернего узла представлен в листинге 11-14: Листинг 11-14 Вы найдете в файле chap10\www.php.test\root\tree\user\goto_node.php на прилагаемом к книге компакт-диске. <?php Как видите, этот сценарий очень прост. Все, что он делает, это извлечение методом GET идентификатора дочернего узла и передача этого идентификатора сценарию index.php, осуществляющего просмотр содержимого узла. Исходный текст сценария goto_ParentNode.php, предназначенного для просмотра содержимого родительского узла мы привели в листинге 11-15: Листинг 11-15 Вы найдете в файле chap10\www.php.test\root\tree\user\goto_ParentNode.php на прилагаемом к книге компакт-диске. Сценарий goto_ParentNode.php практически полностью повторяет аналогичный сценарий ранее рассмотренного в этой главе приложения, предназначенного для редактирования дерева (листинг 11-8), поэтому мы не будем повторять его детальное описание. |