Создание многоуровневого массива с использованием parentIds в PHP

Я пытаюсь настроить список, который может иметь несколько уровней, используя parentId для определения его родителя. Параметр parentId первого элемента имеет значение NULL. Пример некоторых записей:

  id parentId имя
 1 NULL item1
 2 NULL item2
 3 1 пункт3
 4 2 item4
 5 3 пункт5
 6 3 пункт6

Итак, 1 и 2 являются основными пунктами; 3 – ребенок 1; 4 – ребенок 2; 5 – это ребенок из 3 (который является ребенком одного из них); 6 также является ребенком из 3 (который является ребенком одного из них); и т.п.

Я застрял в создании массива, который правильно добавляет эти элементы в нужные уровни. Он должен выглядеть так:

 Array ( [1] => Array ( [name] => item1 [parentId] => [children] => Array ( [3] => Array ( [name] => item3 [parentId] => 1 [children] => Array ( [5] => Array ( [name] => item5 [parentId] => 3 ) [6] => Array ( [name] => item6 [parentId] => 3 ) ) ) ) ) [2] => Array ( [name] => item2 [parentId] => [children] => Array ( [4] => Array ( [name] => item4 [parentId] => 2 ) ) ) ) 

Но скажите, что я просматриваю все элементы, используя foreach() , и я получаю элемент 5. Его parentId равен 3, но в этот момент я понятия не имею, где этот родитель 3 находится в массиве и как добавить детей в этот родитель.

Есть ли уловка, чтобы перебрать эти предметы и поставить их на место правильно?

Solutions Collecting From Web of "Создание многоуровневого массива с использованием parentIds в PHP"

Вот оно

 // your original data as an array $data = array( array( 'id' => 1, 'parentId' => null, 'name' => 'item1' ), array( 'id' => 2, 'parentId' => null, 'name' => 'item2' ), array( 'id' => 3, 'parentId' => 1, 'name' => 'item3' ), array( 'id' => 4, 'parentId' => 2, 'name' => 'item4' ), array( 'id' => 5, 'parentId' => 3, 'name' => 'item5' ), array( 'id' => 6, 'parentId' => 3, 'name' => 'item6' ), ); 

Рекурсивная функция

 function buildTree( $ar, $pid = null ) { $op = array(); foreach( $ar as $item ) { if( $item['parentId'] == $pid ) { $op[$item['id']] = array( 'name' => $item['name'], 'parentId' => $item['parentId'] ); // using recursion $children = buildTree( $ar, $item['id'] ); if( $children ) { $op[$item['id']]['children'] = $children; } } } return $op; } print_r( buildTree( $data ) ); /* Array ( [1] => Array ( [name] => item1 [parentId] => [children] => Array ( [3] => Array ( [name] => item3 [parentId] => 1 [children] => Array ( [5] => Array ( [name] => item5 [parentId] => 3 ) [6] => Array ( [name] => item6 [parentId] => 3 ) ) ) ) ) [2] => Array ( [name] => item2 [parentId] => [children] => Array ( [4] => Array ( [name] => item4 [parentId] => 2 ) ) ) ) */ 

Вы должны использовать идентификатор элемента как ключевое значение для массива, чтобы вы могли добавить элемент к его родительскому объекту следующим образом:

 $array[$parentID]['children'][$childID] = array(); 

Первое, что приходит на ум, – это просто плоская версия того, что у вас есть:

 array ( [0] => array( 'name' => 'item1', 'parent' => null ), [1] => array( 'name' => 'item2', 'parent' => null ), [3] => array( 'name' => 'item3', 'parent' => 0 ), [4] => array( 'name' => 'item4', 'parent' => 3 ), [5] => array( 'name' => 'item5', 'parent' => 1 ), [6] => array( 'name' => 'item6', 'parent' => 1 ), ); 

В принципе, вы только ссылаетесь на родителя. Чтобы найти всех детей, вам придется перебирать массив. Однако начальное время настройки было бы довольно быстрым.

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

 array ( [0] => array( 'name' => 'item1', 'parent' => null, 'children' = array(3) ), [1] => array( 'name' => 'item2', 'parent' => null 'children' = array(5, 6) ), [3] => array( 'name' => 'item3', 'parent' => 0 'children' = array(4) ), [4] => array( 'name' => 'item4', 'parent' => 3 'children' = array() ), [5] => array( 'name' => 'item5', 'parent' => 1 'children' = array() ), [6] => array( 'name' => 'item6', 'parent' => 1 'children' = array() ), ); 

В этом случае вы добавляете все дочерние индексы в родительский. Это займет немного больше времени, но последующее время доступа будет быстрым. Когда вы выясняете родителей, вы просто добавляете массив дочерних элементов.

Единственным реальным недостатком второго подхода является то, что если вы хотите добавить или удалить элемент, вам нужно помнить о возврате и обновлении массива children для родителя.

Вам может не понадобиться новое решение, но это может пригодиться другим пользователям:

 // ! assuming $elements is built like: Array( id1 => Array(/*parameters*/) , ...) function buildTree($elements) { $e = Array(0 => Array()); // elements array $r = Array(0 =>& Array()); // reference array while ($elements) {// repeat till $elements is empty // loop through (remaining) elements foreach ($elements as $id => $element) { $pid = $element['parentId']; // shortcut if (array_key_exists($pid,$r)) { // parent already parsed -> can add this element // use parent's reference to elements array to add this element ( $r[$pid] =& $e[path][to][$pid] ) $r[$pid] ['elements'][$id] = $element; // create reference for this element using parent's reference $r[$id] =& $r[$pid]['elements'][$id]; // unset current element in input array in order to limit while-loop unset($elements[$id]); } } } return $e; // or whatever you need, the reference array can be useful if you need to fetch an element only knowing its id } не // ! assuming $elements is built like: Array( id1 => Array(/*parameters*/) , ...) function buildTree($elements) { $e = Array(0 => Array()); // elements array $r = Array(0 =>& Array()); // reference array while ($elements) {// repeat till $elements is empty // loop through (remaining) elements foreach ($elements as $id => $element) { $pid = $element['parentId']; // shortcut if (array_key_exists($pid,$r)) { // parent already parsed -> can add this element // use parent's reference to elements array to add this element ( $r[$pid] =& $e[path][to][$pid] ) $r[$pid] ['elements'][$id] = $element; // create reference for this element using parent's reference $r[$id] =& $r[$pid]['elements'][$id]; // unset current element in input array in order to limit while-loop unset($elements[$id]); } } } return $e; // or whatever you need, the reference array can be useful if you need to fetch an element only knowing its id } 

Что оно делает:

  • объявить массив для результатов ( $e ) и добавить корневой элемент ( 0/null )
  • объявить массив для ссылок ( $r ) и уже ссылаться на $r[0] на $e[0]
  • цикл через входной массив до тех пор, пока он не станет пустым
    • для каждого элемента:
    • проверьте, обработан ли родитель, если это так, вы знаете, где этот элемент принадлежит, и можете добавить его
      (это мешало мне заставить его работать первые попытки пары и почему цикл while нужен)
    • добавьте текущий элемент с помощью ссылки родителя (обратите внимание на & after = !)
    • создать новую ссылку, используя ссылку родителя
    • отключить текущий элемент из входного массива
      (вы можете оставить это, если вам нравятся бесконечные циклы;))
  • return что вам нужно!

Преимущества этого метода:

  • вы уменьшаете количество циклов foreach (вы назначаете больше всего из того, что вы знаете на данный момент)
  • каждый цикл становится короче, потому что вы отключили анализируемые элементы во входном массиве ( $elements )
  • вы также можете вернуть массив ссылок и иметь возможность быстро получить параметры элемента, если вы знаете, что это id

Другими словами: он должен быть быстрее (я его не тестировал!), По крайней мере для более сложных задач, таких как тот, для которого я его использую.