Связывание через таблицу связи в PHP

Пусть теперь юзер был в разных городах. В этом случае таблица с юзерами могла бы иметь следующий вид:

users
id name city
1 user1 city1, city2, city3
2 user2 city1, city2
3 user3 city2, city3
4 user4 city1

Понятно, что так хранить данные неправильно - города нужно вынести в отдельную таблицу. Вот она:

cities
id name
1 city1
2 city2
3 city3

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

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

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

Вот наша таблица связи:

users_cities
id user_id city_id
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 3 2
7 3 3
8 4 1

Таблица с юзерами будет хранить только имена юзеров, без связей:

users
id name
1 user1
2 user2
3 user3
4 user4
5 user5

Запросы

Давайте сделаем запрос, с помощью которого вытащим юзеров вместе с их городами. Для этого нам понадобится сделать два джоина: первый джоин присоединит к юзерам таблицу связи, а второй джоин по связям присоединит города:

SELECT users.name as user_name, cities.name as city_name FROM users LEFT JOIN users_cities ON users_cities.user_id=users.id LEFT JOIN cities ON users_cities.city_id=cities.id

Результат запроса

Результат нашего запроса в PHP будет содержать имя каждого юзера столько раз, со скольки городами он связан:

<?php $arr = [ ['user_name' => 'user1', 'city_name' => 'city1'], ['user_name' => 'user1', 'city_name' => 'city2'], ['user_name' => 'user1', 'city_name' => 'city3'], ['user_name' => 'user2', 'city_name' => 'city1'], ['user_name' => 'user2', 'city_name' => 'city2'], ['user_name' => 'user3', 'city_name' => 'city2'], ['user_name' => 'user3', 'city_name' => 'city3'], ['user_name' => 'user4', 'city_name' => 'city1'], ]; ?>

Удобнее было бы переконвертировать такой массив и превратить его в следующий:

<?php $res = [ ['user1' => ['city1', 'city2', 'city3']], ['user2' => ['city1', 'city2']], ['user3' => ['city2', 'city3']], ['user4' => ['city1']], ]; ?>

Напишем код, выполняющий такую конвертацию:

<?php $res = []; foreach ($data as $elem) { $res[$elem['user_name']][] = $elem['city_name']; } var_dump($res); ?>

Практические задачи

Пусть товар может принадлежать нескольким категориям. Распишите структуру хранения.

Напишите запрос, который достанет товары вместе с их категориями.

Выведите полученные данные в виде списка ul так, чтобы в каждой li вначале стояло имя продукта, а после двоеточия через запятую перечислялись категории этого продукта. Примерно так:

<ul> <li>product1: category1, category2, category3</li> <li>product2: category1, category3</li> <li>product3: category1</li> </ul>