components-web-app/api-components-bundle

View on GitHub
src/Form/Type/User/ChangePasswordType.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

/*
 * This file is part of the Silverback API Components Bundle Project
 *
 * (c) Daniel West <daniel@silverback.is>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace Silverback\ApiComponentsBundle\Form\Type\User;

use Silverback\ApiComponentsBundle\Entity\User\AbstractUser;
use Silverback\ApiComponentsBundle\Entity\User\UserInterface;
use Silverback\ApiComponentsBundle\Exception\InvalidArgumentException;
use Silverback\ApiComponentsBundle\Form\AbstractType;
use Silverback\ApiComponentsBundle\Repository\User\UserRepositoryInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Daniel West <daniel@silverback.is>
 */
class ChangePasswordType extends AbstractType
{
    private Security $security;
    private UserRepositoryInterface $userRepository;
    private string $userClass;

    public function __construct(Security $security, UserRepositoryInterface $userRepository, string $userClass)
    {
        if (!is_subclass_of($userClass, AbstractUser::class)) {
            throw new InvalidArgumentException(sprintf('The user class `%s` provided to the form `%s` must extend `%s`', $userClass, __CLASS__, AbstractUser::class));
        }
        $this->security = $security;
        $this->userRepository = $userRepository;
        $this->userClass = $userClass;
    }

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        /** @var UserInterface|null $user */
        $user = $builder->getEmptyData();

        $builder
            ->add(
                'username',
                // cannot be HiddenType otherwise it will be null if empty - see: https://github.com/symfony/symfony/issues/39148
                TextType::class,
                [
                    'empty_data' => '',
                    'attr' => ['autocomplete' => 'username'],
                    'data' => $user ? $user->getUsername() : '',
                    'disabled' => true,
                ]
            )
            ->add(
                'oldPassword',
                PasswordType::class,
                [
                    'label' => 'Current password',
                    'attr' => ['autocomplete' => 'current-password'],
                ]
            )
            ->add(
                'plainPassword',
                RepeatedType::class,
                [
                    'type' => PasswordType::class,
                    'invalid_message' => 'The passwords you entered are not the same.',
                    'first_options' => [
                        'label' => 'Password',
                        'attr' => ['autocomplete' => 'new-password'],
                    ],
                    'second_options' => [
                        'label' => 'Repeat Password',
                        'attr' => ['autocomplete' => 'new-password'],
                    ],
                ]
            );
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        /** @var AbstractUser $securityUser */
        $securityUser = $this->security->getUser();

        // With JWT, we are not hitting the database for the user, but in this case we should be.
        // This is so we can detect change sets in the form listeners.
        $databaseUser = $this->userRepository->find($securityUser->getId());

        $resolver->setDefaults(
            [
                'csrf_protection' => false,
                'attr' => [
                    'novalidate' => 'novalidate',
                ],
                'data_class' => $this->userClass,
                'validation_groups' => ['User:password:create', 'User:password:change'],
                'empty_data' => $databaseUser,
            ]
        );
    }
}