БлогNot. PHP: связь "многие ко многим" между двумя таблицами

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 с игнорированием регистра символов при сравнении
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>';
?>
Скриншот приложения в работе
Скриншот приложения в работе

теги: php mysql

20.01.2015, 09:17; рейтинг: 11481