I created a form to allow user to register. Then, I used the same form to allow user to edit his informations. I created his profile page on login.html.twig and I wish to allow him to access to one button so that when he clicks on it, I can upload his picture, and without clicking on submit button his picture would be sent to data.
The file isn't stored and its name is not sent to my database, and it ended up with this error Expected argument of type "string", "null" given at property path "password" which I don't understand because the form is also display on the page where user can edit all other informations, and there is no problem to keep password inputs empty.
How can make it work ?
I'll be so grateful if you help me find the solution.
login.html.twig
{% block body %}
{% if app.user %}
{{ form_start(form, {'attr' : {'class' : 'position-absolute top-50 start-50 translate-middle'}} ) }}
{{ form_widget(form.photo, {'attr' : {'onChange' : 'this.form.submit();'}} ) }}
{{ form_widget(form.gender, {'attr' : {'class' : 'd-none'}}) }}
{{ form_widget(form.lastname, {'attr' : {'class' : 'd-none'}}) }}
{{ form_widget(form.firstname, {'attr' : {'class' : 'd-none'}}) }}
{{ form_widget(form.birthdate, {'attr' : {'class' : 'd-none'}}) }}
{{ form_widget(form.occupation, {'attr' : {'class' : 'd-none'}}) }}
{{ form_widget(form.nationality, {'attr' : {'class' : 'd-none'}}) }}
{{ form_widget(form.nativelanguage, {'attr' : {'class' : 'd-none'}}) }}
{{ form_widget(form.email, {'attr' : {'class' : 'd-none'}}) }}
{{ form_widget(form.password.first, {'attr' : {'class' : 'd-none'}}) }}
{{ form_widget(form.password.second, {'attr' : {'class' : 'd-none'}}) }}
{{ form_widget(form.save, {'attr' : {'class' : 'd-none'}}) }}
{{ form_end(form) }}
{% endif %}
{% endblock %}
UserController.php
<?php
namespace App\Controller;
use App\Entity\User;
use App\Form\UserType;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\String\Slugger\SluggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
class UserController extends AbstractController
{
#[Route('/', name: 'app_user_index', methods: ['GET'])]
public function index(UserRepository $userRepository): Response
{
return $this->render('user/index.html.twig', [
'users' => $userRepository->findAll(),
]);
}
#[Route('/register', name: 'register', methods: ['GET', 'POST'])]
public function createUser(
Request $request,
EntityManagerInterface $entityManagerInterface,
UserPasswordHasherInterface $userPasswordHasherInterface
){
$user = new User();
$userform = $this->createForm(UserType::class, $user);
$userform->handleRequest($request);
if ($userform->isSubmitted() && $userform->isValid()) {
$user->setRoles(["ROLE_USER"]);
$plainPassword = $userform->get('password')->getData();
$hashedPassword = $userPasswordHasherInterface->hashPassword($user, $plainPassword);
$user->setPassword($hashedPassword);
$entityManagerInterface->persist($user);
$entityManagerInterface->flush();
return $this->redirectToRoute('home');
}
return $this->render('front/register.html.twig', [
'userform' => $userform->createView()
]);
}
#[Route('/user/{id}', name: 'app_user_show', methods: ['GET'])]
public function show(User $user): Response
{
return $this->render('user/show.html.twig', [
'user' => $user,
]);
}
#[Route('/profile', name: 'profile')]
public function addPhoto(
Request $request,
UserRepository $userRepository,
SluggerInterface $sluggerInterface,
EntityManagerInterface $entityManagerInterface
){
$connected = $this->getUser();
$useremail = $connected->getUserIdentifier();
$user = $userRepository->findOneBy(['email' => $useremail]);
$userform = $this->createForm(UserType::class, $user);
$userform->handleRequest($request);
if ($userform->isSubmitted() && $userform->isValid()) {
$imagefile = $userform->get('photo')->getData();
if ($imagefile){
$originalFileName = pathinfo($imagefile->getClientOriginalName(), PATHINFO_FILENAME);
$safeFileName = $sluggerInterface->slug($originalFileName);
$newFileName = $safeFileName . '-' . uniqid() . '.' . $imagefile->guessExtension();
$imagefile->move(
$this->getParameter('images_directory'),
$newFileName
);
$user->setPhoto($newFileName);
}
$entityManagerInterface->persist($user);
$entityManagerInterface->flush();
return $this->redirectToRoute('login');
}
return $this->renderForm('security/login.html.twig', [
'user' => $user,
'form' => $userform,
]);
}
#[Route('/update', name: 'update')]
public function updateUser(
Request $request,
UserRepository $userRepository,
EntityManagerInterface $entityManagerInterface,
UserPasswordHasherInterface $userPasswordHasherInterface
){
$connected = $this->getUser();
$useremail = $connected->getUserIdentifier();
$user = $userRepository->findOneBy(['email' => $useremail]);
$userform = $this->createForm(UserType::class, $user);
$userform->handleRequest($request);
if ($userform->isSubmitted() && $userform->isValid()) {
$plainPassword = $userform->get('password')->getData();
$hashedPassword = $userPasswordHasherInterface->hashPassword($user, $plainPassword);
$user->setPassword($hashedPassword);
$entityManagerInterface->persist($user);
$entityManagerInterface->flush();
return $this->redirectToRoute('home');
}
return $this->renderForm('front/edituser.html.twig', [
'user' => $user,
'form' => $userform,
]);
}
#[Route('/delete', name: 'delete')]
public function deleteUser(
UserRepository $userRepository,
EntityManagerInterface $entityManagerInterface
){
$connected = $this->getUser();
$useremail = $connected->getUserIdentifier();
$user = $userRepository->findOneBy(['email' => $useremail]);
$entityManagerInterface->remove($user);
$entityManagerInterface->flush();
return $this->redirectToRoute('home');
}
}
UserType.php
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\CountryType;
use Symfony\Component\Form\Extension\Core\Type\BirthdayType;
use Symfony\Component\Form\Extension\Core\Type\LanguageType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('gender', ChoiceType::class, [
'choices' => [
'Je suis ...' => '',
'un homme' => 'male',
'une femme' =>'female',
'non-binaire' => 'non-binary'
]
])
->add('lastname')
->add('firstname')
->add('birthdate', BirthdayType::class, [
'placeholder' => [
'year' => 'Année', 'month' => 'Mois', 'day' => 'Jour',
],
'choice_translation_domain' => true
])
->add('occupation')
->add('nationality', CountryType::class, [
'placeholder' => 'Je choisis un pays',
])
->add('nativelanguage', LanguageType::class, [
'placeholder' => 'Je choisis ta langue maternelle',
])
->add('email')
->add('password', PasswordType::class, [
'mapped' => false,
'empty_data' => ''
])
->add('password', RepeatedType::class, [
'type' => PasswordType::class,
'invalid_message' => 'Les deux mots de passe doivent être identiques.',
'options' => ['attr' => ['class' => 'password-field']],
'required' => true,
'first_options' => ['label' => 'Password'],
'second_options' => ['label' => 'Repeat Password'],
'empty_data' => ''
])
->add('photo', FileType::class, [
'mapped' => false,
])
->add('save', SubmitType::class, [
'attr' => ['class' => 'save'],
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => User::class,
'translation_domain' => 'forms'
]);
}
}