Symfony2 FormBuilder with Entity class - symfony

I have a form that works well, there is just one issue with it and I'm hoping that I'll get an answer on how to do what I need to do.
<?php
namespace ADS\UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Security\Core\SecurityContext;
class UserType extends AbstractType {
private $type;
public function __construct($type) {
$this->type = $type;
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('firstName', 'text', array('required' => true))
->add('lastName', 'text', array('required' => true));
$builder->add('email', 'email', array('required' => true));
$builder->add('parentCompany', 'entity', array(
'class' => 'ADSUserBundle:Company',
'expanded' => false,
'empty_value' => 'CHOOSE ONE',
'required' => false,
'property' => 'companyName'
))
->add('enabled', 'choice', array('choices' => array('1' => 'Enabled', '0' => 'Disabled')))
->add('roles', 'entity', array(
'class' => 'ADSUserBundle:Roles',
'required' => true,
'property' => 'displayName',
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array('data_class' => 'ADS\UserBundle\Entity\User'));
}
public function getName() { return 'ads_userbundle_user'; }
}
I have this form, the portion I am looking at is the 'roles' portion... Right now it created a multiple select box ( as I expect it to ), though the value is sequentially ie: 0,1,2,3,4...
What I really need is to figure out how to take this entity, and make the property to be the displayName ( as it is now ) and get the value to be the corresponding internalName This way it'll give me an array like:
array('ROLE_EXAMPLE' => 'EXAMPLE', 'ROLE_EXAMPLE1' => 'EXAMPLE1')
Any ideas how to accomplish this is greatly appreciated.

Kamil Adryjanek is correct, it is going to be much easier if you change it from an entity to a choice field. I've done some testing, both with FOSUserBundle and without the bundle - in both cases I hit some interesting road blocks.
First, I tried to run it through QueryBuilder in a repository, that didn't work out as it should have. The reason being, the fact that you wanted to be returning an array instead of a ORM object causes an error.
So next, I started looking at creating the choice field. All the guides, say to use the fieldname role instead of roles so I tried that, but I then had to duplicate the UserInterface from FOSUserBundle - I didn't want to do that -- so here I am stressed, and trying to figure it out.
Here is what I ended up doing, which works well.
private $normalRoles = array();
then in the __construct I add: $this->normalRoles = $roles;
Here is the builder:
$builder
->add('roles', 'choice', array(
'multiple' => true,
'choices' => $this->normalRoles
))
;
Originally, I left the multiple part out, figuring that it'd at least let me see an option box. I ended up getting an Array to String conversion error. So, adding the 'multiple' => true in, fixes that error.
Then, in my repository I created a function called normalizeRoles
public function normalizeRoles() {
$data = array();
$qb = $this->getEntityManager();
$query = $qb->createQuery(
"SELECT r.internalName, r.displayName FROM AcmeUserBundle:Roles r"
)->getArrayResult();
foreach ($query as $k => $v) {
$data[$v['internalName']] = $v['displayName'];
}
return $data;
}
From here, we just have to make some small edits in the DefaultController of the UserBundle in the newAction and editAction ( both are the same changes )
So, first off is to put into your Controller use Acme/UserBundle/Entity/Roles in order to avoid any errors and be able to get that repository.
Next, right before you create the form you run the normalizeRoles() function
$roles = $em->getRepository('AcmeUserBundle:Roles')->normalizeRoles()
Then, you pass it through the construct via: new UserType($roles)
full line for that would look like this:
$form = $this->createForm(new UserType($roles), $entity, array(
'action' => $this->generateUrl('acmd.user.edit', array(
'id' => $id)
)
));
or for new:
$form = $this->createForm(new UserType($roles), $entity, array(
'action' => $this->generateUrl('acmd.user.new')
)
));
At this point -- You'll have a working system that will allow you to dynamically add roles into a database table, and then associate those with a new or current user.

You can try do it via query_builder attribute:
$builder->add('roles', 'entity', array(
'class' => 'ADSUserBundle:Roles',
'required' => true,
'property' => 'displayName',
'query_builder' => function (RolesRepository $queryBuilder) {
return $queryBuilder->someMethod() // some method in this repository that return correct query to db.
},
));

In this case it would be better to use choice field Type (http://symfony.com/doc/current/reference/forms/types/choice.html) instead of entity and pass some role choices as option to form because entity field Type get entity id as key for choices:
public function buildForm(FormBuilderInterface $builder, array $options) {
...
$builder->add('roles', 'choice', array(
'choices' => $options['role_choices']
));
...
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'ADS\UserBundle\Entity\User',
'role_choices' => array()
));
}
Notice: it's recommended to pass variables to form through options parameter, not in constructor.

if I understand your question correctly, you need a data transformers. They help you to show data in form as you want.
Documentation: http://symfony.com/doc/current/cookbook/form/data_transformers.html

Related

Symfony 3: How to use two choices/dropdowns from two tables in one form

Symfony version 3.1.3
I have created a dropdown using the entity called Classes and you can see the Controller below,
public function studentAddClassAction( $id, Request $request )
{
// get the student from the student table
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('PIE10Bundle:Users')->find($id);
// new class object and create the form
$classes= $em->getRepository('PIE10Bundle:Classes')->findAll();
$form = $this->createForm(ClassType::class, $classes);
$form->handleRequest($request);
if( $form->isSubmitted() && $form->isValid() )
{
// form submit operations
}
return $this->render(
'PIE10Bundle:student:layout_student_addclass.html.twig',
array(
'user' => $user,
'title' => 'Add Class',
'tables'=> 1,
'form' => $form->createView()
)
);
}
and the ClassType is below
class ClassType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('classes',
EntityType::class,
array('class' => 'PIE10Bundle:Classes',
'expanded' => false,
'multiple' => false,));
$builder->add('Add Class',
SubmitType::class,
array('attr' => array('class' => 'btn btn-primary',
'style' => 'margin:15px 0;')) );
}
}
And this works fine and it gives all the classes from the database. Also I have another entity called Users and it has a column called roles (DC2Type:array) and it has a role called ROLE_PARENT and I can retrieve all the parents using the following query
$query = $this->getDoctrine()->getEntityManager()
->createQuery('SELECT u FROM PIE10Bundle:Users u WHERE u.roles LIKE :role')
->setParameter('role', '%"ROLE_PARENT"%' );
$users = $query->getResult();
My Question is how to add these parents list as a choice list into same above form in the studentAddClassAction Controller.
Please let me know in any other information is needed for this.
To have a custom set of entities as a choice list you need to use a query_builder option
So it will look like
$builder->add('parent',
EntityType::class,
array('class' => 'PIE10Bundle:Users',
'expanded' => false,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.roles LIKE :role')
->setParameter('role', '%"ROLE_PARENT"%');
},
'multiple' => false
));

Symfony Dynamic Modification of Form without Entity

I am following the Symfony (v2.7) Cookbook recipe for dynamic form modification. What I am aiming for is displaying certain fields based on a user's radio button selection. For example, if a user wishes to filter a search based on records from the last fiscal year, he selects the "Fiscal Year" radio button from the criteriaFilter choice field type (example below), and the appropriate fields are generated. If he changes his mind and selects "Semester" instead, the fiscal year fields are replaced with the semester fields, and so on.
Example code:
$builder
->add('librarian', 'entity', array(
'class' => 'AppBundle:Staff',
'query_builder' => function(EntityRepository $er){
$qb = $er->createQueryBuilder('st');
$qb
->where('st.employmentStatus = :employmentStatus')
->setParameter('employmentStatus', 'faclib')
->orderBy('st.lastName', 'DESC')
->getQuery();
return $qb;
},
'placeholder' => 'All Librarians',
'required' => false
))
->add('program', 'entity', array(
'class' => 'AppBundle:LiaisonSubject',
'query_builder'=>function(EntityRepository $er){
$qb = $er->createQueryBuilder('ls');
$qb
->orderBy('ls.root, ls.lvl, ls.name', 'ASC')
->getQuery();
return $qb;
},
'property' => 'indentedTitle',
'placeholder' => 'All Programs',
'required' => false,
'label' => 'Program'
))
->add('criteriaFilter', 'choice', array(
'expanded' => true,
'multiple' => false,
'choices' => array(
'academic' => 'Academic Year',
'fiscal' => 'Fiscal Year',
'semester' => 'Semester',
'custom' => 'Custom Range'
),
))
;
This seems pretty straighforward based on the cookbook entry. However, the form I am creating is not bound to an entity. Therefore, fetching data via the method
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event){
$form = $event->getForm();
//normally the entity, but NULL in this case
$data = $event->getData();
...
which would normally allow for calling of getter methods on entity properties returns null. So obviously this can't work in this case.
So the question is, is there another way to dynamically generate fields inside of a form that is not tied to an entity?
You can pass options to the form, including data. So something like (from memory but it's untested):
// controller
$this->createForm(SomeForm::class, null, ['fiscalYears' => [2001, 2002]);
// type
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['fiscalyears' => []);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$fiscalYears = $options['fiscalYears'];
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) use ($fiscalYears) {
$form = $event->getForm();
$form->add('fiscalYear', ChoiceType::class, [
'choices' => $fiscalYears
]);
}
}

Symfony2: formbuilder : dynamically modify querybuilder

I am using the formbuilder to create a form as followed:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('content', 'textarea')
->add('rosters', 'entity', array(
'class' => 'PlatformBundle:team',
'property' => 'display',
'multiple' => true,
'expanded' => true,
'required' => true
))
->add('send', 'submit')
;
}
At the moment I get all "teams". I need to adapt the form to display certain teams depending of the request.
I can use the query-builder inside the form builder
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('content', 'textarea')
->add('rosters', 'entity', array(
'class' => 'PlatformBundle:team',
'property' => 'display',
'query_builder' => function(TeamRepository $t) use ($userId) {
return $r->createQueryBuilder('t')
->where('(t.user = :user')
},
'multiple' => true,
'expanded' => true,
'required' => true
))
->add('send', 'submit')
;
}
But the query changes for different questionnaire. In brief: always the same questionnaire but different teams to be listed (Am I making sense?).
Does someone has an idea how dynamically modify the querybuilder inside a formbuilder?
I suggest two possible alternatives.
If the request comes from the form itself (i.e. you already submitted the form with some data and want to refine the fields) you can access the submitted data like this:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$data = $builder->getData();
// now you can access form data
If the request comes from another source, you should use the "options" parameter. First, build a new $option for the requested user:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'user' => null,
));
}
Note: I set the default to null but you can set it to whatever you want.
After that you can pass the $option where you build the form, i.e.
// some controller
$option = array('user' => $request->get('user');
$teamForm = $this->createForm(new TeamType(), null, $options);
// ...
For those looking for an answer...
The best solution I found is create a variable in the formtype and to import it form the controller. My formType will look like that:
class formType extends AbstractType
{
// declare and construct the query in the class to use it in the function
private $qb;
public function __construct ($qb)
{
$this->qb = $qb;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
// declare the variable within the function
$qb = $this->qb;
$builder
->add('content', 'textarea', array('required' => false))
->add('rosters', 'entity', array(
'class' => 'PlatformBundle:Team',
// use ($qb) -> $qb is query built in the controller (or repository)
'query_builder' => function(TeamRepository $r) use ($qb) {
return $qb;
},
'property' => 'display',
'multiple' => true,
'expanded' => true,
'required' => true
))
->add('send', 'submit');
}
In my controller I just pass $qb as an argument of the formtype
$qb = $this->getDoctrine()->getManager()->getRepository('PlatformBundle:Team')->qbteam($Id);
$form = $this->createForm(new formType($qb), $form);
with qbteam a function in the team repository which return the query (not a result).
public function qbteam($Id){
$qb = $this->createQueryBuilder('r')
->leftJoin('r.team', 'm')
->addSelect('m')
->where('m.user = :user')
->setParameter('user', $Id);
return $qb;
}
I hope it will help others.
cheers

UniqueEntity constraint

I want to validate user entity for uniqueness. I do it this way:
$builder->add('email', 'email', array(
'required' => true,
'constraints' => array(
new NotBlank(), new Email(), new UniqueEntity(array('fields' => array('email')))
)
)
)
But I am getting following error:
Warning: get_class() expects parameter 1 to be object, string given in
vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php
line 66
What am I doing wrong?
It's failing because UniqueEntity needs to be applied against the entity and not an individual field. Called a class constraint. Your best bet is to use validation.yml as described in : http://symfony.com/doc/current/reference/constraints/UniqueEntity.html
However, it should be possible to apply it using setDefaultOptions:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'constraints' => array(
new UniqueEntity(array('fields' => array('email'))),
I'm using the following code in my object class in order to handle the unicity of the site's name, maybe you could try it.
#UniqueEntity(
fields={"name"},
errorPath="name",
message="This name is already in use, please chose another one."
)
SF3+ inside your FormType class
public function configureOptions( OptionsResolver $resolver ): void {
parent::configureOptions( $resolver );
$resolver->setDefaults( array(
'constraints' => array( new UniqueEntity( array( 'fields' => array( 'email' ) ) ) )
) );
}
All the credit goes to Cerad.

Symfony2 : Form View - add another field on entity field type

I have the following code in my buildForm method of my FormType
$builder->add('privileges', 'entity', array(
'label' => 'Privileges',
'expanded' => true,
'multiple' => true,
'class' => 'AcmeStoreBundle:AdminPrivilege',
'property'=> 'description',
'query_builder' => function(EntityRepository $er) use ($category)
{
return $er->createQueryBuilder('p')
->where('p.categoryid = :categoryID')
->andWhere('p.parentid = -1')
->setParameter('categoryID', $category->getId())
->orderBy('p.position', 'ASC');
}
));
Here if the parentid is greater than -1, then i'd like to show further form components after the checkbox where parentid is greater than -1 is created.
I've searched over Google and have been unable to find a way to do this, can anybody help?
Mat.
If I understand correctly, you can inject parentid and entity manager to form type construct from controller.
So you can run query before add field to builder, and use if-else. For example:
public function __construct($parentId, $em)
{
$this->parentId = $parentId;
$this->em = $em;
}
public function buildForm(FormBuilder $builder, array $options)
{
$choices = $this->em->getRepository()->callNeededMethod();
if($this->parentId){
$builder->add([someFieldParams]);
}else{
$builder->add([anoutherFieldParams]);
}
}

Resources