В предыдущих статьях, мы рассмотрели теорию хранения и управления древовидных структур данных, а так же реализовали Perl модуль для облегчения управления ими. Теперь напишем небольшой скрипт упраления (администрирования). Идея скрипта проста - требуется легко и непринужденно, с помощью скрипта, управлять деревом каталогов.
Возьмем стандартную таблицу в которой будет хранится наше дерево каталогов, в ней мы будем хранить только одно дерево, реализацию принципа работы, при котором количество деревьев неограничено, я рассмотрю в следующих статьях. Итак, наша таблица:
CREATE TABLE my_tree ( id INT(11) NOT NULL AUTO_INCREMENT, left_key INT(11) NOT NULL DEFAULT 0, right_key INT(11) NOT NULL DEFAULT 0, level INT(11) NOT NULL DEFAULT 1, name VARCHAR(150) NOT NULL, PRIMARY KEY id (id), INDEX left_key (left_key, right_key, level) );
Конечно, количество дополнительных полей (одно из них - name) может быть неограничено. Концепция
Для начала определим, какие функции должен выполнять наш скрипт:
* создание узла; * редактирование узла (с возможностью изменения подчиненности); * удаление узла; * перемещение узла на уровень вверх; * перемещение узла на уровень вниз; * перемещение узла на порядок вверх (в пределах подчиненности); * перемещение узла на порядок вниз (в пределах подчиеннности);
Ограничение доступа - вообще не учитывал, то есть авторизация скрипта, проверка доступа - просто отсутсвуют. Оставлю это на Вашей совести. А вообще, проще всего, просто запаролировать директирию скрипта .htaccess и все...
HTML шаблон*, я все-таки вынес из скрипта - терпеть не могу править HTML в скрипте, и Вам того не советую. Шаблон состоит из трех частей:
* верхняя часть (header.html) - заголовки и прочая до момента вывода списка категорий (начало таблицы); * строка списка категорий (row.html) - одна строка таблицы списка категорий; * нижняя часть (footer.html) - конец таблицы вывода списка категорий, форма создания, редактирования категории;
* Эта структура шаблонов была придумана "на ходу", поэтому не будем заострять внимание на её правильности, не это важно. HTML-код шаблонов:
* В первом файле включен JavaSript, так как форма редактирования и создания одна, то при нажатии на ссылку "изменить" нужно внести соответствующие данные в форму. По кнопке "Вернуть" формы, требуется очистить форму от данных редактируемого узла; * текст заключенный в квадратные скобки и знак доллара ([$текст$]), то что будет динамически заменяться нашим скриптом, где: o [$id$] - идентификатор узла; o [$name$] - поле name узла, или имя узла; o [$prefix$] - отступ на который смещается имя узла (зависит от уровня узла); o [$par$] - порядковый номер родительского узла в select формы редактирования (не путать с id родительского узла!); o [$ac$] - случайный набор символов, я буду использовать текущее время (против кеширования страниц);
Код скрипта
Для начала определим где какие файлы у нас будут лежать:
* cgi-bin/admin_tree/ o lib/ + MP/ # NestedSets.pm o template/ + header.html + row.html + footer.html o admin.pl
Что за файл NestedSets.pm, я думаю, объяснять не нужно (это модуль описанный в предыдущих статьях), с .html файлами - тоже понятно, остался только один файл - admin.pl, его мы как раз и опишем. Итак, код скрипта:
#!/usr/bin/perl # Подключение основных модулей use strict; use CGI; use DBI; use vars $query, $dbh, # объект подключения в базе данных $nested, # объект работы с деревои NestedSets %user_vars; # Глобальные пользовательские переменные
# Подключаем модуль для работы с деревои NestedSets use lib lib/; use Global::NestedSets; # Указываем переменные пользовательские переменные $user_vars{table} = my_tree; # Имя таблицы БД # Коннект к базе $dbh = DBI->connect(DBI:mysql:database=mybase:host=localhost:port=3306, user, password) die $DBI::errstr; # Выбираем переданные данные $query = new CGI; $user_vars{id} = $query->param(id) undef; # Идентификатор узла $user_vars{doing} = $query->param(doing) undef; # Производимое действие # Определяем объект Global::NestedSets $nested = new Global::NestedSets {DBI=>$dbh, table=>$user_vars{table}};
# Если производится какое-либо действие if ($user_vars{doing}) { # Действие - поднять узел на уровень вверх if ($user_vars{doing} eq level_up) { $nested->set_unit_level(unit=>$user_vars{id}, move=>up); # Действие - опустить узел на уровень вниз } elsif ($user_vars{doing} eq level_down) { $nested->set_unit_level(unit=>$user_vars{id}, move=>down); # Действие - поднять узел на порядок вверх } elsif ($user_vars{doing} eq order_up) { $nested->set_unit_order(unit=>$user_vars{id}, move=>up); # Действие - опустить узел на порядок вниз } elsif ($user_vars{doing} eq order_down) { $nested->set_unit_order(unit=>$user_vars{id}, move=>down); # Действие - удалить узел } elsif ($user_vars{doing} eq delete) { $nested->delete_unit(unit=>$user_vars{id}); # Действие - создать узел } elsif ($user_vars{doing} eq new) { # Выбираем данные формы $user_vars{name} = $query->param(name) Новая; $user_vars{parent} = $query->param(parent) root; # Создаем узел в дереве и получаем его ID $user_vars{id} = $nested->insert_unit(under=>$user_vars{parent}); # Обновляем дополнительные поля узла $dbh->do(UPDATE .$user_vars{table}. SET name = .$user_vars{name}. WHERE id = .$user_vars{id}) die $DBI::errstr; # Действие - отредактировать } elsif ($user_vars{doing} eq edit) { # Выбираем данные формы $user_vars{name} = $query->param(name) Новая; $user_vars{parent} = $query->param(parent) root; # Выбираем ID родителя редактируемого узла my $check = ($nested->get_parent_id(unit=>$user_vars{id}))->[0]; # Если меняется родительский узел, то производим перемещение if ($check ne $user_vars{parent}) { $nested->set_unit_under(unit => $user_vars{id}, under => $user_vars{parent}) } # Обновляем дополнительные поля узла $dbh->do(UPDATE .$user_vars{table}. SET name = .$user_vars{name}. WHERE id = .$user_vars{id}) die $DBI::errstr; } }
# Выдаем заголовок браузеру print Content-type: text/html; charset=windows-1251nn; # Открываем шаблон верхней части страницы и выводим его на экран open (HTML, ./template/header.html) die Can not open file header.html!; print <HTML>; close HTML;
# Открываем шаблон строки списка и заносим его в переменную open (HTML, ./template/row.html) die Can not open file row.html!; my $line = join(, <HTML>); close HTML;
# Выбираем полностью все дерево и сортируем по левому ключу my $sql = SELECT id, name, level FROM .$user_vars{table}. ORDER BY left_key; my $sth = $dbh->prepare($sql); $sth->execute() die $DBI::errstr; # Объявляем хеш и переменную (счетчик) с помощью которого будем определять # порядок родительского узла в списке select формы my %par = (0 => 0, root => 0); my $i = 1; # Объявляем переменную для формирования списка select формы my $list_select; while (my $row = $sth->fetchrow_hashref()) { # Копируем шаблон строки во временную переменную my $temp_line = $line; # Формируем переменную для антикеша $$row{ac} = time; # Формируем отступ перед названием узла $$row{prefix} = x ($$row{level} - 1); # Определяем порядок родительского узла в списке select формы $$row{par} = $par{($nested->get_parent_id(unit=>$$row{id}))->[0]}; $par{$$row{id}} = $i; $i++; # Обрабатываем строку заменяя соотвествующие $temp_line =~s /[$(w+)$]/$$row{$1}/g; print $temp_line; $list_select .= <option value=.$$row{id}.>. $$row{prefix}.$$row{name}.</option>; } $sth->finish();
# Открываем шаблон нижней части страницы и записываем его в переменную open (HTML, ./template/footer.html) die Can not open file footer.html!; my $footer = join(, <HTML>); close HTML; # Вносиим в шаблон список select формы $footer =~s /[$list_select$]/$list_select/g; # ... и выводим на экран print $footer; # Все... exit; 1;
Вот собственно и все. Как видно, никаких сложностей нет, сам скрипт размером менее чем сто строк благодаря использованию модуля. Конечно, нужно еще проверить данные формы, может быть, некоторые операции...