С помощью php и foreach вывести списком все уникальные чаты, а в которых есть непрочитанные сообщения - добавить иконку уведомления

пробовал так, но выводит все уникальные значения, т.е. 2 варианта для конкретного чата с уведомлением($icon) и без :

if( $see == 0 ) { $icon = '<div class="icon"></div>'; } else { $icon = ''; }
$arr[] = '<div>'.$icon.'</div>';
    
$el = array_unique($arr);
foreach($el as $el) { echo($el); }

вот думаю как с помощью foreach оставить для конкретного чата из 2х значений (с уведомлением и без) только одно - с уведомлением (оно же в массиве есть, а выборку не могу придумать как сделать)

введите сюда описание изображения

сори, но голова уже не варит - вот первый мой вопрос на стаке, не кидайтесь камням :)

вот весь код (используется Redbeanphp для работы с базой):

echo('<div class="message-list">');

$message_list = R::findAll('messages', 'ORDER BY time ASC');
 foreach ($message_list as $message_list) {
  if( in_array($message_list->whom_id, $_SESSION['company']) && $message_list->whom_type == 'c' || in_array($message_list->from_id, $_SESSION['company']) && $message_list->from_type == 'd' ){
  if( ( !in_array($message_list->from_id, $_SESSION['company']) && $message_list->from_type == 'c' ) || $message_list->from_type == 'u' ){
   if( $message_list->from_type == 'u' ) {
    $m_user_list = R::find('users', 'id= ?', [$message_list->from_id]);
    foreach ($m_user_list as $m_user_list) { $m_from_list = $m_user_list->name .' '. $m_user_list->surname; $m_img_list_from = $m_user_list->avatar; }
   } else {
    $m_user_list = R::find('companys', 'id= ?', [$message_list->from_id]);
    foreach ($m_user_list as $m_user_list) { $m_from_list = $m_user_list->name; $m_img_list_from = $m_user_list->logo; }
   }
   if( $message_list->whom_type == 'u' ) {
    $m_whom_list = R::find('users', 'id= ?', [$message_list->whom_id]);
    foreach ($m_whom_list as $m_whom_list) { $m_img_list_whom = $m_whom_list->avatar; }
   } else {
    $m_whom_list = R::find('companys', 'id= ?', [$message_list->whom_id]);
    foreach ($m_whom_list as $m_whom_list) { $m_img_list_whom = $m_whom_list->logo; }
   }
   if( $message_list->see == 0 ) { $ic_see = '<div class="icon"></div>'; } else { $ic_see = ''; }
    $message_list_arr[] = '<div class="message-list-block" data-id="'.$message_list->from_id.'" data-type="'.$message_list->from_type.'" data-idw="'.$message_list->whom_id.'" data-typew="'.$message_list->whom_type.'"><img class="whom" src="'.$m_img_list_whom.'"><img src="'.$m_img_list_from.'"><p>'.$m_from_list.'</p>'.$ic_see.'</div>';
  }
 }
}

$message_list_el = array_unique($message_list_arr);
foreach($message_list_el as $message_list_el) { echo($message_list_el); }

echo('</div>');

таблица с такими полями:

  • id
  • from_id - id от кого пригло
  • from_type - тип учетной записи от кого пришло
  • whom_id - id кому пришло
  • whom_type - тип учетной записи кому пришло
  • time - когда написано
  • message - текстовка
  • see - 1/0 прочитано или нет

Ответы (2 шт):

Автор решения: Roman Lytvynov

Здесь нужно копать в сторону запроса в БД. Опираясь на структуру данных, если это SQL, Вы можете сделать запрос:

SELECT 
  from_id,
  min(see) as see,
  max(time) as time
FROM chat_messages
WHERE whom_id = $id 
GROUP BY from_id
ORDER BY time DESC;

Здесь происходит группировка строк по полю from_id, тем самым получаем уникальные значения этого поля.

min(see) - получаем, какое значение минимальное, соответственно если есть хоть один 0, то это означает, что есть непрочитанные сообщения.

max(time) - получаем время последнего сообщения. На выходе получаем коллекцию (массив), элементы которого содержат поля:

  • from_id - для отображения идентификатора чата (отправителя)
  • see - 0/1, по которому решаем, показывать иконку или нет
  • time - время последего сообщения

Если Вы хотите показать еще и текст последнего сообщения, то запрос будет чуть сложнее:

SELECT 
  from_id,
  min(see) as see,
  max(time) as time,
  message
FROM (
  SELECT
    from_id,
    see,
    time,
    message
  FROM chat_messages
  WHERE whom_id = $id
  ORDER BY time DESC
) as t
GROUP BY from_id
ORDER BY time DESC;

Пример тут

Ну и в конце концов, в PHP мы просто проходимся по массиву с уже подготовленными данными и формируем HTML:

$chats_list = //result from sql request;
$chats_string = '';
foreach($chats_list as $chat) {
    $icon = $chat['see'] ? '' : "<img src="path_to_icon">";
    $time = $chat['time'];
    $from = $chat['from_id'];
    $message = $chat['message'];
    $chats_string .= "<div>$icon $time $from - $message</div>";
}

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

UPD: согласно комментария Во-первых, загружать себе всю всю таблицу из базы, чтобы потом фильтровать её в коде плохая практика. Вы вытаскиваете много лишней информации. Учитывая то, что Вы знаете, по каким критериям стоит фильтровать данные, используйте WHERE в Вашем запросе, это уменьшит объем трафика, использование оперативной памяти Вашим приложением и уменьшит количество кода для обработки.

Во-вторых, Вы в цикле делаете n запросов R::find, что чревато медленной работой и потенциально блокировкой таблиц. Соберите все параметры, по которым нужно сделать дополнительные запросы, и за один запрос к каждой таблице вытащите все что нужно из нее.

Если абстрагироваться от Вашей конкретной задачи и вернуться к массивам, то необходимо создать массив аккумулятор, после пройтись по массиву и в аккумулятор внести нужные данные:

$chats = [];
foreach($messages as $message) {
    $from_id = $message['from_id'];
    if (!isset($chats[$from_id])) {
        $chats[$from_id] = // добавляем данные из сообщения;
    } else {
        // проверяем на прочитанное / непрочитанное, на последнее сообщение
    }
}
→ Ссылка
Автор решения: Quotsu

выборку делаю по непрочитанным, фильтрую и проверяю есть ли в $chats id отправителя, и далее проверяю на прочтение для добавления иконки в общем вышло как-то так :

echo('<div class="message-list">');
 $chats = [];
 $message_list = R::findAll('messages', 'ORDER BY see ASC');
  foreach ($message_list as $message_list) {
   if( in_array($message_list->whom_id, $_SESSION['company']) && $message_list->whom_type == 'c' || in_array($message_list->from_id, $_SESSION['company']) && $message_list->from_type == 'd' ){
    if( ( !in_array($message_list->from_id, $_SESSION['company']) && $message_list->from_type == 'c' ) || $message_list->from_type == 'u' ){
     if ( !isset($chats[$message_list->from_id]) ) {
      if( $message_list->from_type == 'u' ) {
       $m_user_list = R::findOne('users', 'id= ?', [$message_list->from_id]);
       $m_from_list = $m_user_list->name .' '. $m_user_list->surname;
       $m_img_list_from = $m_user_list->avatar;
      } else {
       $m_user_list = R::findOne('companys', 'id= ?', [$message_list->from_id]);
       $m_from_list = $m_user_list->name; $m_img_list_from = $m_user_list->logo;
      }
      if( $message_list->whom_type == 'u' ) {
       $m_whom_list = R::findOne('users', 'id= ?', [$message_list->whom_id]);
       $m_img_list_whom = $m_whom_list->avatar;
      } else {
       $m_whom_list = R::findOne('findOne', 'id= ?', [$message_list->whom_id]);
       $m_img_list_whom = $m_whom_list->logo;
      }
      if( $message_list->see == 0 ) { $ic_see = '<div class="icon"></div>'; } else { $ic_see = ''; }
      $message_list_arr[] = '<div class="message-list-block" data-id="'.$message_list->from_id.'" data-type="'.$message_list->from_type.'" data-idw="'.$message_list->whom_id.'" data-typew="'.$message_list->whom_type.'"><img class="whom" src="'.$m_img_list_whom.'"><img src="'.$m_img_list_from.'"><p>'.$m_from_list.'</p>'.$ic_see.'</div>';
    }
   }
  }
 }
    
 $message_list_el = array_unique($message_list_arr);
 foreach($message_list_el as $message_list_el) { echo($message_list_el); }
    
echo('</div>');
→ Ссылка