Symfony 2 : Add a custom form element, not in an Entity - symfony

I work with Symfony2 and I would like to create a registration form.
I don't want to use FOSUserBundle.
So, I create an Entity Account (with fields : username, password, email...) and I create the form :
$account = new Account();
$form = $this->createFormBuilder($account)
->add('username', 'text', array('label' => 'Nom de compte :'))
->add('password', 'password', array('label' => 'Mot de passe :'))
->add('email', 'email', array('label' => 'Adresse email :'))
->getForm();
Now, I want to add a confirmation field for the password.
But, when I try to add a field with add() method, for example "password_confirmation"
I have this :
Neither property "password_confirmation" nor method "getPasswordConfirmation()" nor method "isPasswordConfirmation()" exists in class "App\FrontBundle\Entity\Account"
How can I add a custom field ? And after, how to valid it ?
Thank you.
BR.

An update for Symfony 2.1:
property_path has been deprecated and instead you should use mapped. The syntax remains the same:
->add('password_confirmation', 'password', array('mapped' => false))

In a normal situation, you'd need to explicitly specify that *password_confirmation* isn't part of the entity, using the property_path option.
->add('password_confirmation', 'password', array('property_path' => false))
And then to validate it with a CallBackValidator.
But, in this specific case, where you want to repeat a field, the repeated widget can do that for you.
->add('password_confirmation', 'repeated', array(
// See the docs :)
));

Related

How to prefill field of type EntityType from PHP

In my form, I have a field of type EntityClass:
$builder
->add(
'user',
EntityType::class,
[
'required' => false,
'label' => 'User',
'placeholder' => 'Please choose',
'choice_label' => 'email',
'choice_value' => 'id',
'class' => 'AppBundle:User',
]
)
;
This field works fine - until I try to pre-fill it from my PHP code. Then it stays empty, and only shows "Please choose".
Pre-filling looks like this:
$user = $this->userRepository->find(...);
$form->get('user')->setData($user);
But it also does not work if I call ->setData($user->getId()), or even ->setData($user->getEmail()).
So how do I prefill a field of type EntityType?
You should not prefill Form, you should prefill Model, if you need it.
$user = $this->userRepository->find(...);
$entity = new YourEntity();
$entity->setUser($user);
$form = $this->createForm(YourEntity::class, $entity);
And it's not about EntityType. It's about any Type in Symfony - there is no way to bind a default value for them. Data is binded on Model.
UPD from comment: It's not true, that Form could be used without Model. It could be used without Doctrine Entity or any other ORM (or not ORM) Entity. But they still operate with data, i.o. with model.
\Symfony\Component\Form\FormFactoryInterface has definition
public function create($type = 'form', $data = null, array $options = array());
So some kind of $data is always present when you're using Form Component.

Capitalize filter in symfony2 Constraint Messages

I'm new to symfony. I searched a lot especially in the symfony documentation:
http://symfony.com/doc/current/book/translation.html#translating-constraint-messages
But I found nothing about it.
I know how to use capitalize twig filter in a twig template.
http://twig.sensiolabs.org/doc/filters/capitalize.html
{{ 'my first car'|capitalize }}
But is it possible to capitalize a translation in these three scenarios:
In entities annotations Constraints Messages (this example does not work, translation is not translated if I add |capitalize)
class Author
{
/**
* #Assert\NotBlank(message = "author.name.not_blank|capitalize")
*/
public $name;
}
In a form builder (this example does not work, translation is not translated if I add |capitalize)
->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'invalid_message' => 'user.password_confirm_mismatch|capitalize',
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password'),
'required' => false
)
In a controller with the translator service. In this case it is not very important because I can use the php ucfirst function. But maybe there is another method more in line with symfony best practices.
//Not capitalized, any way to add twig capitalize filter in this code?
$tranlsation_test_1 = $this->get('translator')->trans('info.profile_updated_successfully',array(),null,$new_user_app_language_code);
//Capitalized but with the php ucfirst function
$tranlsation_test_2 = ucfirst($this->get('translator')->trans('info.profile_updated_successfully',array(),null,$new_user_app_language_code));
Many tanks for your advices.

Symfony2, Sonata, FormMapper, add hidden field to be handled in PrePersist/PreUpdate

I actually did some tricks so i could be able to persist a user if its ID is passed by an url parameter. (Custom action from user list).
/admin/se/api/bundle/create?user=7
I actually could not find how to send the user entity returned by a findByOne(array('id' => $user_id)) so i guess i'll need to pass the $user_id through a hidden field and handle its value in a PrePersist
Otherwise passing the id that way
->add('user', 'hidden', array('data' => $user_id))
will return an error :
This value is not valid.
Symfony\Component\Validator\ConstraintViolation
Object(Symfony\Component\Form\Form).children[user] = 7
Caused by:
Symfony\Component\Form\Exception\TransformationFailedException
Compound forms expect an array or NULL on submission.
This is my first attempt that is not working :
$container = $this->getConfigurationPool()->getContainer();
$request = $container->get('request');
$user_id = $request->get('user');
if(!empty($user_id)){
$em = $this->getModelManager()->getEntityManager($this->getClass());
$user = $em->getRepository('ApiBundle:User')->findOneBy(array('id' => $user_id));
if($user){
$formMapper
->with('User', array('description' => '<strong>User : </strong>'.$user->getDisplayName()))
->add('user', 'hidden', array('data' => $user_id))
// this of course doesn't work as explained above. How can i have my own hidden input not related to any property
->end();
}
So how would i do that? Any better solution is welcomed.
Well this is the best trick i found. I wish 'sonata_type_model_hidden' has more options. I guess i could do my own custom field to be able to do that. But i'm not sure how and anyway this solution is fast to implement.
$formMapper
->with('Guide', array('description' => '<strong>Guide : </strong>'.$guide->getDisplayName()))
->add('guide', 'sonata_type_model_autocomplete', array(
'property' => array('firstname', 'lastname', 'username', 'email'),
'data_class' => null, // IMPORTANT
'data' => $guide,
'attr' => array('class' => 'sonata-autocomplete-hidden'), // custom class
'label_attr' => array('class' => 'sonata-autocomplete-hidden'), // custom class
)
)
->end();
To hide the field :
.sonata-autocomplete-hidden{
display:none;
}
If you have any better solutions, you're welcome.

Symfony: Using "action" as a form field name causes issues

I have a form type that has this field:
->add('action', 'text', array(
'required' => false,
))
The form type maps to an entity that has a property action.
The problem is when I render this form in twig, the field is pre-populated with the form's HTTP action URL, which is clearly not what I want.
Do I have to rename the form field and entity property to avoid this collision?
I was incorrectly initializing the form in my controller. I was doing this:
$filterForm = $this->createForm(new ActionLogFilterType(), array(
'action' => $this->generateUrl('admin.action_logs.index'),
'method' => 'GET',
));
When I needed to do this:
$filterForm = $this->createForm(new ActionLogFilterType(), null, array(
'action' => $this->generateUrl('admin.action_logs.index'),
'method' => 'GET',
));
So it turns out I can indeed use action as a form field as I hoped.
In Symfony 2.3 are the variables method and action added, see http://symfony.com/doc/current/reference/forms/twig_reference.html. So, yes, you will have to rename it.

Save form data temporary

The user has the ability to filter a list of data. For this use case i build a form type "UserFilterType", which look like this.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('firstName', 'text', array('attr' => array('maxlength' => 255)))
->add('roles', 'entity', array(
'class' => 'ApplicationUserBundle:Role',
'property' => 'name',
'label' => false,
'multiple' => false,
'expanded' => false,
'empty_value' => 'msg.role.all',
'translation_domain' => 'role', 'required' => false)
));
}
So far everything works very well. The user can filter the output. But if the user wants to sort the output, eg by first name. A get request executed and filter options lost. I will save the filter options temporary, maybe in a session.
I try the following option:
$session = $this->getRequest()->getSession();
$session->set('userFilter', serialize($form->getData());
.....
$form->setData(unserialize($session->set('userFilter')));
The form fields are filled out correctly, but if i execute the filter again with a post request i get the following error message
"Entities passed to the choice field must be managed. Maybe persist them in the entity manager?"
Does anyone have any idea how I should proceed? I hope I could describe my problem understandable.
Instead of field type 'entity', you might want to use 'choice' and set the querybuilder for the choicelist in the form.
This is caused by the fact, that 'entity' type field expects an Entity as a value. When you unserialize the data, even if it was an Entity from the database, the EntityManager doesn't know this(the unserialized object is just an instance of ApplicationUserBundle:Role, it might not be in the database yet) so this is not a "managed" entity.
You can also add the EntityManager that you found a "lost lamb" and add it back to the manager by:
$filters = unserialize($session->set('userFilter'));
$roles = $filters['roles'];
$entityManager->merge($roles);
This way you tell the EntityManager that this object is already persisted(through ->persist) and EntityManager should treat it as an object fetched from the database
but you need to test it

Resources