Ответ 1
У меня есть следующий класс утилиты, чтобы сделать именно то, что вам нужно.
class NestingUtil
{
/**
* Nesting an array of records using a parent and id property to match and create a valid Tree
*
* Convert this:
* [
* 'id' => 1,
* 'parent'=> null
* ],
* [
* 'id' => 2,
* 'parent'=> 1
* ]
*
* Into this:
* [
* 'id' => 1,
* 'parent'=> null
* 'children' => [
* 'id' => 2
* 'parent' => 1,
* 'children' => []
* ]
* ]
*
* @param array $records array of records to apply the nesting
* @param string $recordPropId property to read the current record_id, e.g. 'id'
* @param string $parentPropId property to read the related parent_id, e.g. 'parent_id'
* @param string $childWrapper name of the property to place children, e.g. 'children'
* @param string $parentId optional filter to filter by parent
*
* @return array
*/
public static function nest(&$records, $recordPropId = 'id', $parentPropId = 'parent_id', $childWrapper = 'children', $parentId = null)
{
$nestedRecords = [];
foreach ($records as $index => $children) {
if (isset($children[$parentPropId]) && $children[$parentPropId] == $parentId) {
unset($records[$index]);
$children[$childWrapper] = self::nest($records, $recordPropId, $parentPropId, $childWrapper, $children[$recordPropId]);
$nestedRecords[] = $children;
}
}
return $nestedRecords;
}
}
Использование вашего кода:
$employees = json_decode($flat_employees_json, true);
$managers = NestingUtil::nest($employees, 'EmployeeID', 'ManagerEmployeeID', 'employees');
print_r(json_encode($managers));
Вывод:
[
{
"FirstName": "Tom",
"LastName": "Jones",
"EmployeeID": "123",
"ManagerEmployeeID": "",
"Manager Name": "",
"employees": [
{
"FirstName": "Alice",
"LastName": "Wong",
"EmployeeID": "456",
"ManagerEmployeeID": "123",
"Manager Name": "Tom Jones",
"employees": []
},
{
"FirstName": "Tommy",
"LastName": "J.",
"EmployeeID": "654",
"ManagerEmployeeID": "123",
"Manager Name": "Tom Jones",
"employees": []
}
]
},
{
"FirstName": "Billy",
"LastName": "Bob",
"EmployeeID": "777",
"ManagerEmployeeID": "",
"Manager Name": "",
"employees": [
{
"FirstName": "Rik",
"LastName": "A.",
"EmployeeID": "622",
"ManagerEmployeeID": "777",
"Manager Name": "Billy Bob",
"employees": [
{
"FirstName": "Bob",
"LastName": "Small",
"EmployeeID": "111",
"ManagerEmployeeID": "622",
"Manager Name": "Rik A.",
"employees": []
},
{
"FirstName": "Small",
"LastName": "Jones",
"EmployeeID": "098",
"ManagerEmployeeID": "622",
"Manager Name": "Rik A",
"employees": []
}
]
},
{
"FirstName": "Eric",
"LastName": "C.",
"EmployeeID": "222",
"ManagerEmployeeID": "777",
"Manager Name": "Billy Bob",
"employees": []
}
]
}
]
Edit1: Fix, чтобы избежать игнорирования некоторых сотрудников.
Если последний элемент является сотрудником с действующим менеджером, но менеджер отсутствует в списке, то игнорируется, потому что где должно быть расположено?, это не корень, но не имеющий действительного менеджера.
Чтобы избежать этого, добавьте следующие строки непосредственно перед оператором return в утилите.
if (!$parentId) {
//merge residual records with the nested array
$nestedRecords = array_merge($nestedRecords, $records);
}
return $nestedRecords;
Edit2: Обновление утилиты до PHP5.6
После некоторых тестов в PHP7 утилита работает отлично в php7.0, но не в php5.6, я не уверен, почему, но что-то в ссылке на массив и не задано. Я обновляю код утилиты для работы с php5.6 и вашим прецедентом.
public static function nest($records, $recordPropId = 'id', $parentPropId = 'parent_id', $childWrapper = 'children', $parentId = null)
{
$nestedRecords = [];
foreach ($records as $index => $children) {
if (isset($children[$parentPropId]) && $children[$parentPropId] == $parentId) {
$children[$childWrapper] = self::nest($records, $recordPropId, $parentPropId, $childWrapper, $children[$recordPropId]);
$nestedRecords[] = $children;
}
}
if (!$parentId) {
$employeesIds = array_column($records, $recordPropId);
$managers = array_column($records, $parentPropId);
$missingManagerIds = array_filter(array_diff($managers, $employeesIds));
foreach ($records as $record) {
if (in_array($record[$parentPropId], $missingManagerIds)) {
$nestedRecords[] = $record;
}
}
}
return $nestedRecords;
}