How to manage only one oneToMany entity in parent form - forms

I have an Entity Criteria which can have several Entity Vote (associated).
I want to DISPLAY a list of criteria and to show only ONE Vote form in front of each Criteria (not zero, not many), to allow user to vote for each Criteria.
But, as I have a oneToMany association, in my CriteriaForm, I must have a CollectionType of Vote (I think ?). I tried to have only a VoteType :
$builder->add('votesCritere', VoteCritereType::class);
but I have an error :
The form's view data is expected to be an instance of class LabelBundle\Entity\VoteCritere, but is an instance of class Doctrine\ORM\PersistentCollection
So I think I must have a CollectionType. But, if I have a collection type, I start with ZERO form (because My Criteria does not contain any Vote yet).
I want to have only (and always) one Vote form in front of each criteria, to add a vote to the criteria (I don't want the number of forms reflects the number of votes)
How can I do that ?
Thanks for your help !

Let's think out loudly - in case you use entity bind form with collection type for votes, it'll allways show all of yours votes that would be bind to each criteria.
In this case I would recommend the array of forms based on criteria entity IDs.
Create Vote form type:
class VoteType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('answer', ChoiceType::class, array(
'choices' => array(
'Yes' => true,
'No' => false,
)
))
->add('note', TextareaType::class),
->add('criteriaId', HiddenType::class),
->add('vote', SubmitType::class);
...
...
}
In the controller - get traversable of criterias (e.g. method findAll() method of repository) and create array of named forms based on criteria entity IDs:
$ff = $this->get('form.factory');
foreach ($criterias as $criteria) {
$voteForms[] = $ff->createNamedBuilder(
'vote_'.$criteria->getId(),
new VoteType,
array(
'criteriaId' => $criteria->getId()
)
)
->getForm()
->createView();
}
return $this->render(someView, array("voteForms" => $forms));
And when the form is submitted then you can identify to which criteria the vote belongs by the hidden criteriaId field.
EDIT - Added: How to submit all forms with one button
'criteriaId' field and 'vote' submit in the VoteType is no longer needed - so:
class VoteType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('answer', ChoiceType::class, array(
'choices' => array(
'Yes' => true,
'No' => false,
)
))
->add('note', TextareaType::class);
...
...
}
And in the controller just create form with VoteTypes members:
$fb = $this->createFormBuilder();
foreach ($criterias as $criteria) {
$fb->add('vote-criteria-'.$criteria->getId(), VoteType::class);
}
$fb->add('submit', SubmitType::class);
$frm = $fb->getForm();
$frm->handleRequest($request);
if ($frm->isSubmitted() && $frm->isValid()) {
// all criteria vote data
$frm->getData();
...
}
return $this->render(someView, array('form' => $frm->createView()));

Related

How can I relation a foreign key with a form in symfony?

Hi I'm trying to make a relation between two entities, a user creates proyectes, but in the create a project form I don't know how do it.
If I do this:
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('Nombre')
->add('Resumen')
->add('Creados')
;
}
I have the following error:
What can i do to make the form that make a relation and show the user how creates the proyect? It's my first time using stakoverflow, thank you!
you could use the EntityType for a to-one relationship like:
$builder->add('users', EntityType::class, [
'class' => User::class,
// uses the User.username property as the visible option string
'choice_label' => 'username',
]);
Have a look at the documentation, too. This will help you understand other form field types as well.
For a to-many relationship you can use Collection which is a bit more complicated.

How to create dynamic cascade form in Symfony?

I have been using Symfony 3 for a while, and until now I only generated simple forms. Now, I would like to generate a more complex, dynamic form and a bit of help would be appreciated.
My website references Products, that are associated to categories. Therefore, I created a ProductCategory entity which relies on the Doctrine extension Tree. The actual products are therefore the leaves of the tree structure, the other nodes (i.e. those with at least one child) are only product categories.
An example of such structure would be this one:
Food
Fruits
Apple
Pear
Vegetables
Pepper
Zucchini
Vehicle
Car
Bike
In this example, the products are Apple, Pear, Pepper, Zucchini, Car and Bike.
I would like to generate a form aimed at selecting a product. The expected behavior would be this one:
Initially, the user sees a single dropdown list with the root categories (here Food and Vehicle)
When the user selects a category, a new dropdown appears below the previous one, populated with the subcategories corresponding to the previously selected category (e.g. Fruits and Vegetables if Food had been chosen)
This process should be recusrive until the user selected a product, i.e. a leaf of the tree structure.
In order for the form to be valid, the user must have selected a Product (leaf).
For now, I have been able to generate the initial form with the dropdown listing the root categories. Here is the code used to do this.
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use AppBundle\Entity\ProductCategory;
use AppBundle\Repository\ProductCategoryRepository;
 
class ProductType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
            $builder->add('productCategory',EntityType::class,array(
                'class'         => 'AppBundle:ProductCategory',
                'query_builder' => function(ProductCategoryRepository $repository){
                        return $repository->getRootNodesQueryBuilder();
                },
                'label'         => 'product category',
                'choice_label'  => 'name',
                'choice_value'  => 'name',
                'multiple'      => false,
                'expanded'      => false,
                'required'      => true
            ));
    }
 
 
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => Product::class,
            'method' => 'POST',
        ));
    }
}
From this, the idea would be to use AJAX and jQuery to:
Send the (current) selected category to the server, using the "change" event on the dropdown
Generate and display the new dropdown below the previous one, with the adequate
However, I do not know how to modify the ProductType class in order to generate the new EntityType field based on the current selected category, then to send back the updated form in order to allow its display by jQuery.
Therefore, any help or advice would be more than welcome in order to make progress in building this dynamic form !
If I understand your problem correctly then you need to get child categories for given categories. You can pass it as a option to ProductType
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use AppBundle\Entity\ProductCategory;
use AppBundle\Repository\ProductCategoryRepository;
 
class ProductType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
$category = $options['category'];
            $builder->add('productCategory',EntityType::class,array(
                'class'         => 'AppBundle:ProductCategory',
                'query_builder' => function(ProductCategoryRepository $repository) use ($category){
                        return $repository->getRootNodesQueryBuilder($category);
                },
                'label'         => 'product category',
                'choice_label'  => 'name',
                'choice_value'  => 'name',
                'multiple'      => false,
                'expanded'      => false,
                'required'      => true
            ));
    }
 
 
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => Product::class,
            'method' => 'POST',
'category' => null
        ));
    }
}
In getRootNodesQueryBuilder() you can parse child categories based upon passed category. In controller (where you receive AJAX request), you can use it like
$form = $this->createForm(ProductType::class, $product, [
'category' => $request->get('category')
]);

Symfony entity field type with custom unselectable separators

Lets say I have an entity City which has cityName also has priority 1, 2 or 3. I need to create a select box with the cities ordered by priority (ascending) and also add a separator before priority changes. the list should look something like that:
---Big cities:
bigCity1
bigCity2
bigCity3
---Smaller cities:
smallerCity1
smallerCity2
smallerCity3
smallerCity4
---Smallest cities:
smallestCity1
smallestCity2
....
those separators starting with --- shouldn't be selectable.
currently my formType looks like that:
class SelectCityFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('city', 'entity', array(
'class' => 'MainBundle:City',
'property' => 'city',
))
->add('Select', 'submit');
}
public function getName()
{
return 'selectCityForm';
}
}
How do I achieve what I want?
You could use optgroup tag to group cities by specified field. Just add 'group_by' => 'field_name' option to the city field.

Creating a grid of forms in Symfony 2

I'm working on a page on which I would like to render an entity's instance as phpMyAdmin would do for instance.
More specifically, I would like to get a table in which columns are fields of the entity and rows are all the instances of this entity, and make all values (except id) editable, and save them to database.
My problem is I don't really know what is a good practice to do that and what is possible with Symfony (I'm quite new with this framework).
My first idea was to create a single form but it seems difficult to
combine several instances in a single form.
I also considered to create a form per row, but this means every line would have its own "submit" field, and it would be impossible to save changes in more than one row at once.
In Symfony's doc I've seen a topic about form collections but I don't know if this is something I can use, since the instances of my entity are completely independant.
Well, I think I'm not the first wanting to do that, but I was unable to find any way to do it, maybe am I missing something ?
Any advice is welcomed, thanks !
Doing "à là symfony" you could create a Base Form, for example AllRowsType that has a field of Type collection, and each one of the rows is of Type RowType:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('rows', 'collection', array(
'type' => new RowType(),
'allow_add' => false,
'allow_delete' => false,
'by_reference' => false
));
}
Then your RowType will be a normal form for your entity.
class RowType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\Bundle\DemoBundle\Entity\Row',
));
} }
The validations can be on your RowType like a normal form, but if you have problems, you can set cascade_validation => true on the default options of your AllRowsType.
To iterate each RowType in twig you can do:
{% for row in form.rows%} {{ form_row(row.name) }} {% endfor %}
On twig in order to get the ID of each row entity you can do:
{{ row.vars.value.id }}
On Controller, you can do $allrowsForm->get('rows')->getData() and you have an ArrayCollection of Row Entities and do whatever you want.
You can read http://symfony.com/doc/current/cookbook/form/form_collections.html for how to handle a collection of forms.

How to Force a Collection to be a Single Drop down menu in a form

I would to create a single drop dwon menu for my user roles collection like below :
class UserType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('userRoles',null, array('multiple'=>false))
;
}
But I get this error message:
Expected an object, but got a collection. Did you forget to pass
"multiple=true" to an entity field?
Any Idea how to make a Collection not multiple selectable !?
The field type that you are using ("entity") expects a simple object as input if you set "multiple" to false. For your purpose, you might want to use the CallbackTransformer to transform between the collection and the object:
$builder->add(
$builder->create('userRoles', null, array('multiple' => false))
->addModelTransformer(new CallbackTransformer(
// transform the collection to its first element
function (Collection $coll) { return $coll[0]; },
// transform the element to a collection
function (MyEntity $entity) { return new ArrayCollection(array($entity)); }
))
);
If you don't use latest Symfony2 master, you should use the deprecated method prependNormTransformer() instead of addModelTransformer().