Reputation: 21
I am trying to use all the power of Symfony to build a site. I already have 2 years of experience in Symfony but for the first time, I am trying to use the concept of custom form type to create a specific registration form. I already have a registration form for basic users but now, I want to create another one for managers whom operate hotels.
I have already created a form type named RegistrationFormType and another one named HotelType. My thing is that I want to use both of these form types in one form. But I keep getting an exception which says
Neither the property "user" nor one of the methods "getUser()", "user()", "isUser()", "hasUser()", "__get()" exist and have public access in class "App\Entity\User"
I have followed whatever was said in the Symfony official documentation about custom form types.
I made this form type RegistrationHotelFormType which is made up of another form RegistrationFormType used elsewhere as a form for basic users to create accounts and HotelType generated by Symfony itself.
<?php
namespace App\Form;
use App\Entity\User;
use App\Form\HotelType;
use App\Form\RegistrationFormType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Validator\Constraints\IsTrue;
use Symfony\Component\Validator\Constraints\Regex;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Validator\Constraints\File;
class RegistrationHotelFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('user', RegistrationFormType::class, ['mapped' => false])
->add('hotel', HotelType::class, ['mapped' => false])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
This above use to work but in the Symfony documentation, I don't need to set a mapped parameter to false knowing that I want form values to be assigned to objects fields which are Hotel and User. How can I get this done? (https://symfony.com/doc/current/form/create_custom_field_type.html#creating-form-types-created-from-scratch).
RegistrationFormType:
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Validator\Constraints\IsTrue;
use Symfony\Component\Validator\Constraints\Regex;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Validator\Constraints\File;
class RegistrationFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class, ['error_bubbling'=>true, 'label' => 'Email', 'attr' => ['type'=>'text','class'=>'form-control', 'placeholder'=>'Email', 'aria-label'=>"Username", 'aria-describedby'=>"basic-addon1"]])
->add('plainPassword', RepeatedType::class, [
'type' => PasswordType::class,
'label' => 'Mot de passe',
'attr' => ['error_bubbling'=>true,
'type'=>'text',
'class'=>'form-control',
'placeholder'=>'Mot de passe',
'aria-label'=>"Username",
'aria-describedby'=>"basic-addon1"],
'mapped' => false,
'constraints' => [
new NotBlank([
'message' => 'Mot de passe requis',
]),
new Length([
'min' => 8,
'minMessage' => 'Le mot de passe doit avoir au moins {{ limit }} caractères',
// max length allowed by Symfony for security reasons
'max' => 128,
'maxMessage' => 'Le mot de passe ne doit pas dépasser {{ limit }} caractères',
]),
// new Regex([
// 'pattern' => '/^\S*(?=\S*[a-z])(?=\S*[A-Z])(?=\S*[\d])\S*$/',
// 'message' => 'Le mot de passe doit contenir au moins 1 lettre et 1 chiffre',
// ])
],
])
->add('firstname', TextType::class, ['error_bubbling'=>true, 'label' => 'Prénom', 'attr' => ['type'=>'text','class'=>'form-control', 'placeholder'=>'Prénom', 'aria-label'=>"Username", 'aria-describedby'=>"basic-addon1"]])
->add('surname', TextType::class, ['error_bubbling'=>true, 'label' => 'Nom', 'attr' => ['type'=>'text','class'=>'form-control', 'placeholder'=>'Nom', 'aria-label'=>"Username", 'aria-describedby'=>"basic-addon1"]])
->add('phoneNumber', TextType::class, ['error_bubbling'=>true, 'label' => 'Téléphone', 'attr' => ['type'=>'text','class'=>'form-control', 'placeholder'=>'Téléphone', 'aria-label'=>"Username", 'aria-describedby'=>"basic-addon1"]])
->add('city', TextType::class, ['error_bubbling'=>true, 'label' => 'Ville', 'attr' => ['type'=>'text','class'=>'form-control', 'placeholder'=>'Ville', 'aria-label'=>"Username", 'aria-describedby'=>"basic-addon1"]])
->add('termsAccepted', CheckboxType::class, [
'error_bubbling'=>true,
'mapped' => false,
'constraints' => new IsTrue(),
'attr' => ['type'=>"checkbox", 'class'=>"form-check-input"]
])
->add('resumePDF', FileType::class, [
'error_bubbling'=>true,
'label' => 'CV (Fichier PDF)',
'mapped' => false,
'required' => true,
'constraints' => [
new File([
'maxSize' => '1024k',
'mimeTypes' => [
'application/pdf',
'application/x-pdf',
],
'mimeTypesMessage' => 'Téléchargez un fichier PDF valide',
])
],
'attr' => ['type'=>"file", 'class'=>"custom-file-input"]
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
HotelType
<?php
namespace App\Form;
use App\Entity\Hotel;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class HotelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('address');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Hotel::class,
]);
}
}
RegistrationController.php
/**
* @Route("/inscription-hotel", name="app_register_hotel")
*/
public function registerHotel(Request $request, UserPasswordEncoderInterface $passwordEncoder, \Swift_Mailer $mailer): Response
{
$session = new Session();
// $session->start();
$user = new User();
$form = $this->createForm(RegistrationHotelFormType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
}
return $this->render('registration/register-hotel.html.twig', [
'registrationForm' => $form->createView(),
]);
}
Thanks in advance for your help
Upvotes: 0
Views: 418
Reputation: 8374
Cerad diagnosed why the error is triggered (the second argument to createForm
will be used and the form will try to access the user
and hotel
property on it), the simplest solution follows:
I would remove the data_class
from the combined form, and just omit the second parameter to createForm
in your controller. The return value of $form->getData()
would be an array:
['user' => {User object}, 'hotel' => {Hotel object}]
(you can also provide a similarly structured array to your createForm
call as the second parameter)
of course, you can create a new class that contains both the user and the hotel...
Upvotes: 1