Как сгруппировать данные при выводе из базы в html?

121
23 декабря 2020, 11:20

подскажите как правильно организовать идею:

У меня есть база locality например такая:

id  region  location,   map
1   region1, карусели,  улица пушкина, 1;
2   region1, магазин,   улица пушкина, 2;
3   region2, зоопарк,   фрунзе 1;
4   region2, кинотеатр, фрунзе 3;
5   region2, театр,     улица 1;
6   region3, театр №2,  улица 2;

В одном region ест уникальные значения location и map, они не повторяются информацию я селекчу по id и записываю как mysqli_fetch_array в массив $loc. как вписать эту информацию правильно в html следующего содержания:

<h3>region1</h3> 
<li>Магазин<iframe>улица пушкина, 1</iframe> 
<li>магазин<iframe>	улица пушкина, 2</iframe> 
<h3> region2</h3> 
<li>зоопарк <iframe>фрунзе 1</iframe> 
<li> кинотеатр <iframe>фрунзе 3</iframe>
Не понимаю как сделать цикл что бы при считывании цикл не делал мне что-то типо

<h3>region1</h3> 
    <li>Магазин<iframe>улица пушкина, 1</iframe> 
<h3>region1</h3> 
    <li>магазин<iframe>	улица пушкина, 2</iframe>
Т.е например

<? foreach $loc as $m?>
 <h3><?$m['region']?></h3>
        <li><?$m ['location']?><iframe><?$m ['map']?></iframe>
        <li><?$m ['location']?><iframe><?$m ['map']?></iframe>
//*дальше следующий заголовок той же конструкции.
<? endforeach ?>

Буду благодарен за помощь и объяснение как нужно правильно. Заранее извиняюсь за возможно не корректный пример или выражения.Поправьте меня если, что не так или нужно уточнить.

Например у меня такая функция, в которую я записал массив:

function loc_array($link){
    $query = 'SELECT * FROM  locality';
    $result = mysqli_query($link, $query) or die('Запрос не удался: ' . mysqli_error($query));
    if(!$result)
        die(mysqli_error(($link)));
    $n = mysqli_num_rows($result);
    $loc = array();
    for ($i = 0;$i<$n;$i++)
    {
        $row = mysqli_fetch_assoc($result);
        $loc[] = $row;
   }
    return $loc;
И потом ее вызвал
 $link = db_connect();
    $loc = loc_array($link);
Answer 1

Когда полей много

Решение абсолютно примитивное: выборку надо всего лишь отсортировать. Добавив три слова в запрос, ORDER BY region. После этого регионы будут идти подряд, и достаточно будет поставить условие как в ответе Timur - если регион сменился, то вывести его:

$region = '';
while ($result = mysqli_fetch_assoc($query)) {
    if ($region != $result['region']) {
        print "<h3>{$result['region']}</h3>";
        $region = $result['region'];
    }
    print "<li>{$result['location']}<iframe>{$result['map']}</iframe></li>";
}

Но есть и более удобный вариант. Если вместо убогой mysqli взять нормальный драйвер для работы с БД, то он сможет вернуть данные уже сгруппированные по региону

function loc_array($pdo){
    $query = 'SELECT region, id, location, map FROM  locality';
    return $pdo->query($query)->fetchAll(PDO::FETCH_GROUP);
}

После этого для вывода будет нужно всего два вложенных цикла

<?php foreach ($loc as $region => $chunk): ?>
<h3><?= $region ?></h3>
<ul>
    <?php foreach ($chunk as $row): ?>
        <li><?= $row['location']?><iframe><?= $row['map']?></iframe></li>
    <?php endforeach ?>
</ul>
<?php endforeach ?>

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

Когда полей только два

В самом простом варианте, когда надо запросить только 1 уникальное поле, и к нему - одно поле с группировкой, то можно воспользоваться запросом с GROUP BY и функцией group_concat(), которая соберёт все отдельные значения при группировке через запятую

SELECT region, group_concat(location) FROM locality GROUP BY region

и затем можно будет в РНР скрипте сделать explode():

$query = $mysqli->query("SELECT region, group_concat(location) loc FROM locality GROUP BY region");
while ($result = mysqli_fetch_assoc($query)) {
    print "<h3>{$result['region']}</h3>";
    foreach (explode(",", $result['loc']) as $location)
        print "<li>$loсation</li>";
    }
}

Но здесь надо помнить про две засады

  1. В сгруппированных данных может встретиться замятая, которая устроит ири выводе харакири. Лечится задаванием другого разделителя в group_concat
  2. Общая длина строки выдаваемой group_concat ограничена по умолчанию 1024 символами. Лечится запросом SET SESSION group_concat_max_len = 1000000;

Но опять же, чтобы избежать всех этих хитросплетений, можно воспользоваться ПДО:

function loc_array($pdo){
    $query = 'SELECT region, location FROM locality';
    return $pdo->query($query)->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_COLUMN);
}

И выводить

<?php foreach ($loc as $region => $locations): ?>
<h3><?= $region ?></h3>
<ul>
    <?php foreach ($locations as $item): ?>
        <li><?= $item ?></li>
    <?php endforeach ?>
</ul>
<?php endforeach ?>
Answer 2
<? foreach $loc as $m?>
<h3><?$m['region']?></h3>
<ul>
    <li><?$m ['location']?><iframe><?$m ['map']?></iframe></li>
    <li><?$m ['location']?><iframe><?$m ['map']?></iframe></li>
</ul>
<? endforeach ?>

Ознакомьтесь со списками в HTML http://htmlbook.ru/html/ul / http://htmlbook.ru/html/ol

<h3>Region #1</h3> 
<ul> 
	<li>Content</li> 
	<li>Content</li> 
</ul> 
<h3>Region #2</h3> 
<ul> 
	<li>Content</li> 
	<li>Content</li> 
</ul> 
<h3>Region #3</h3> 
<ul> 
	<li>Content</li> 
	<li>Content</li> 
</ul>

Answer 3

Можно сделать вот так:

function in_array_r($needle, $haystack, $strict = false) {
  foreach ($haystack as $key => $item) {
    if (($strict ? $key === $needle : $key == $needle)) {
        return $key;
    }
  }
  return false;
}
$locality = array();
while ($result = mysqli_fetch_assoc($query)) {
  if ($key = in_array_r($result['region'], $locality)) {
    $result = array_diff($result, [$key]);
    $locality[$key][] = $result;
  } else {
    $region = $result['region'];
    unset($result['region']);
    $locality[$region][] = $result;
  }
}
foreach ($locality as $key => $value) {
  print "<h3>$key</h3>";
  foreach ($value as $val) {
    print "<li>{$val['location']}<iframe>{$val['map']}</iframe></li>";
  }
}

Но лучше переделать структуру базы. Примерно так:

CREATE TABLE region
(
  id INT PRIMARY KEY AUTO_INCREMENT,
  region VARCHAR(255)
);
CREATE TABLE locality
(
  id INT PRIMARY KEY AUTO_INCREMENT,
  location VARCHAR(255),
  map VARCHAR(255),
  region_id INT,
  FOREIGN KEY (region_id)  REFERENCES region (id)
);

В этом случае вывод из базы можно организовать так:

$query = mysqli_query($connection, "SELECT * FROM locality LEFT JOIN 
region ON locality.region_id = region.id");
$region = '';
while ($result = mysqli_fetch_assoc($query)) {
  if ($region == $result['region']) {
    print "<li>{$result['location']}<iframe>{$result['map']}</iframe 
           </li>";
  } else {
    print "<h3>{$result['region']}</h3>";
    print "<li>{$result['location']}<iframe>{$result['map']}</iframe> 
           </li>";
    $region = $result['region'];
  }
}
READ ALSO
Join 2 коннектов

Join 2 коннектов

Столкнулся с проблемой при выборке данных MySql 56 основная часть данных храниться в одном коннекте и справочники хранятся в другом коннекте...

104
При нажатии кнопки формы ничего не происходит

При нажатии кнопки формы ничего не происходит

PHP html в разных файлахНужно по нажатию кнопки submit выполнить php

110
Вернуть исходное состояние при false

Вернуть исходное состояние при false

Не выведет ничего, тк нет пробела

136
правильный синтаксис конкатенации?

правильный синтаксис конкатенации?

vs code и netbeans дружно бычат что неожидали такого от меня

132