Как получить древовидный массив за один запрос?

Или можно перефразировать задачу — Как получить многомерный массив из одномерного (плоского), или Получить вложенный массив из одномерного.

Допустим у нас есть база в которой есть список документов, или чего-то там, и есть уникальное поле id, а также неуникальное поле parent_id. Если выбрать все элементы, то получится двумерны массив вида [id_xxx] => array("id", ..., "parent_id"). И с таким массивом может быть неудобно работать если вы захотите вывести вложенны список к приеру или как-то еще реализовать вложенность элементов. Для того чтобы решить эту проблему я предлагаю использовать нижеследующий скрипт:

Итак, вот такая у нас база:

И вот что мы хотим получить:

Для этого пишем код:

// Подключаемся к базе MYSQL
// Получаем из базы плоский массив (не буду это расписывать, т.к. у всех свои способы)
// Может быть у вас массив вообще не из MYSQL базы, а из CSV файла какого-нибудь..
class MyClassName {
    public $cash = array();
    public function getMaterialListTree(){
        $query = $this->db->query("SELECT * FROM YourTable ORDER BY sort_order"); // Получаем как-то наш плоский массив
        $this->cash['getMaterialList'] = $query; // И записываем его в кэш
        $output = array();
        foreach($this->cash['getMaterialList'] as $item){
            if($item['parent_id'] == 0){ //Запускаем рекурсивную функцию только для паренотов самого высого уровня
                $output[$item['id']] = $this->getMaterialListTree_get($item['id']);
            }
        }
        return $output;
    }
    // Рекурсивная функция
    public function getMaterialListTree_get($id = 0){
        $output = array();
        foreach($this->cash['getMaterialList'] as $k => $item){
            if($item['id'] == $id){
                // Записываем значение в возвращаемый массив
                $output = $this->cash['getMaterialList'][$k]; 
                // снова проходимся по нашему плоскому массиву в поисках документов у которых текущий числится родителем
                foreach($this->cash['getMaterialList'] as $item_1){
                    if($item_1['parent_id'] == $id){
                        // если пока что массив для деток не создавался, создаем его
                        if(empty($output['children'])){ 
                            $output['children'] = array();
                        }
                        // если находим деток, то запускаем рекурсию дальше
                        $output['children'][$item_1['id']] = $this->getMaterialListTree_get($item_1['id']);
                    }
                }
            }
        }
        return $output;
    }
}

// Печатаем как нам удобно:
$tree_elem = new MyClassName();
print_r($tree_elem->getMaterialListTree());

Еще один вариант для структурирования одномерного массива

Исходные данные те же самые, что и в первом примере. Но результат нужен такой:

Для этого пишем код:

class MyClassName {
    public function getMaterialList(){
        $input_array = $this->db->query("SELECT * FROM YourTable ORDER BY sort_order");
        $material_list = array();
        
        // Надо определить кто является парентом чтобы их не выводить
        $material_parent_ids = array();
        foreach($input_array as $elem){
            $material_list[$elem['id']] = $elem;
        }
        foreach($material_list as $elem){
            if(!in_array($elem['parent_id'] ,$material_parent_ids)){
                $material_parent_ids[$elem['parent_id']] = $material_list[$elem['parent_id']]['id'];
            }
        }
        
        $material_list_string = array();
        foreach($material_list as $elem){
            $temp_array = array();
            // Тут нет рекурсии, но уж простите, в задаче не требовалось. Рекурсия выполнена в предыдущем примере.
            if(!in_array($elem['id'],$material_parent_ids)){
                $temp_array[] = $elem['material_name'];
                if($elem['parent_id'] != 0){
                    $temp_array[] = $material_list[$elem['parent_id']]['material_name'];
                }
                if($material_list[$elem['parent_id']]['parent_id'] != 0){
                    $temp_array[] = $material_list[$material_list[$elem['parent_id']]['parent_id']]['material_name'];
                }
                $material_list_string[$elem['id']] = implode(" / ", array_reverse($temp_array));
            }
        }
        asort($material_list_string);
        return $material_list_string;
    }
}
// Печатаем как нам удобно:
$tree_elem = new MyClassName();
print_r($tree_elem->getMaterialListTree());

Комментарии (0)

  1. Напишите первый комментарий
*Комментарий будет опубликован после проверки модератором

Комментарии easyComm

Станислав 17 марта 2018, 22:28

Увы, "...за один запрос" лишнее)

Администратор по умолчанию

Почему? Запрос к базе 1, после него мы только с кэшем работаем:)

[MODx] Генератор настроек MIGX

[MODx, MIGx] Документация на русском по MIGX

[MODX] Импорт и экспорт в MiniShop2. Реализация 1

[MODx] minishop2 tips

[JS] Маска для ввода телефона +7(___)___-__-__

[MODx, miniShop2] Генерация YML для выгрузки в маркет

Как включить HTTPS (SSL) в MODX - Подробная инструкция

[MODx] Подсказки по API

[OpenCart] Вывести на главную все категории с картинками

[MODx, MIGX] Примеры использования

[MODx] Сайт на обслуживании (выключить сайт)

Универсальная форма обратной связи — feedBackForm

[PHP, JSON] Пример перевода с помощью Yandex Translate API

[MODx, Gallery] Галерея. Вывести обложки альбомов и фотографии

Расширение свойств товаров minishop2

[Opencart] Вернуть английский язык, если вы его удалили

[OpenCart] Вывести модуль напрямую через контроллер

[MODx, Quip] Более рабочий вариант.

[MODX, MIGX] Вывести getImageList только если он не пустой

[MODX, MIGX] Тип поля "Список ресурсов" (resourcelist) с ограничением по родителю (parents)

Красивое адаптивное меню под pdoMenu

[MODx] Создать источник файлов

[MODx] Вывод tv-параметров в формате [название TV] - [значение TV]

[PHP] Простое логирование

[MODX] Вывести список всех опций MiniShop2

Все записи