Контекст: У вас есть часть приложения на Symfony, отвечающая за управление пользователями. В рамках этой задачи вам необходимо будет отредактировать функцию, которая отвечает за регистрацию пользователей, чтобы сделать её более читаемой и эффективной.
Исходный код:
// src/Controller/UserController.php
namespace App\Controller;
use App\Entity\User;
use App\Form\UserType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class UserController extends AbstractController
{
/**
* @Route("/register", name="register")
*/
public function register(Request $request, EntityManagerInterface $entityManager): Response
{
$user = new User();
$form = $this->createForm(UserType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$hashedPassword = password_hash($user->getPassword(), PASSWORD_BCRYPT);
$user->setPassword($hashedPassword);
$entityManager->persist($user);
$entityManager->flush();
return $this->redirectToRoute('app_home');
}
return $this->render('user/register.html.twig', [
'form' => $form->createView(),
]);
}
}Задание:
- Убедитесь, что код соответствует стандартам PSR.
- Сделайте функции более понятными и структурированными. Если необходимо, выделите в отдельные методы.
- PSR-1: Используются правильные пространства имен и имена классов.
- PSR-2: Код отформатирован с правильными отступами и пробелами.
- PSR-4: Структура каталогов соответствует пространствам имен.
- PSR-1: Соответствие стандартам именования и использования аннотаций.
- PSR-2: Код отформатирован правильно, соблюдаются отступы и пробелы.
- PSR-1: Используются правильные пространства имен и имена классов.
- PSR-2: Код отформатирован с правильными отступами и пробелами.
- PSR-1: Соответствие стандартам именования и использования аннотаций.
- PSR-2: Код отформатирован правильно, соблюдаются отступы и пробелы.
- S - Single Responsibility Principle (SRP):
- Отвечает только за хранение данных пользователя и валидацию.
- S - Single Responsibility Principle (SRP):
- Отвечает только за логику регистрации пользователя.
- O - Open/Closed Principle (OCP):
- Легко расширяем для добавления новых методов валидации или логики регистрации.
- D - Dependency Inversion Principle (DIP):
- Зависит от интерфейса
ValidatorInterfaceиEntityManagerInterface, а не от конкретных реализаций.
- Зависит от интерфейса
- S - Single Responsibility Principle (SRP):
- Отвечает только за обработку запросов, связанных с пользователями.
- S - Single Responsibility Principle (SRP):
- Отвечает только за создание формы для регистрации пользователя.
- Весь код для валидации и обработки регистрации вынесен в
User RegistrationService, что предотвращает дублирование логики в контроллере. - DTO
User RegistrationDTOиспользуется для передачи данных, что уменьшает количество повторяющегося кода.
- Код написан просто и понятно. Например, методы имеют четкие названия, и их логика легко воспринимается.
- Логика регистрации пользователя разбита на небольшие, понятные методы.
- Поведение методов интуитивно понятно. Например, методы
registerUserиhandleRequestделают именно то, что от них ожидается. - Использование DTO для передачи данных также делает код более предсказуемым.
- В
User RegistrationServiceиспользуется валидация данных с помощьюValidatorInterface. Если данные не валидны, возвращается массив ошибок, что позволяет быстро выявить и обработать проблемы. - Контроллер обрабатывает ошибки и отображает их пользователю, что также соответствует принципу быстрого выявления ошибок.
- Код разделен на различные слои: контроллер, сервис и DTO. Каждый класс отвечает за свою область, что улучшает поддержку и тестируемость.
- В
User RegistrationDTOиспользуются приватные свойства с публичными методами доступа, что скрывает внутреннюю реализацию и защищает данные.
- В
User RegistrationServiceиспользуетсяValidatorInterface, что позволяет не зависеть от методов, которые не используются.
- Вместо создания сложных иерархий классов используется композиция через сервисы и DTO, что делает код более гибким и простым.
DTO
// src/DTO/UserRegistrationDTO.php
namespace App\DTO;
use Symfony\Component\Validator\Constraints as Assert;
class UserRegistrationDTO
{
#[Assert\NotBlank]
#[Assert\Length(min: 6)]
private string $username;
#[Assert\NotBlank]
#[Assert\Length(min: 8)]
private string $password;
public function getUsername(): string
{
return $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
}Service
// src/Service/UserRegistrationService.php
namespace App\Service;
use App\DTO\UserRegistrationDTO;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class UserRegistrationService
{
private EntityManagerInterface $entityManager;
private ValidatorInterface $validator;
public function __construct(EntityManagerInterface $entityManager, ValidatorInterface $validator)
{
$this->entityManager = $entityManager;
$this->validator = $validator;
}
public function registerUser(UserRegistrationDTO $userDTO): array
{
$errors = $this->validator->validate($userDTO);
if (count($errors) > 0) {
return (array) $errors;
}
$user = new User();
$user->setUsername($userDTO->getUsername());
$hashedPassword = password_hash($userDTO->getPassword(), PASSWORD_BCRYPT);
$user->setPassword($hashedPassword);
$this->entityManager->persist($user);
$this->entityManager->flush();
return [];
}
}Controller
// src/Controller/UserController.php
namespace App\Controller;
use App\DTO\UserRegistrationDTO;
use App\Form\UserType;
use App\Service\UserRegistrationService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class UserController extends AbstractController
{
#[Route('/register', name: 'register')]
public function register(Request $request, UserRegistrationService $registrationService): Response
{
$userDTO = new UserRegistrationDTO();
$form = $this->createForm(UserType::class, $userDTO);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if ($errors = $registrationService->registerUser ($userDTO)) {
return $this->render('user/register.html.twig', [
'form' => $form->createView(),
'errors' => $errors,
]);
}
return $this->redirectToRoute('app_home');
}
return $this->render('user/register.html.twig', [
'form' => $form->createView(),
]);
}
}
Form
// src/Form/UserType.php
namespace App\Form;
use App\DTO\UserRegistrationDTO;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('username', TextType::class)
->add('password', PasswordType::class);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => UserRegistrationDTO::class,
]);
}
}