Вопрос: Как правильно организовать обработку данных в экшене контроллера?

Лучший ответ: По хорошему валидацию должен делать реквест
Неа. Request - это просто набор данных.Тоесть выходит 2 запроса, 1 - через валидаторы фреймворка, а 2 - когда в контроллере или сервисе достаем эту-же запись для дальнейшей работы.
Вы пытаетесь найти единственное правильно место для проверок. По хорошему проверки должны быть во всех методах, даже в приватных. Что-то не так - бросайте исключение. Эта практика на первый взгляд жуткая, но она вас обезопасит от огромнейшего количества ошибок.
Опережая ваш вопрос: "это ж сколько дублировать проверки придется?". Да, много, но поверьте, оно стоит того.В добавок к этому иногда логика может быть достаточно замысловатой
Если вы пытаетесь в одной точке захватить множество контекстов - безусловно. Если же будете делать необходимые проверки всюду - эта сложность не возникнет.Мне не очень нравится идея выносить куски логики в реквест
Мысль здравая.Соответственно получается неудобная ситуация, когда часть валидации нужно разместить в реквесте, а часть в сервисе.
Не нарушайте SRP.<?php

namespace Vendor\Project\AppBundle\Controller;

use KoKoKo\assert\Assert;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Vendor\Project\Path\Authorization\UserNotFoundException;
use Vendor\Project\Path\Authorization\InvalidCredentialsException;

class LoginController extends Controller
{
    /**
     * @Route("/login", name="login")
     * @Method({"POST"})
     * @param Request $request
     * @return JsonResponse
     */
    public function loginAction(Request $request) : JsonResponse
    {
        try {
            $login = $request->request->get('login');
            $pass  = $request->request->get('pass');

            Assert::assert($login, 'login')->string()->notEmpty()->match('/^[a-z\d]{3,32}$/i');
            Assert::assert($pass, 'pass')->string()->notEmpty()->lengthBetween(6, 32);
        } catch (\Throwable $exception) {
            return new JsonResponse($exception->getMessage(), JsonResponse::HTTP_BAD_REQUEST);
        }

        try {
            $user = $this->get('UserAuthorizator')->authorize($login, $pass);

            return new JsonResponse($user->getId());
        } catch (UserNotFoundException $exception) {
            return new JsonResponse('User not found', JsonResponse::HTTP_BAD_REQUEST);
        } catch (InvalidCredentialsException $exception) {
            return new JsonResponse('Invalid login or path', JsonResponse::HTTP_BAD_REQUEST);
        } catch (\Throwable $exception) {
            $this->get('logger')->error($exception->getMessage(), ['exception' => $exception]);

            return new JsonResponse('Something went wrnog :((', JsonResponse::HTTP_INTERNAL_SERVER_ERROR);
        }
    }
}

Худший ответ: К сожалению, никак. Чекни это: https://github.com/illuminate/validation/blob/mast...
Запросы к базе идут напрямую, по-этому передать готовую модель валидатору невозможно. Так-же посмотри на это: https://github.com/illuminate/validation/blob/mast...
Тоже хардкодинг. Laravel - не волшебная палочка, и очеень далек от совершенства. С ним стает очень тяжело при выполнении задач сложнее CRUD. Но всегда есть варианты:
1) Можно создать свой класс валидатора, который будет наследовать обычный, там переписать методы проверки из базы, а так-же переписать DatabasePresenceVerifier, дабы тот юзал переданную модель. Но все это явно не будет элегантным и привальным решением, чисто как вариант.
2) Использовать сторонние валидаторы.
3) И самый простой - просто доставать модель, а потом уже $model->toArray() передавать валидатору. Таким образом можно полностью контролировать что и откуда достается, а так-же делать разные типы валидаций, выходящие за рамки обычного валидатора. Но в таком подходе тоже есть минусы - часть обычных методов валидатора можно выкинуть, таких как unique и тд. 
Если 3 вариант не подходит, что скорее всего именно так, то нужно искать абсолютно кастомные верификаторы.
Других вариантов нету. Использовать встроенные средства Ларьки - ад. Я вот UUID в виде binary храню, так мне тут половину фреймворка раскопать надо, что-бы реализовать это нормально. Symfony в этом плане явно выигрывает.