PHP: связь "многие ко многим" между двумя таблицами
Задача будет не слишком отличаться от аналогичной для "оффлайновой" СУБД вроде ACCESS, по крайней мере, мы по-прежнему реализуем связь "многие ко многим" как пару связей "один ко многим".
При использовании связки PHP+MySQL нам достаточно продемонстрировать, как извлекать категории для заданного объекта и объекты, относящиеся к заданной категории, а исключение дублирующихся связей можно сделать просто с помощью дополнительного запроса. Структура БД может быть в простейшем случае такой (выполните этот запрос в PHPMyAdmin после создания пустой базы с именем my
):
create table if not exists categories ( id int primary key auto_increment, category varchar(32) ); create table if not exists objects ( id int primary key auto_increment, object varchar(32) ); create table if not exists links ( id_category int, id_object int ); insert into categories (id,category) values (1,'Notes'), (2,'Balls'), (3,'Rating'); insert into objects (id,object) values (1,'Admin'), (2,'User'); insert into links (id_category,id_object) values (1,1), (2,1), (2,2), (3,2);
Заодно добавлено несколько записей и связей, чтоб было что выводить.
Напишем скрипт, показывающий получение всех объектов категории, всех категорий, имеющихся у объекта, а также добавление/удаление связей.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html><head> <meta content="text/html; charset=Windows-1251" http-equiv="content-type"> <title>Many-to-Many Relationship</title> </head><body> <?php define ('DB_HOST','localhost'); //имя хоста //(*) 1 define ('DB_LOGIN','root'); //логин define ('DB_PASSWORD','root'); //пароль define ('DB_NAME','my'); //имя БД mysql_connect (DB_HOST, DB_LOGIN, DB_PASSWORD); mysql_select_db (DB_NAME); $action = ''; $cat = $obj = ''; if (isset($_GET['cat'])) $cat = abs(intval(trim($_GET['cat']))); if (isset($_GET['obj'])) $obj = abs(intval(trim($_GET['obj']))); if (isset($_GET['action'])) $action = htmlspecialchars(trim($_GET['action'])); if (!empty($action)) { if (!empty($cat)) { $result1 = mysql_fetch_array(mysql_query('select id from categories where id='.$cat)); if (!$result1) { $cat=''; echo '<br>Передан несуществующий id категории скорректировано на "пусто"'; } } if (!empty($obj)) { $result2 = mysql_fetch_array(mysql_query('select id from objects where id='.$obj)); //(*) 2 if (!$result2) { $obj=''; echo '<br>Передан несуществующий id объекта скорректировано на "пусто"'; } } } if (!empty($cat) && !empty($obj)) { $sql = 'select * from links where id_category='.$cat.' and id_object='.$obj; $result = mysql_query ($sql); $n = mysql_num_rows ($result); switch ($action) { case 'add': if ($n==0) { $sql = 'insert into links (id_category,id_object) values ('.$cat.','.$obj.')'; $result = mysql_query ($sql); echo '<br>Запрос на добавление выполнен'; } else echo '<br>Связь уже существует, повторно не добавляем'; break; case 'del': if ($n>0) { $sql = 'delete from links where id_category='.$cat.' and id_object='.$obj; $result = mysql_query ($sql); echo '<br>Запрос на удаление выполнен'; } else echo '<br>Связь не задана, нечего удалять'; break; default: echo '<br>'.($n==0?'Не связано':'Связано'); break; } } else { $action='show'; if (!empty($cat) && empty($obj)) $sql = 'select * from categories where id='.$cat.' order by id asc'; else if (empty($cat) && !empty($obj)) $sql = 'select * from objects where id='.$obj.' order by id asc'; else $sql = //(*) 3 'select * from links,categories,objects where links.id_category=categories.id && links.id_object=objects.id'; $result = mysql_query ($sql); if ($result) { $n = mysql_num_rows ($result); if ($n>0) while ($row = mysql_fetch_assoc ($result)) { echo '<br>'; foreach ($row as $key=>$val) echo $key.'=>'.$val.' '; } else echo '<br>Результат запроса пуст (выбрано 0 строк)'; } else echo '<br>Ошибка SQL: '.mysql_error(); } echo '<form method="get" action="'.$_SERVER['PHP_SELF'].'"> <p>Введите натуральные числа или оставьте поля пустыми</p> <p>Категория <input type="text" name="cat" maxlength="2" size="2" value="'.(empty($cat)?'':$cat).'"> Объект <input type="text" name="obj" maxlength="2" size="2" value="'.(empty($obj)?'':$obj).'"> <select name="action" size="1"> <option value="show" '.($action=='show'?'selected':'').'>показать <option value="add" '.($action=='add'?'selected':'').'>связать <option value="del" '.($action=='del'?'selected':'').'>удалить </select> <input type="submit" value="Выполнить"></p> </form></body></html>'; ?>
Примечания к листингу (*)
1. Не забудьте скорректировать данные доступа к БД, заданные константами define
2. Во избежание дублирования кода здесь лучше сделать функцию-обработчик допустимых значений параметров $cat
и $obj
3. В нагруженных скриптах с потенциально большими объёмами данных не стоит делать запросов к двум, а тем более, к трём таблицам. Старайтесь всегда обойтись запросами типа "выбрать все объекты данной категории" или "выбрать все категории для данного объекта"
Демо-скрипт "многие ко многим" в работе
P.S. Ниже показана более современная версия скрипта, рассчитанная на работу с PHP7 и MySQLi вместо MySQL, проверял в XAMPP. Структура самой БД не изменилась, только теперь предполагается, что она создавалась в кодировке Юникода utf-8 (тип сопоставления utf8_general_ci):
Запустить через XAMPP компоненты Apache и MySQL.
- до версии PHP 5.3 включительно СУБД по умолчанию был MySQL;
- с версии 5.4 и в PHP 7.X СУБД называется MySQLi, формат БД не менялся, имена функций для работы с БД – да (везде передаётся идентификатор соединения).
Теперь утилита для управления БД доступна из браузера по адресу: http://localhost/phpmyadmin/
Выбрать "Базы данных", ввести имя базы и кодировку (utf-8 с игнорированием регистра символов при сравнении)
utf-8 с игнорированием регистра символов при сравнении
<!doctype html> <html lang="ru"> <head> <meta charset="utf-8"> <title>Отношение многие-ко-многим</title> </head> <body> <?php define ('DB_HOST','localhost'); //имя хоста //(*) 1 define ('DB_LOGIN','root'); //логин define ('DB_PASSWORD',''); //пароль define ('DB_NAME','my'); //имя БД $mysql_id = mysqli_connect (DB_HOST, DB_LOGIN, DB_PASSWORD); mysqli_select_db ($mysql_id, DB_NAME); $action = ''; //действие $cat = $obj = ''; //категория и объект //(*) 2 if (isset($_REQUEST['cat'])) $cat = abs(intval(trim($_REQUEST['cat']))); if (isset($_REQUEST['obj'])) $obj = abs(intval(trim($_REQUEST['obj']))); if (isset($_REQUEST['action'])) $action = htmlspecialchars(trim($_REQUEST['action'])); if (!empty($action)) { if (!empty($cat)) { $sql = 'select id from categories where id='.$cat; $result1 = mysqli_fetch_array(mysqli_query($mysql_id,$sql)); if (!$result1) { $cat=''; echo '<br>Передан несуществующий id категории скорректировано на "пусто"'; } } if (!empty($obj)) { $sql = 'select id from objects where id='.$obj; $result2 = mysqli_fetch_array(mysqli_query($mysql_id,$sql)); if (!$result2) { $obj=''; echo '<br>Передан несуществующий id объекта скорректировано на "пусто"'; } } } if (!empty($cat) and !empty($obj)) { $sql = 'select * from links where id_category='.$cat.' and id_object='.$obj; $result = mysqli_query ($mysql_id,$sql); $n = mysqli_num_rows ($result); switch ($action) { case 'add': //Запрос на добавление if ($n == 0) { $sql = 'insert into links (id_category,id_object) values ('.$cat.','.$obj.')'; $result = mysqli_query ($mysql_id, $sql); echo '<br>Запрос на добавление выполнен'; } else echo '<br>Связь уже существует, повторно не добавляем'; break; case 'del': //Запрос на удаление if ($n > 0) { $sql = 'delete from links where id_category='.$cat.' and id_object='.$obj; $result = mysqli_query ($mysql_id, $sql); echo '<br>Запрос на удаление выполнен'; } else echo '<br>Связь не задана, нечего удалять'; break; default: echo '<br>'.($n==0 ? 'Не связано' : 'Связано' ); break; } } else { //Подбираем запрос в зависимости от того, какой id передан: $action='show'; if (!empty($cat) and empty($obj)) //Выбрать по категории $sql = 'select * from categories where id='.$cat.' order by id asc'; else if (empty($cat) and !empty($obj)) //Выбрать по объекту $sql = 'select * from objects where id='.$obj.' order by id asc'; else $sql = //Выбрать всё (*) 3 'select * from links,categories,objects where '. 'links.id_category=categories.id && links.id_object=objects.id'; $result = mysqli_query ($mysql_id,$sql); if ($result) { $n = mysqli_num_rows ($result); if ($n > 0) while ($row = mysqli_fetch_assoc ($result)) { echo '<br>'; foreach ($row as $key=>$val) echo $key.'=>'.$val.' '; //вывод информации } else echo '<br>Результат запроса пуст (выбрано 0 строк)'; } else echo '<br>Ошибка SQL: '.mysqli_error($mysql_id); } //Форма для интерфейса с БД echo '<form method="post" action="'.$_SERVER['PHP_SELF'].'"> <p>Введите натуральные числа или оставьте поля пустыми</p> <p>Категория: <input type="text" name="cat" maxlength="2" size="2" value="'.(empty($cat)?'':$cat).'"> Объект: <input type="text" name="obj" maxlength="2" size="2" value="'.(empty($obj)?'':$obj).'"> <select name="action" size="1"> <option value="show" '.($action=='show'?'selected':'').'>показать <option value="add" '.($action=='add'?'selected':'').'>связать <option value="del" '.($action=='del'?'selected':'').'>удалить </select> <input type="submit" value="Выполнить"></p> </form></body></html>'; ?>
Скриншот приложения в работе
20.01.2015, 09:17 [14753 просмотра]