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

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.

Related

doctrine : ManyToOne : the field is inserted in the DB with NULL value [duplicate]

I'm searching how to implement a system of choosing languages dynamically using form builder.
so we must have two html array inputs field : name="languages[]" and the second will be name="languges_level[]"
So this system allows the user to set the language which he can speak with his level on it.
The User can add/remove Language dynamically before he submits the Form.
The Questions :
1- Form Level : what will be the field form type? I have to add 2 form fields which will be combined to create my result array which will be stored in the database. So this two field will be not mapped with ORM.
->add('langues', TYPEXXX:class)
->add('langues_level', TYPEXXX:class)
3- Twig Level: should i make some change in the twig as well?
So what will be the best solution in my case?
My first try is :
->add('languages', CollectionType::class, array(
'entry_type' => ChoiceType::class,
'entry_options' => array(
'choices' => array(
'Français' => 'Français',
'English' => 'English',
'Italien' => 'Italien',
'Espanish' => 'Espanish',
),
'label' => ' ',
),
))
->add('language_levels', CollectionType::class, array(
'entry_type' => ChoiceType::class,
'entry_options' => array(
'choices' => array(
'Beginner' => 'Beginner',
'Medium' => 'Medium',
'Good' => 'Good',
),
'label' => ' ',
),
));
but this don't work as I mentioned in the picture .. who had a perfect solution plz ?
I think you need a Collection of Forms.
so we must have two html array inputs field : name="langues[]" and the
second will be name="langes_level[]"
(..)
3- Twig Level: should i make some change in the twig as well?
(..)
4- Javascript Level, i can develop it when the inputs are clean
created in the html.
No, no and no. Describing how your 'array input fields' should be named exactly is not the right mentality if you're using a framework like Symfony. Describe your entity fields, describe your form fields and Symfony will give all form elements a name. Symfony Forms will render and handle the form for you, so there is (very likely) no need to be bothered what the form element names are exactly.
Your entity class:
class LanguageLevel
{
protected $user;
protected $language;
protected $level;
//getters and setters
}
Create a form type:
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('languages', CollectionType::class, array(
'entry_type' => LanguageLevelType::class,
'allow_add' => true,
));
}
}
And a LanguageLevelType:
class LanguageLevelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('language', LanguageType::class)
->add('level', ChoiceType::class, array(
'choices' => array(
'Good' => 5,
'Bad' => 1,
),
));
}
}
If the rendered output is not what you want, check the documentation if you can configure the Form Types. Manually changing the twig template, your controller and/or javascripts for a specific case is possible, but I think the 'Collection of Forms' from above will cover your use case.

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.

Symfony2 DateTime null accept

So, I want to be able send a null option to my DOB field.
Here is my form builder:
->add('birthDate', DateType::class, array(
'widget' => 'single_text',
'format' => 'yyyy-MM-dd'))
And here is those field in my entity
/**
* #ORM\Column(
* type="date",
* nullable=true
* )
* #JMS\Groups("single")
*
* #var \DateTime
*/
protected $birthDate;
When I`m trying to send a null I got an error msg
Expected argument of type "DateTime", "NULL" given
any ideas?
CRITICAL - Uncaught PHP Exception Symfony\Component\PropertyAccess\Exception\InvalidArgumentException: "Expected argument of type "DateTime", "NULL" given" at /var/www/server.local/vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php line 253
$type = $trace[$i]['args'][0];
$type = is_object($type) ? get_class($type) : gettype($type);
throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given', substr($message, $pos, strpos($message, ',', $pos) - $pos), $type));
}
}
In this case, the problem was caused by PHP type hinting.
If you use type hinting (for instance setBirthDate(\DateTime $value)) then PHP forces you that you actually provide a DateTime object. Obviously, null is not such an object. To resolve this problem, it is possible to give $value a default value like this: setBirthDate(\DateTime $value = null).
This is documented behavior and explained in the PHP Documentation (http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration).
Relevant passage:
To specify a type declaration, the type name should be added before the parameter name. The declaration can be made to accept NULL values if the default value of the parameter is set to NULL.
The problem occurs due type-hinted setter as it is mentioned in the comments. There are two solutions:
1. Use 'by_reference' => true on your form:
$builder->add(
'birthDate',
DateType::class,
[
'widget' => 'single_text',
'format' => 'yyyy-MM-dd',
'by_reference' => true,
]
);
2. Let your setter accept null:
public function setBirthDate(\DateTime $value = null)
{
.....
}
Don't pass any values to it. Make the field not required by doing this:
->add(
'birthDate',
DateType::class,
array(
'required' => false,
'widget' => 'single_text',
'format' => 'yyyy-MM-dd'
)
)
I have been using DOB field in my project. Try this.
My ORM file looks like this <field name="dob" type="date" column="dob" nullable="true"/>
->add('dob','birthday',array(
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
'required' => false,
'attr' => array('class' => 'datepicker',
'data-provide' => 'datepicker','data-date-format' => 'dd-mm-yyyy')
))

Add roles in FOSUserBundle

I feel the same as a forum user who posted this:
I implemented FOSUserBundle, and I want to add to RegisrationFormType roles that are taken from a table. When I had it like this:
->add('roles', 'choice', array('label' => 'Rol', 'required' => true,
'choices' => array( 'ROLE_ADMIN' => 'ADMINISTRADOR','ROLE_SUPERADMIN' => 'SUPERADMINISTRADOR',
'ROLE_USER' => 'USUARIO'), 'multiple' => true))
And it works! But they must leave the BD, I can not put the Entity field because roles should be an array, not an object. How I can generate the array with the roles taken from a table? In FosUSerbundle as you would add roles?
Thanks ....
I write because that user had no answer. I followed [the steps of official documentation] (https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/index.md) and adding the above lines in the register of FOSUserBundle works, but I want to work this from the database.
And then I used to create groups this. Two additional tables were created and even now joined a group or role in the list, but not how to show the login to register a new user.
Has anyone solved it?
So you have the roles in a table? You could inject the EntityManager in the form type and use it to fetch the choices.
->add('roles', 'choice', array(
'label' => 'Rol',
'choices' => $this->getRoles(),
'multiple' => true,
'required' => true,
))
Then create a method which gives you the data the way you need.
Something like:
public function getRoles()
{
$roles = array();
$er = $this->em->getRepository('AppBundle:Role');
$results = $er->createQueryBuilder('r')
// conditions here
->getQuery()
->getResult();
// process the array?
foreach ($results as $role) {
$roles[$role->getId()] = $role->getLabel();
}
return $roles;
}

Render a Collection of Text Fields which is passed to Doctrine array field

In my entity i have an array field:
/**
* #var array
*
* #ORM\Column(name="work_experience", type="array")
*/
private $workExperience;
now i want to render a collection of text fields which will be passed to this array field.
->add('workExperience', 'collection', array(
'type' => 'text',
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
#'by_reference' => false,
'options' => array(
'required' => false,
'attr' => array('class' => 'line-box')
),
))
but now when i render this field, no input is shown? What is my mistake?
{{ form_row(form.workExperience) }}
Thank you
When prototyping, the collection field(s) is only render if your entity has a value assigned to workExperience inside your controller, Otherwise you would need to use javascript to take the prototype info and create the input field(s), this is also true if you want to add new field(s) with or without your entity having any value.
To get the following to render with values
{{ form_row(form.workExperience) }}
You can do something like the following:
public function controllerAction(Request $request)
{
//By populating your entity with values from your database
//workExperience should receive a value and be rendered in your form.
$em = $this->getDoctrine()->getManager();
$entity = $em
->getRepository('yourBundle:entity')
->findBy(...yourParameters...);
$form = $this->createForm('your_form_type', $entity);
...
Or
...
//If you do not have any data in your database for `workExperience`
//then you would need to set it in your controller.
$arr = array('email' => 'name#company.com', 'phone' => '888-888-8888');
$entity->setWorkExperience($arr);
$form = $this->createForm('your_form_type', $entity);
...
Keep in mind that collection are usually used for one-to-many or many-to-many relationships.
Using it for array can be done but there is not much documented on it. While this link is not a perfect fit, the general ideas presented many be helpful: form_collections

Resources