Совсем недавно мне в руки попалась задачка со следующим содержанием:
Необходимо создать отчет по сотрудникам за выбранный период времени. При формировании необходимо отразить кол-во занесенных часов с учетом времени подчиненных. У каждого сотрудника могут быть подчинённые, причем у подчиненных могут быть свои подчиненные и т.д.
Пример входных данных:
Таблица сотрудников:
id (int) | name (string) | email (string) | employer (int) | info (text) |
1 | Петр | petr@tm.ru | 0 | |
2 | Федор | fedor@tm.ru | 1 | |
3 | Николай | nik@tm.ru | 0 | |
4 | Яна | yana@tm.ru | 2 | |
5 | Антон | anton@tm.ru | 3 | |
6 | Екатерина | katya@tm.ru | 1 |
Рабочее время по каждому сотруднику:
employeeId (int) | time (string) | date (date) |
3 | 2:00:00 | 2017-02-10 |
1 | 5:00:00 | 2017-02-15 |
2 | 1:00:00 | 2017-02-11 |
3 | 4:30:00 | 2017-02-12 |
Задача на первый взгляд достаточно простая, единственная сложность в том, что неизвестно максимальный уровень иерархии. Таким образом придётся использовать рекурсивную функцию для обхода всего древа и вычисления суммы.
Первое, что мы сделаем, это представим наше древо сотрудников в виде списка смежности. Для этого считаем всю таблицу сотрудников в массив с индексами соответствующими id-сотрудника. И каждому элементу добавим еще ряд, соответствующий id всех подчинённых. Плюс, нулевым элементом запишем id всех сотрудников имеющих высший уровень иерархии. На выходе получим следующее:
[ [0] => [1, 3], [1] => [2, 6], [2] => [4], [3] => [5], [4] => [], [5] => [], [6] => [], ]
Визуально это выглядит так:
Теперь нам остается только подсчитать сумму часов по каждому работнику, но следует помнить, что сумма часов у работника с id:2 будет равняться сумме часов этого же работника и всех его подчиненных, в данном случае id:2+id:4. А вот уже сумма id:1=id:2+id:4+id:6.
Поскольку мы не знаем максимального уровня иерархии, мы будем использовать рекурсивную функцию для спуска вниз по иерархии и считать часы на всех уровнях итерации начиная с конца. Для этого потребуется две функции:
- sumLevel() — будет считать сумму на текущем уровне иерархии.
- sumAll() — спускается по иерархии и считает общую сумму по каждому сотруднику.
В качестве параметров для этих функций будем использовать только массив с id текущего уровня, а общий массив сотрудников с их часами и другими данными будет идти как глобальный.
Функция sumLevel():
function sumLevel($array = []) { global $tmp_employers; // глобальный массив сотрудников $sumAll = 0; // проход по уровню и вычесление суммы всех сотрудников этого уровня foreach($array AS $item) { $sumAll += $tmp_employers[$item]['sum_all']; } return $sumAll; }
Функция sumAll():
function sumAll($array = []) { global $tmp_employers; $sumAll = 0; foreach($array AS $item) { if(count($tmp_employers[$item]['employers']) > 0) { sumAll($tmp_employers[$item]['employers']); // подсчет часов на каждом уровне } /* сумма всех часов работника равняется сумме его часов + сумме всех часов работников на уровень ниже */ $tmp_employers[$item]['sum_all'] = $tmp_employers[$item]['sum_single'] + sumLevel($tmp_employers[$item]['employers']); } return false; }
На этом основная часть задачи выполнена. Как считать данные с базы и вывести их на экран я не рассматривал, так как хотел разобрать именно алгоритм вычисления суммы в иерархии.
Результат со входными данными получился следующий:
# | Имя | Кол-во часов с подчиненными | Кол-во часов без подчиненных | |
1 | Петр | petr@tm.ru | 6 | 5 |
2 | —Федор | fedor@tm.ru | 1 | 1 |
3 | —-Яна | yana@tm.ru | 0 | 0 |
4 | —Екатерина | katya@tm.ru | 0 | 0 |
5 | Николай | nik@tm.ru | 6.5 | 6.5 |
6 | —Антон | anton@tm.ru | 0 | 0 |
Полный код можно скачать здесь.