Делается ровно так как описано в вопросе.
Начинаете обход массива в цикле через
foreach и пишете всю логику
Делать предварительно выборку всех потомков родителя нет смысла. Всё можно сделать в один проход по всем элементам используя результирующий массив
$result как хранилище состояния
$result = [];
//перебираем в цикле все элементы из исходного массива
foreach($input as $row)
{
//есть поле - значит это дочерний элемент
if(isset($row->dates))
{
foreach($row->dates as $date_val)
{
//тут нужная логика по нахождению мин макс даты у потомков
}
//нужные значения записываем по id-родителя в $result
//$result[$row->parent_id]['min_date'] = ...
}
else
{
//вносим родителя в $result
$result[$row->id] = $row;
}
}