Возвращает массив массивов массивов объектов

У меня есть некоторые иерархические данные, созданные таким образом:

CREATE TABLE `departments` (`deperatment_id` INTEGER NOT NULL, `department_name` varchar(32) NOT NULL); INSERT INTO `departments`(`deperatment_id`, `department_name`) VALUES (1, "HR"), (2, "Software"), (3, "Accounts"); CREATE TABLE `jobs` (`deperatment_id` INTEGER NOT NULL, `job_id` INTEGER NOT NULL, `job_name` varchar(32) NOT NULL); INSERT INTO `jobs` (`deperatment_id`, `job_id`, `job_name`) VALUES (1, 1, "Idiot"), (1, 2, "Fool"), (2, 3, "PHB"), (2, 4, "Software guru"), (2, 5, "PFY"), (3, 6, "Number cruncher"); CREATE TABLE `peeps` (`job_id` INTEGER NOT NULL, `peep_name` varchar(32) NOT NULL); INSERT INTO `peeps`(`job_id` , `peep_name` ) VALUES(1, "Smith"), (2, "Jones Major"), (2, "Jones Minor"), (4, "Mr. In-the-wrong-department"), (4, "Mawg"), (5, "William Topaz McGonagall"), (6, "Blaise Pascal"), (6, "Isaac Newton"); 

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

Это аккуратная иерархия дерева, и я хочу вернуть ее в ответ на запрос AJAX, поэтому у меня есть три вложенных цикла, каждый из которых выдает SELECT (coed unavailable, так как он находится дома, и я нахожусь в офисе, но я что вы можете это визуализировать, это достаточно просто).

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

Тем не менее, это показывает только на уровне глубины, поэтому одного SELECT с while ($row = $stmt->fetch(PDO::FETCH_OBJ)) было достаточно для построения возвращаемого значения – массива объектов.

Как мне построить мое возвращаемое значение, которое будет вложенным массивом массива массивов объектов?


[Обновление] Я удалил предыдущую ссылку на скрипку, так как она смутила хотя бы одного человека.

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


[Обновить] Вот для вас 50 очков.

Ответы Йельдара Курмангалиева завершены на 90%, но мне просто нужно некоторое просветление по двум небольшим пунктам.

  • его SQL не показывает данные, связанные с departments . Я подозреваю, что мне просто нужно (INNER?) JOIN departments.* . Но какая именно команда SQL?

  • что такое PHP-код? Я подозреваю, что $result = $sqlQuery->fetchAll(PDO::FETCH_ASSOC); или похожие

Кстати, есть скрипка для SQL

Мои лучшие усилия только возвращают плоский массив, а не вложенное дерево, как показывает ответ 🙁


[Обновить] Спасибо за отличный ответ. Чтобы помочь другим, я опубликовал рабочую скрипту по адресу http://phpfiddle.org/main/code/xfdj-wthc

Обратите внимание, что решение поддерживает несколько внешних ключей, в которых у меня есть только один. Я мог бы упростить код для личного использования, но спасибо @trincot за то, что он настолько гибкий, что может быть полезным для других.

Вот код, который извлекает данные из каждой таблицы в отдельные массивы, а затем строит из нее окончательную структуру данных.

Он довольно общий, так как вам нужно только указать, какие поля являются общими ключами (первичные и внешние ключи, которые, как предполагается, имеют одинаковое имя) для каждой пары таблиц:

 function loadTable($dbh, $table) { // Perform simple table select, and return result set $sth = $dbh->prepare("SELECT * FROM $table"); $sth->execute(); $rows = $sth->fetchAll(PDO::FETCH_ASSOC); return $rows; } function connectChildren($parents, $children, $name, $common_keys) { /* Returns $parents array, but with each element extended with * a $name key, which is an array of matching $children elements. * The match is made by comparing the values for each of the $common_keys * in both arrays. * When a $children element is added to the $name array, its $common_keys * are removed from it as they are already known in the $parents element. * (this removal behaviour is optional and could be left out) */ $index = []; // Build a temporary index to associate $parents elements by their // primary key value (can be composite) foreach ($parents as $i => $parent) { $primary_key = []; foreach ($common_keys as $common_key) { $primary_key[] = $parent[$common_key]; } $index[implode("|", $primary_key)] = $i; $parents[$i][$name] = []; } // Main algorithm: inject $children into $parents foreach($children as $child) { $foreign_key = []; // Collect foreign key value foreach ($common_keys as $common_key) { $foreign_key[] = $child[$common_key]; // Remove foreign key from child unset($child[$common_key]); } // Find the corresponding $parents element via the index $i = $index[implode("|", $foreign_key)]; $parents[$i][$name][] = $child; } return $parents; } // Step 1: load all the table data $rows_dep = loadTable($dbh, "departments"); $rows_job = loadTable($dbh, "jobs"); $rows_peep = loadTable($dbh, "peeps"); // Step 2: connect the data, layer by layer, in bottom-up order: $rows_job = connectChildren($rows_job, $rows_peep, "peeps", ["deperatment_id", "job_id"]); $rows_dep = connectChildren($rows_dep, $rows_job, "jobs", ["deperatment_id"]); print_r ($rows_dep); с function loadTable($dbh, $table) { // Perform simple table select, and return result set $sth = $dbh->prepare("SELECT * FROM $table"); $sth->execute(); $rows = $sth->fetchAll(PDO::FETCH_ASSOC); return $rows; } function connectChildren($parents, $children, $name, $common_keys) { /* Returns $parents array, but with each element extended with * a $name key, which is an array of matching $children elements. * The match is made by comparing the values for each of the $common_keys * in both arrays. * When a $children element is added to the $name array, its $common_keys * are removed from it as they are already known in the $parents element. * (this removal behaviour is optional and could be left out) */ $index = []; // Build a temporary index to associate $parents elements by their // primary key value (can be composite) foreach ($parents as $i => $parent) { $primary_key = []; foreach ($common_keys as $common_key) { $primary_key[] = $parent[$common_key]; } $index[implode("|", $primary_key)] = $i; $parents[$i][$name] = []; } // Main algorithm: inject $children into $parents foreach($children as $child) { $foreign_key = []; // Collect foreign key value foreach ($common_keys as $common_key) { $foreign_key[] = $child[$common_key]; // Remove foreign key from child unset($child[$common_key]); } // Find the corresponding $parents element via the index $i = $index[implode("|", $foreign_key)]; $parents[$i][$name][] = $child; } return $parents; } // Step 1: load all the table data $rows_dep = loadTable($dbh, "departments"); $rows_job = loadTable($dbh, "jobs"); $rows_peep = loadTable($dbh, "peeps"); // Step 2: connect the data, layer by layer, in bottom-up order: $rows_job = connectChildren($rows_job, $rows_peep, "peeps", ["deperatment_id", "job_id"]); $rows_dep = connectChildren($rows_dep, $rows_job, "jobs", ["deperatment_id"]); print_r ($rows_dep); 

Выходные данные:

 Array ( [0] => Array ( [deperatment_id] => 1 [department_name] => HR [jobs] => Array ( [0] => Array ( [job_id] => 1 [job_name] => Idiot [peeps] => Array ( [0] => Array ( [peep_name] => Smith ) ) ) [1] => Array ( [job_id] => 2 [job_name] => Fool [peeps] => Array ( [0] => Array ( [peep_name] => Jones Major ) [1] => Array ( [peep_name] => Jones Minor ) ) ) ) ) [1] => Array ( [deperatment_id] => 2 [department_name] => Software [jobs] => Array ( [0] => Array ( [job_id] => 4 [job_name] => Software guru [peeps] => Array ( [0] => Array ( [peep_name] => Mr. In-the-wrong-department ) [1] => Array ( [peep_name] => Mawg ) ) ) [1] => Array ( [job_id] => 5 [job_name] => PFY [peeps] => Array ( [0] => Array ( [peep_name] => William Topaz McGonagall ) ) ) ) ) [2] => Array ( [deperatment_id] => 3 [department_name] => Accounts [jobs] => Array ( [0] => Array ( [job_id] => 6 [job_name] => Number cruncher [peeps] => Array ( [0] => Array ( [peep_name] => Blaise Pascal ) [1] => Array ( [peep_name] => Isaac Newton ) ) ) ) ) ) 

Затем вы можете продолжить работу с json_encode($departments) и т. Д.

Если вы хотите вывести древовидную структуру, вам не нужно использовать циклы. Вы можете просто сделать следующий запрос:

 SELECT p.`peep_name`, j.* FROM `peeps` p INNER JOIN `jobs` j ON j.job_id = p.job_id 

который вернет структуру, такую ​​как:

 peep_name deperatment_id job_id job_name Smith 1 1 Idiot Jones Major 1 2 Fool Jones Minor 1 2 Fool Mr. In-the-wrong-department 2 4 Software guru Mawg 2 4 Software guru William Topaz McGonagall 2 5 PFY Blaise Pascal 3 6 Number cruncher Isaac Newton 3 6 Number cruncher 

Затем вы сможете вывести эти данные в этом формате или объединить эти данные в древовидную структуру желаемого формата. Например, вы можете вывести его в следующем формате:

 { "departments": [ { "ID": 1, "jobs": [ { "ID": 1, "Name": "Idiot" "Peeps": [ "Smith" ] }, { "ID": 2, "Name": "Fool" "Peeps": [ "Jones Major", "Jones Minor" ] } ] }, // etc. ] }