Скрипт управления деревом

Автор: Topol Суббота, Август 11th, 2012 Нет комментариев

Рубрика: Perl

В предыдущих статьях, мы рассмотрели теорию хранения и управления древовидных структур данных, а так же реализовали 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-код шаблонов:

header.html

<html><head> <title>Скрипт управления деревом каталогов</title> <meta http-equiv="Content-Type" content="text/html; charset=windows-1251"> <script language="javascript"> function EditCat (IdCat, NameCat, ParentCat) { document.getElementById('TitleForm').innerHTML = 'ИЗМЕНИТЬ КАТЕГОРИЮ'; document.getElementById('buttonForm').value = 'Изменить'; document.FormCategory.id.value = IdCat; document.FormCategory.name.value = NameCat; document.FormCategory.parent.options[ParentCat].selected = true; document.FormCategory.doing.value = 'edit'; } function ClearFormEdit () { document.getElementById('TitleForm').innerHTML = 'ДОБАВИТЬ КАТЕГОРИЮ'; document.getElementById('buttonForm').value = 'Добавить'; document.FormCategory.id.value = 'xx'; document.FormCategory.name.value = 'Новая категория'; document.FormCategory.doing.value = 'new'; } </script> </head> <body> <h1>Скрипт управления деревом каталогов</h1> <h2>Список категорий фирм</h2> <table width="100%" border="0" cellpadding="0" cellspacing="0"> row.html <tr> <td>[$prefix$] [$name$]</td> <td width="80"> <a href="#form" onClick="javascript: EditCat ('[$id$]','[$name$]','[$par$]');"> изменить </a> </td> <td width="80"><a href="?ac=[$ac$]&doing=delete&id=[$id$]">удалить</a></td> <td width="80"><a href="?ac=[$ac$]&doing=level_up&id=[$id$]">влево</a></td> <td width="80"><a href="?ac=[$ac$]&doing=level_down&id=[$id$]">вправо</a></td> <td width="80"><a href="?ac=[$ac$]&doing=order_up&id=[$id$]">вверх</a></td> <td width="80"><a href="?ac=[$ac$]&doing=order_down&id=[$id$]">вниз</a></td> </tr> footer.html </table> <a name="form"></a> <table align="center" width="95%" border="0" cellpadding="0" cellspacing="0"> <form action="?" method="post" name="FormCategory"> <tr><td colspan="2"><h1 id="TitleForm">ДОБАВИТЬ КАТЕГОРИЮ</h1></td></tr> <tr> <td>Название категории</td> <td><input type="text" value="Новая категория" name="name" size="60"></td> </tr> <tr> <td>Подчинение категории</td> <td><select name="parent"> <option value="root">-- Без подчинения --</option> [$list_select$] </select></td> </tr> <tr> <td colspan="2">&nbsp; <input type="hidden" name="id" value="xx"> <input type="hidden" name="doing" value="new"> <input type="hidden" name="ac" value="[$ac$]"> </td> </tr> <tr> <td colspan="2" align="center"> <input type="submit" name="buttonForm" id="buttonForm" value="Добавить"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <input type="reset" value="Вернуть" onClick="javascript: ClearFormEdit ();"> </td> </tr> </form> </table> </body></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'} = '&nbsp;&nbsp;&nbsp;&nbsp;' 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;

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

Источник:  internet-technologies.ru

Оставить комментарий

Чтобы оставлять комментарии Вы должны быть авторизованы.

Похожие посты