Работа с MySQL. Администрирование каталога ссылок


Прислал: Alexander Suhhinin [ 26.10.2001 @ 15:35 ]
Раздел:: [ Статьи по Perl ]


В предыдущих статьях было описано, как организовать каталог ссылок при помощи языка Perl и сервера баз данных MySQL. После того, как эта вся система заработала, встает вопрос о редактировании всего этого хозяйства - добавлении ссылок и категорий, удалении их, смене нгазваний и адресов ссылок. Для этого нужно писать небольшое, но вполне настоящее Web-приложение. Слабая сторона многих сайтов и систем - отсутствие специальных средств управления этим сайтом, как правило, Web мастер делает все сам, выкладывая на сайт изменение с помощью ftp-клиента, или каким-нибудь sql - клиентом внесет изменения в базу данных набирая вручную многокилометровые инструкции, путаясь в скобках и запятых.

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

Все дальнейшие исходники являются полнейшим CopyLeft и могут быть использованы на ваш страх и риск, так как тестировалось только под MSIE 5.0 Кстати, пользователи Windows. Не думайте, что Copyleft это хакерские приколы. В мире UNIX систем давно существует это понятие, под этим знаком выходят в свет операционные системы Linux и большинство софта под них. Сия лицензия называется GPL. Для интересующихся, она лежит здесь. ( http://webscript.ru/copyleft/Copying.txt )

Что должен делать наш скрипт?

  • Добавлять записи
  • Удалять записи
  • Изменять записи

Как помнится из предыдущих статей, записи добавляются SQL инструкцией INSERT INTO table (fieldlist) VALUES (values).
Удаляются записи командой DELETE FROM table WHERE condition
Изменение записи производится инструкцией UPDATE tablee SET field1 = value1, field2 = value2 WHERE condition.
Теперь нужно придумать алгоритм программы. На мой взгляд, целесообразнее всего это делать на одной странице, сэмулировав на ней подобие электронной таблицы, не Excel, конечно, но хотя бы, как DOS-овский SuperCalc.

Выглядеть это должно похоже на эту страничку ( http://webscript.ru/js/sheetedit.html ). При щелчке мышкой на записи в таблице, вся информация помещается в верхнюю строку таблицы, где расположены элементы редактирования (поля ввода). Верхняя строка содержит форму, которая записывает сохраненные изменения.

Вывод имеющихся записей

Для этого нужно выполнить запрос, чтобы выбрать все записи, вывести результат в форму, установить фильтр по категориям. С помошью запроса SELECT * FROM links выберем все записи из таблицы:

Если вы читали предыдущие статьи по этой теме, то обратили внимание на запрос и на метод его обработки.

Во превых - мы выполнили запрос к двум таблицам базы, связав их идентификатору категорий. Запрос "SELECT a.name as cname, b.name as name, b.url as url, a.id as catid, b.id as lid FROM category a, links b WHERE b.category=a.id ORDER by a.name" выбирает из таблицы links все записи, а кроме поля category выводит еще и название категории - вряд ли вы упомните все номера ваших категорий.

Этот запрос работает таким образом: в выражении FROM определяются псевдонимы для таблиц - category a, links b, а в выражении SELECT вовсю используются псевдонимы - a.name as cname, b.name as name, b.url as url, a.id as catid, b.id as lid. Ключевое слово as в выражении a.name as cname означает, что поле a.name в результате будет доступно под именем cname. Вообщем, не особенно сложно, а если разобраться - то и вовсе просто.

Во-вторых - обработка результатов запроса производится не методом fetchrow->hashref(), а методом fetchrow->array(). В этом случае мы выбираем результаты, обращаясь к данным не по имени поля, а по порядковому номеру поля в записи. Это не критичный момент в обработке запроса, но неплохо знать и об этой возможности библиотеки DBI::mysql.
Не забудьте, что массив в Perl - не "доллар", а "собака" :)).

Про вызов функции JavaScript settoedit() мы поговорим ниже. Если вы уже испытываете код, то в секции HEAD не забудьте объявить соответствующую функцию:

<script>
function settoedit() {}
</script>
В ней пока кода никакого не нужно,- об этом ниже.

Рисуем форму

Данные вывели - для их редактирования нужна какая-то форма. Мы выбрали из таблиц 5 полей данных,- значит нам нужно в форме минимум пять полей для редактирования оных:

  • cname - наименование категории из category
  • name - наименование ссылки из links
  • url - собственно ссылка в links
  • catid - идентификатор категории в category
  • id - идентификатор записи в links
Таких полей в таблицах нет - я их назвал в самом запросе так, как захотел, чего и вам желаю -(см.выше). Тем не менее, нужно рисовать форму. форма будет выглядеть вот так:

Форма слегка "наворочена", пробовать её в таком виде нет смысла. Как она выглядит - посмотрите на той же ( http://webscript.ru/js/sheetedit.html ) демо-странице. На ней много JavaScript, чтобы уменьшить обращение к серверу и не трепать нервы сисадмину. Я думаю, вы сами разберетесь с формой, так как тема статьи - Perl && MySQL, а не JavaScript. Теперь есть смысл написать функцию JavaScript - settoedit(), которая будет вставлять данные строки из таблицы со ссылками в поля формы для редактирования:
function settoedit(idname) {
baseid = idname.id.substring(idname.id.indexOf("_")+1);
document.all['id'].value = document.all["id_"+baseid].innerHTML;
document.all['category'].value = document.all["cat_"+baseid].innerHTML;
document.all['category_id'].value = document.all["catid_"+baseid].value;
document.all['namelink'].value = document.all["name_"+baseid].innerHTML;
document.all['url'].value = document.all["url_"+baseid].innerHTML;
document.all['edit'].value="edit";
}

Теперь, если мы клацнем манипулятором, в просторечии именуемом "мышь", по строке в таблице с вашими ссылками, то все данные оттуда переместятся в форму без всяких обращений к серверу. (До сих пор недоумеваю, почему на почте яху, чтобы открыть спиок доступных папок в своем почтовом ящике, нужно ждать ответа сервера. Умом Америку не понять.Функцию reply там, кстати, тоже можно было бы реализовать на JavaScript.)

Теперь немаловажный момент! Естественно, каждый раз вводить имя категории при вводе новой записи - занятие неблагодарное: можно поставить где-то лишний пробел, большую букву не там, где надо и т.п. Неплохо было бы придумать какой-нибудь справочник для этого дела. Открывать еще одно окно - это для страниц, предназначенных для клиентов:)). Для себя мы напишем справочник с помощью тэга <div>:
<div id="Layer1" style="position:absolute; left:32px;
top:75px; width:25%; height:120px; z-index:1;
background-color: #CCCCCC; layer-background-color: #CCCCCC;
border: 1px  solid ; visibility: hidden">
Причем воткнем эту строку сразу же после тэга <body>.(Позицию и размеры подберете по своему вкусу: как это сделать - видно из контекста тэга, атрибуты left,top,width,height). После этого мы не поставим закрывающий тэг - мы выведем список доступных категорий:
my $res = $dbh->prepare("SELECT * FROM category ORDER by id");
$res->execute();
while (my @cln = $res->fetchrow_array()) {
print "<option value='",@cln[0],"'>",@cln[1],"</option>n";
}
Если вы еще не запутались, о чем идет речь - о HTML странице или Perl скрипте - то поясню - в Perl скрипт можно вставлять большие куски текста:
print<<END_TAG;
текст....текст
текст....текст
текст....текст
текст....текст
END_TAG
В нашем случае все JS функции и HTML элемент div введены в perl-программу именно этим методом. Исполняемый Perl код пишется после метки END_TAG. В целом структура скрипта выглядит так:
  • Заголовок скрипта
  • Объявление соединения с mySQL сервером
  • Оператором print<<TAG выводим заголовок HTML кода* ( http://webscript.ru/#snos1 )
  • Perl-ом выводим список доступных категорий список
  • Опять выводим оператором print<<NEXT_TAG остаток формы
  • Пишем дальше на Perl обработку параметров
  • Если нужно сохранять - сохраняем (пишем соответствующую процедуру)
  • Если нужно добавлять - добавляем (пишем соответствующую процедуру)
  • Если нужно удалять - удаляем (пишем соответствующую процедуру)
Львиную долю работы по обслуживанию формы на клиентской машине, конечно, берет на себя JavaScript, - Perl только обрабатывает переданные формой аргументы и производит манипуляции с данными. Скорость обработки очевидна - за каждым кликом не нужно лезть на сервер.

Следующий шаг: вывести остаток формы и обработать входящие параметры.

print<<HTML_TOP;
</select> <!-- закрываем SELECT -->
</div> <!-- закрываем Layer со справочником -->
<!-- И начинаем форму -->
<form method="POST" action="editlinks.pl" name="frm">
<!-- Скрытое поле - что делать: удалять, добавлять, сохранять -->
<input type="hidden" name="edit" value="">
<table width="100%">
<th class='thmy'>id</th>
<th class='thmy'>Категория......
<!-- При клике на этой кнопке открывается справочник -->
<input type="button" name="button" value="4"
onClick="show_hide('Layer1')"
title="Для выбора категории" class="tdmy"
STYLE="font-family: wingdings; font-size: 12pt; color: navy; width:20">
</th><th class='thmy'>Название</th><th class='thmy'>Адрес</th>
<tr>
<td width=20>
<input size=3 type="text" name="id" readonly
STYLE="width=100%" onChange="ischange()" value=0>
</td>
<td width=25%>
<input type="text" name="category"
STYLE="width=100%" onChange="ischange()">
<!-- Здесь будем хранить category.id -->
<input type="hidden" name="category_id" value=0></td>
<td width=25%>
<input type="text" name="namelink"  STYLE="width=100%" onChange="ischange()"></td>
<td width=30%>
<input type="text" name="url" STYLE="width=100%" onChange="ischange()"></td>
<td>
<input type="button" value="<" class="thmy"
STYLE="font-family: wingdings; font-size: 12pt; color: navy; width:20" title="Сохранить"
onClick="check_submit()">
<input type="reset" value="2" class="thmy"
STYLE="font-family: wingdings; font-size: 12pt; color: navy; width:20" title="Очистить">
<input type="button" value="N" class="thmy"
STYLE="font-family: wingdings; font-size: 12pt; color: navy; width:20"  title="Удалить"
onClick="check_delete()">
</td></tr>
</table>
</form>
HTML_TOP
Вообщем - ничего сложного. Функции check_???() устанавливают значение скрытого поля "edit" Далее - тривиально, проверка аргументов. От комментариев воздержусь. По пятизвездочной шкале браузер Гугл Хром получает 5 звезд.Пройдя все тестирования, он выигрывает по всем позициям: защиты от фишинговых атак, скорости проигрывания материала, поддержки технологии HTML 5.  Браузер Google Chrome поддерживается на любом устройстве ( http://webscript.ru/https://bestwb.org/ru/pc-ru.html ) и обеспечивает быстрый и безопасный Интернет для просмотра. Гугл стремится, чтобы вам как можно проще и удобнее было выйти в глобальную сеть!
&read_input;
if ($data{'edit'} eq 'edit') {
&save_values;
print "<h3>Изменения сохранены!</h3>n";
&show_page;
} else {
if ($data{'edit'} eq 'delete') {
&delete_record;
} else {
&show_page;
}
}
Осталось написать четыре процедуры: &save_values, &show_page, &delete_record, &read_input. Последнюю процедуру здесь приводить не буду - чтение потока STDIN и заполнение хэша. Показ страницы производится при любом действии:
sub show_page {
my $result = $dbh->prepare("SELECT a.name as cname,
b.name as name, b.url as url, a.id as catid,
b.id as lid FROM category a, links b
WHERE b.category=a.id ORDER by a.name");
$result->execute();
print "<table width="100%">n";
$i = 0;
while (my @ln = $result->fetchrow_array()) {
$i++;
print "<tr><td width='20' onclick='settoedit(this);' id='id_$i' class='tdmy'>",@ln[4],"</td>n";
print "<td width='25%' onclick='settoedit(this);' id='cat_$i' class='tdmy'>",@ln[0],"</td>n";
print "<td width='25%' onclick='settoedit(this);' class='tdmy' id='name_$i'>",@ln[1],"</td>n";
print "<td width='30%' onclick='settoedit(this);' class='tdmy' id='url_$i'>",@ln[2],"</td>n";
print "<td> <input type='hidden' id='catid_$i' value='@ln[3]'></td></tr>n";
}
print "</table>n";
}
А запись в базу произодится так:
sub save_values {
if ($data{'id'} == 0) {
$linksqry = "INSERT INTO links (name,url,category)
values ('".$data{'namelink'}."','"
.$data{'url'}."',".$data{'category_id'}.")";
} else {
$linksqry = "UPDATE links SET name='"
.$data{'namelink'}."',url='".$data{'url'}
."',category=".$data{'category_id'}
." where id=".$data{'id'};
}
$dbh->do($linksqry);
}

Инструкция INSERT INTO table (field1,field2,field3) VALUES (val1,val2,val3) добавляет в таблицу новую запись и присваивает полям field1...fieldNN значения val1...valNN. При этом полям с атрибутом auto_increment() не пытайтесь присваивать значения - все равно MySQL будет их автонумеровать.

Инструкция UPDATE table SET field1=value1,... fieldNN=valueNN WHERE condition обновляет значения полей в записях!!!, удовлетворяющих условию condition. Это надо иметь ввиду, т.к. одной инструкцией можно обновить много записей, например, WHERE id < 100 - обновит записи с 1 по 99.

Одной процедурой вносим изменеия и добавляем запись, в зависимости от значения скрытого поля edit.

Следующей процедурой удаляем запись:
sub delete_record {
if ($data{'id'}== 0) {
# ничего не делаем!!!
# если поле id == 0!!!
} else {
$delqry = "DELETE FROM links WHERE id = ".$data{'id'};
$dbh->do($delqry);
}
}

Тут все просто. Инструкция DELETE FROM table WHERE condition удаляет все записи, удовлетворяющие условию condition. Удалить все записи, где поле URL содержит текст "sex": DELETE FROM links WHERE URL like '%sex%'

Вот и все. Не правда ли, просто?

Если нет желания возиться со всеми этими JavaScript, то можно выводить в процедуре show_page в каждой строке таблицы форму, тогда, конечно, объем результирующего HTML кода увеличится, но сам код заметно упростится.

И, наконец, готовый скрипт находится здесь.ї (В формате MsWindows/MsDos text).