Как лучше реализовать код?
Если уж ударяться в ООП - я бы сделал отдельный класс который делает эту хитрую логику с подзадачами и вызывал бы его из контроллера.
$taskManager = new TaskManager(); // вынести инстанцирование во вне а еще лучше использовать DI
// таск есть таск, имеет он при себе родителя или нет
// по сути у нас обычное дерево
$parentTask = new Task();
$subTask = new Task($parentTask);
$subSubTask = new Task($subTask);
// вся логика по сохранию тасков инкапсулируется в менеджер тасков
$taskManager->addTask($parentTask);
$taskManager->addTask($subTask);
$taskManager->addTask($subSubTask);
Если используете Doctrine в качестве ORM, то в менеджере вы должны делать только persist записи. flush в идеале должен быть только один (коммит транзакции), и желательно его делать в каком-нибудь так же даже не в контроллере, а сделать глобальный обработчик, мол если у нас не возникло ошибок во время обработки http запроса - делаем flush. Таким образом мы никогда не забудим закоммитить изменения в базу.