За последние 24 часа нас посетили 30938 программистов и 1446 роботов. Сейчас ищут 875 программистов ...

Как сделать умный цикл с оптимизация!

Тема в разделе "PHP для новичков", создана пользователем engine.energy, 21 июн 2015.

  1. engine.energy

    engine.energy Новичок

    С нами с:
    31 май 2015
    Сообщения:
    149
    Симпатии:
    0
    При запрос в базу и получает данные с чата.
    $chat = array(с базы);

    При каждый foreach вызываю user_id

    где то 20 раз за это мы вызываем класс new User($user_id);

    А там уже идет запрос на базу узера и получаем логина и так далее....

    за 20 раз мы отправляем запрос в базу это немного значительно обьема.

    Есть ли обойти его?

    Ответ нужен:

    - Как его уменшить с 20 запроса на 1 раз.

    Подробно: точнее при нового класса храним индификатор при полного сбора пока не закончит и начнет собирать запрос 1 раз всех индификатор. Но этого не помогает как сделать хз!
    Пример:
    Код (PHP):
    1. $array_chat = array(
    2. array('user_id' = 1),
    3. array('user_id' = 2),
    4. array('user_id' = 3),
    5. array('user_id' = 1),
    6.  
    7. foreach($array_chat AS $message)
    8. {
    9.   $user = new user($message['user_id']);
    10.   print $user->login;
    11. }
    12. );
    13.  
    Извини за плохой русского языка. +) Если вам не понравилось можете оставить плохой коммент, что я тупой или вроде!
     
  2. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.598
    Симпатии:
    1.764
    Собрать все уникальные user_id в массив, к примеру $user_ids, и потом сделать один запрос in. А потом сделать что-то типа:
    Код (PHP):
    1. class UsersRepository {
    2.      private $users;
    3.      public function __construct($ids) {
    4.           global $mysqli; // Не хотите глобал, сделайте статическое поле какого-нибудь класса. Не суть в данном случае
    5.           // Для безопасности
    6.           foreach ($ids as &$id) {
    7.               $id = (int) $id;
    8.           }
    9.           $ids_str = implode(",", $ids);
    10.           $res = $mysqli->query("select * from users where id in ($ids)");
    11.           while ($u = $res->fetch_object()) {
    12.                $this->users[$u->id] = $u;
    13.           }
    14.           $res->close();  
    15.      }
    16.      function getUser($id) {
    17.           return isset($this->users[$id]) ? $this->users[$id] : null;
    18.      }
    19. }
    20.  
     
  3. engine.energy

    engine.energy Новичок

    С нами с:
    31 май 2015
    Сообщения:
    149
    Симпатии:
    0
    mkramer, хорошая идея! О нем и забыл =) И запомни реше обо мне что я пишу PDO а не мускли =)
     
  4. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.598
    Симпатии:
    1.764
    Я ещё о тебе что-то помнить должен? Переписать с mysqli на PDO - пара минут
     
  5. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.128
    Симпатии:
    1.248
    Адрес:
    там-сям
    У меня есть вариант, но он специфический и не для всех ситуаций подойдет.

    В большинстве случаев нам не нужны сразу 100500 экземпляров User. Мы обрабатываем их последовательно внутри цикла и на каждой итерации цикла используем новый объект. А старый просто теряется и ждет когда его утилизует сборщик мусора ))) Или мы хотим получить только какой-то конкретный объект по его индексу/айдишнику. Но почти никогда нам не нужны сразу два разных объекта из одной выборки!

    Исходя из этих соображений я делал класс-итератор, реализующий стандартные интерфейсы Iterator и ArrayAccess и который на входе получает массив строк из базы. Двумерный массив: колонки и записи. Полученные одним запросом и считанные в цикле. Что-то вроде:

    Код (PHP):
    1. // где-то внутри репозитория
    2. while ($row = mysqli_fetch_assoc($result)) {
    3.   $rows[] = $row;
    4. }
    5. return new MyIterator($rows);
    Итератор НЕ создаёт сразу все объекты-записи. А делает new MyClass() только тогда, когда требуются запись — в перекрытых методах offsetGet() и current(). (см. описание интерфейсов по ссылкам выше)
    Код (PHP):
    1. // итератор выглядит как массив, но на самом деле при получении элемента "массива"
    2. // происходит обращение к методу, где мы можем создать новый объект-запись. 
    3. foreach ($userIterator as $user) {
    4.     echo $user->id, $user->name;
    5. }
    Я пошел даже дальше и стал создавать только один экземпляр объекта-записи, а в методах итератора просто присваиваю его полям нужные значения из массива. И за всё время использования только в одной задаче мне понадобилось зафиксировать состояние объекта-записи чтобы работать сразу с двумя разными экземплярами. Это возможно через операцию clone:
    Код (PHP):
    1. // этот экземпляр объекта уже не изменится при получении новой записи из итератора
    2. $savedUser = clone $user;
     
  6. engine.energy

    engine.energy Новичок

    С нами с:
    31 май 2015
    Сообщения:
    149
    Симпатии:
    0
    artoodetoo, суть уловил но не много не понял =)

    Добавлено спустя 5 минут 4 секунды:
    У меня такой код класс User и Auth

    Класс Auth
    Код (PHP):
    1. <?php
    2.  
    3. namespace Classes\Core;
    4.  
    5. class Auth
    6. {
    7.     static protected $_data = null;
    8.     
    9.     /**
    10.     * Инициализация пользователя
    11.     * @return User
    12.     */
    13.     static protected function InitUser()
    14.     {
    15.         if (!empty($_SESSION ['SESSION_ID_USER'])) 
    16.         {
    17.             self::$_data = new User($_SESSION ['SESSION_ID_USER']);
    18.         }
    19.         else
    20.         {
    21.             self::$_data = new User();
    22.         }
    23.  
    24.         if (self::isGuest() && isset($_SESSION ['SESSION_ID_USER'])) 
    25.         {
    26.             unset($_SESSION ['SESSION_ID_USER']);
    27.         }
    28.     }
    29.     
    30.     /**
    31.     * @return User
    32.     */
    33.     static public function GetUser()
    34.     {
    35.         if (is_null(self::$_data)) 
    36.         {
    37.             self::InitUser();
    38.         }
    39.         
    40.         return self::$_data;
    41.     }
    42.     
    43.     /**
    44.     * @return True/False
    45.     */
    46.     static public function IsGuest()
    47.     {
    48.         return !self::GetUser()->id;
    49.     }
    50.     
    51.     /**
    52.     * @return True/False
    53.     */
    54.     static public function IsUser()
    55.     {
    56.         return !!self::GetUser()->id;
    57.     }
    58. }
    модельный дата Класс User
    Код (PHP):
    1. <?php
    2.  
    3. namespace Classes\Core;
    4.  
    5. class User
    6. {
    7.     static protected $_data = array();
    8.     static protected $_update = array();
    9.     
    10.     /**
    11.     * Инициализация Пользователя
    12.     */
    13.     static protected function InitUser($id)
    14.     {
    15.         $res = Db::GetPdo()->prepare("SELECT * FROM `users` WHERE `id` = ? LIMIT 1");
    16.         $res->execute(Array($id));
    17.         
    18.         return self::$_data = $res->fetch();
    19.     }
    20.     
    21.     /**
    22.     * Инициализация Гостя
    23.     */
    24.     static protected function InitGuest()
    25.     {
    26.         return self::$_data['id'] = false;
    27.     }
    28.     
    29.     /**
    30.     * 
    31.     */
    32.     public function __construct($id = false)
    33.     {
    34.         if ($id)
    35.         {
    36.             return self::InitUser($id);
    37.         }
    38.         
    39.         return self::InitGuest();
    40.     }
    41.     
    42.     public function __destruct()
    43.     {
    44.         if (self::$_update) 
    45.         {
    46.             $sql = array();
    47.             foreach (self::$_update as $key => $value) 
    48.             {
    49.                 $sql[] = "`" . $key . "` = " . Db::GetPdo()->quote($value);
    50.             }
    51.             
    52.             Db::GetPdo()->query("UPDATE `users` SET " . implode(', ', $sql) . " WHERE `id` = '" . self::$_data['id'] . "' LIMIT 1");
    53.             self::$_update = array();
    54.         }
    55.     }
    56.     
    57.     /**
    58.     * 
    59.     */
    60.     public function __get($name)
    61.     {
    62.         switch($name)
    63.         {
    64.             default:
    65.                 return !isset(self::$_data[$name]) ? false : self::$_data[$name];
    66.             break;
    67.         }
    68.     }
    69.     
    70.     /**
    71.     * 
    72.     */
    73.     public function __set($name, $value)
    74.     {
    75.         if (empty(self::$_data['id']))
    76.         {
    77.             return;
    78.         }
    79.         
    80.         if (!isset(self::$_data[$name])) 
    81.         {
    82.             throw new \Exception(sprintf('Поле %s не существует', $name));
    83.         }
    84.         
    85.         self::$_data[$name] = $value;
    86.         self::$_update[$name] = $value;
    87.     }
    88. }
    Auth это который я сижу и выдает только мои данные!

    Добавлено спустя 41 минуту 21 секунду:
    Сделал так но много код даже 2 раза foreach как то муторно =)

    Код (PHP):
    1. $res = $db->GetPdo()->prepare("SELECT * FROM `chat` LIMIT 100");
    2. $res->execute(Array(1));
    3.         
    4. if($chat = $res->fetchAll())
    5. {
    6.   echo '<pre>';
    7.     print_r($chat);
    8.   echo '</pre>';
    9.   
    10.   foreach($chat AS $comment)
    11.   {
    12.     $array[] = $comment['user_id'];
    13.   }
    14.  
    15.   $users = new Users($array);
    16.   
    17.   foreach($chat AS $comment)
    18.   {
    19.       $user = $users->GetUser($comment['user_id']);
    20.       
    21.       echo 'Писал: ' . $user->login . '</br>';
    22.       echo $comment['text'];
    23.   }
    24. } 
     
  7. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.128
    Симпатии:
    1.248
    Адрес:
    там-сям
    engine.energy, идея коротко:
    1. нам не нужны лишние объекты;
    2. класс из предметной области не знает ничего о способе его хранения.
     
  8. engine.energy

    engine.energy Новичок

    С нами с:
    31 май 2015
    Сообщения:
    149
    Симпатии:
    0
    artoodetoo, и что ты предлогаеш? все пользовать в базе ? Нет так как каждый обьект служит для аватара и прочие функции дабы быстро обрашать!