Symfony 2 - Form for each instance of entity - php

I have a Timeslot functionality where the user has to be able to edit the start and end of each slot.
So I would like to create one form that contains alle timeslots. I based my code on this article in symfony docs: http://symfony.com/doc/current/cookbook/form/form_collections.html
The problem is that in this article, there's some kind of parent entity to which the collection instances should be assigned to. In my case, the timeslots are standalone and do not have a relation with another entity (that is relevent at this stage).
So.. my question: How do I create a collection of forms with my timeslots?
Controller.php
/**
* #Route("/settings", name="settings")
* #Template()
*/
public function settingsAction()
{
$timeslots = $this->getDoctrine()->getRepository("ScheduleBundle:Timeslot")->findAll();
$timeslots_form = $this->createFormBuilder()
->add('timeslots', 'collection', array(
'type' => new TimeslotType(),
))
->add('save', 'submit')
->getForm();
return array(
'timeslots' => $timeslots_form->createView()
);
}
TimeslotType.php:
class TimeslotType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('start', 'time', array(
'input' => 'datetime',
'widget' => 'choice',
))
->add('end', 'time', array(
'input' => 'datetime',
'widget' => 'choice',
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Oggi\ScheduleBundle\Entity\Timeslot'
));
}
/**
* #return string
*/
public function getName()
{
return 'timeslot';
}
}
Any thoughts?
thanks in advance!

Pass an array instead of an entity to your form. The form can process either just fine.
$timeslots = $this->getDoctrine()->getRepository("ScheduleBundle:Timeslot")->findAll();
$formData = array('timeslots' => $timeslots);
$timeslots_form = $this->createFormBuilder($formData) ...

Related

Symfony - Filter a form with a CollectionType of Entity

I have two entities, client and order.
I have an admin interface where I show all the orders of a client, where I can modify or delete every order.
To do that I use a Collection Type:
My controller:
$form = $this->createForm(ClientConfigType::class, $client);
This is my ClientConfigType :
<?php
namespace App\Form;
use App\Entity\Client;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ClientConfigType extends AbstractMainType {
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add("orders",
CollectionType::class,
[
'entry_type' => OrderConfigType::class,
'allow_add' => true,
'label' => false
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => Client::class,
'allow_extra_fields' => true,
));
}
}
And my OrderConfigType is a classic formType.
Everything is working perfectly without any filtering.
But I want to be able to filter and display my collectionType of Order.
For Exemple I would like to display the order of a specific date or the orders > 100$, etc
I tried to use query builder but it's only working for EntityType and not CollectionType
I tried to pass a variable from my Controller to my Form then to my Entity "get" function like that:
$minimumPrice = $request->query->get('minimumPrice');
$form = $this->createForm(ClientConfigType::class, $client, ['minimumPrice' => $minimumPrice ]);
Then in my ConfigType I can retrieve my variable in the configureOptions function but Then, I can't do anything to use that value to filter my collection Type.
How can I filter my collectionType ?
Instead of passing minutePrice you can query like you want your orders, and pass order's collection to the form.
Example here:
class ClientConfigType extends AbstractMainType {
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add("orders",
CollectionType::class,
[
'entry_type' => OrderConfigType::class,
'allow_add' => true,
'label' => false,
'data' => $options['orderCollection']
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => Client::class,
'orderCollection' => null,
));
}
}
$orderCollection = $em->getRepository(Order::class)->findAll(); //something like this or custom query it s an example
$form = $this->createForm(ClientConfigType::class, $client, ['orderCollection' => $orderCollection ]);

Symfony FormType construct error

My application show this error
Type error: Too few arguments to function AppBundle\Form\ActualiteType::__construct(), 0 passed in /Applications/MAMP/htdocs/SyndicNous/vendor/symfony/symfony/src/Symfony/Component/Form/FormRegistry.php on line 90 and exactly 2 expected
My formType
class ActualiteType extends AbstractType
{
/**
* #var bool $admin
*/
private $admin;
/**
* #var User $user
*/
private $user;
/**
* ActualiteType constructor.
* #param bool|false $admin
*/
public function __construct($admin = false, $user)
{
$this->admin = $admin;
$this->user = $user;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$categories = array(
'Travaux' => 'Travaux',
'Voisinage' => 'Voisinage',
);
$builder
->add('title')
->add('category')
->add('content')
->add('datePublish')
->add('category', ChoiceType::class, array(
'choices' => $categories
)
);
if ($this->user->getResidence() != null) {
$builder->add('residence', EntityType::class, array(
'class' => 'AppBundle:Residence',
'choices' => $this->user->getResidence(),
));
} else {
$builder->add('residence', 'entity', array(
'class' => 'AppBundle:Residence',
'choice_label' => 'name'
));
};
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Actualite'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_actualite';
}
}
Do you have any idea where the problem would come from? Thank you
I do not understand what you're trying to do. You do not need to use the constructor to pass parameters to your formType. There is the second parameter of the buildForm method for this ($options).
In your controller, create your form like this :
$form = $this->createForm(ActualiteType::class, $actualite, [
'admin' => $admin,
'user' => $user
]);
And modify your formType like that :
class ActualiteType extends AbstractType {
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$admin = $options['admin']; // Not used ?
$user = $options['user'];
$categories = array(
'Travaux' => 'Travaux',
'Voisinage' => 'Voisinage',
);
$builder->add('title')
->add('category')
->add('content')
->add('datePublish')
->add('category', ChoiceType::class, array(
'choices' => $categories
)
);
if ($user->getResidence() != null) {
$builder->add('residence', EntityType::class, array(
'class' => 'AppBundle:Residence',
'choices' => $user->getResidence(),
));
} else {
$builder->add('residence', 'entity', array(
'class' => 'AppBundle:Residence',
'choice_label' => 'name'
));
};
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Actualite',
'admin' => null, // Default
'user' => null // Default
));
}
}
Do not forget to put the default values of your options in configureOptions method.

Symfony CollectionType self referencing

I have many to many association in my entity and i would like to use the collection type field from symfony to add multiple times.
I've never done this way before and i'm kinda lost.
The field that i would like to have multiple times is headquarter with a non mapped field for each headquarter.
The error that i'm getting;
The property "headquarter" in class "AppBundle\Entity\SurveyManager"
can be defined with the methods "addHeadquarter()",
"removeHeadquarter()" but the new value must be an array or an
instance of \Traversable, "AppBundle\Entity\HeadQuarterManager" given.
The Form Type implementing the Collection.
class SurveyOptionType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('headquarter', CollectionType::class, [
'entry_type' => HeadQuarterType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'label' => 'Sediu',
])
->add('isEnabled', CheckboxType::class, [
'label' => 'Chestionar Activ',
'required' => false,
])
->add('submit', SubmitType::class, [
'label' => 'Salveaza',
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SurveyManager::class
]);
}
}
This is the Collection Form
class HeadQuarterType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('headquarter', EntityType::class, [
'class' => HeadQuarterManager::class,
'label' => 'Sediu',
])
->add('userNumber', TextType::class, [
'label' => 'Numar Utilizatori',
'mapped' => false,
])
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SurveyManager::class
]);
}
}
UPDATE:
Here is the Entity class stripped away from all the uncesessary data
class SurveyManager
{
/**
* #var int
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\HeadQuarterManager")
* #ORM\JoinTable(name="survey_headquarters",
* joinColumns={#ORM\JoinColumn(name="survey_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="headquarter_id", referencedColumnName="id")}
* )
*/
private $headquarter;
public function __toString()
{
// TODO: Implement __toString() method.
return $this->name;
}
/**
* Constructor
*/
public function __construct()
{
$this->question = new \Doctrine\Common\Collections\ArrayCollection();
$this->headquarter = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add headquarter
*
* #param \AppBundle\Entity\HeadQuarterManager $headquarter
*
* #return SurveyManager
*/
public function addHeadquarter(\AppBundle\Entity\HeadQuarterManager $headquarter)
{
$this->headquarter[] = $headquarter;
return $this;
}
/**
* Remove headquarter
*
* #param \AppBundle\Entity\HeadQuarterManager $headquarter
*/
public function removeHeadquarter(\AppBundle\Entity\HeadQuarterManager $headquarter)
{
$this->headquarter->removeElement($headquarter);
}
/**
* Get headquarter
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getHeadquarter()
{
return $this->headquarter;
}
}
Controller method
$em = $this->getDoctrine()->getManager();
$surveyRepository = $em->getRepository(SurveyManager::class);
//$surveyManager = $surveyRepository->findOneById($surveyId);
$surveyManager = new SurveyManager();
$form = $this->createForm(SurveyOptionType::class, $surveyManager);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($surveyManager);
$em->flush();
return $this->redirectToRoute('admin_survey-manager_survey_options',
[
'id' => $surveyManager->getId()
]);
}
return [
'surveyManager' => $surveyManager,
'form' => $form->createView(),
];
While doing research about this i did a separate demo to test on it to see better the problem.
Here is an updated code simplified that should work really smooth
https://gist.github.com/bogdaniel/a0bcc848e2bd282382f45a2bd15cc0e2
You will find more info about the error in the gist.
I don't really understand what do you want to archive, and what exactly does it mean:
The field that i would like to have multiple times is headquarter with a non mapped field for each headquarter.
But the error is quite self-explaining. Your SurveyManager class has to have some methods to populate data from Form. The flow is following - Form gets data (from User, for example) with an array of headquarters. And then Form looks for the way to pass this array to the object - and doesn't find them because you don't have appropriate methods.
Id doesn't matter if the property is mapped (I believe, you mean mapped to Doctrine), it still has be provided a way to pass data from Form to Object.
How are you rendering your form? I suspect something similar to the issue found here: Error : the new value must be an array or an instance of \Traversable, Entity class given
Other than that, the only issue I can see in your code is that the 'data_class' option from HeadQuarterType should be HeadQuarterManager::class.

Symfony 2 - Differents fields for search form than list

I have three entity for a project : Book, Author and Theme. For information, Author and Theme are linked with Book in Many to One/One to Many. A book has one or more author and one or more themes.
And I have generated crud about theses for basics functions (add, edit and delete) and default views/form.
But I need to do some functions search, three exactly:
Search Book with part of the Title
Search Book with one Author
Search Book with one or more Theme.
I coded the three functions and working good but I have a problem about the fields used in search forms.
The first one is correct, it show a input field where we put the title and the book list as result is correct but the two others …
Because of the relationship the field are drop list where the user choose a item and search it, the result is correct but I wish for the second to have an input field for search the author name and the last to display checkbox so I’ve made research but I can’t understand why in my forms type, I can’t change the kind of field I want.
Here my two Forms Type :
Search with one Author : I just want to display an input field and not a list but get error each time
<?php namespace Projet\BibliothequeBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class BookByAuthorType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('authors')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Projet\BibliothequeBundle\Entity\Book'
));
}
/**
* #return string
*/
public function getName()
{
return 'authors';
}
}
Search Book with one or more Theme : I tried to display chechbox by link the 'themes' array to result of checkbox but don't work too.
<?php namespace Projet\BibliothequeBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class BookByThemeType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
// $themes = array();
// $themes=$builder->add('themes');
$builder
->add('themes', 'choice', [
'choices' => 'themes',
'multiple' => true,
'expanded' => true
])
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Projet\BibliothequeBundle\Entity\Book'
));
}
/**
* #return string
*/
public function getName()
{
return 'themes';
}
}
UPDATE : My BookController functions maybe it's the origin of my problems.
// AUTHOR SEARCH FUNCTIONS
public function SearchByAuthorAction() {
$entity = new Book();
$form = $this->CreateSearchAuthorForm($entity);
return $this->render('ProjetBibliothequeBundle:Book:searchbyauthor.html.twig', array(
'form' => $form->createView(),
));
}
private function CreateSearchAuthorForm(Book $entity) {
$form = $this->createForm(new BookByAuthorType(), $entity, array(
'action' => $this->generateUrl('book_submit_search_by_author'),
'method' => 'POST',
));
$form->add('submit', 'submit', array('label' => 'Search'));
return $form;
}
public function SubmitSearchByAuthorAction(Request $request) {
$entity = new Book();
$form = $this->CreateSearchAuthorForm($entity);
$form->handleRequest($request);
$author = $form->get('authors')->getData();
$repository = $this->getDoctrine()
->getEntityManager()
->getRepository('ProjetBibliothequeBundle:Book');
$Booklist = $repository->findByAuthor($author);
return $this->render('ProjetBibliothequeBundle:Book:resultbyauthor.html.twig',
array('Booklist' => $Booklist));
}
//THEME SEARCH FUNCTIONS
public function SearchByThemeAction() {
$entity = new Book();
$form = $this->CreateSearchThemeForm($entity);
return $this->render('ProjetBibliothequeBundle:Book:searchbytheme.html.twig', array(
'form' => $form->createView(),
));
}
private function CreateSearchThemeForm(Livre $entity) {
$entities = $this->getDoctrine()->getManager()->getRepository('ProjetBibliothequeBundle:Book')->findAll();
$form = $this->createForm(new BookByThemeType(), $entity, array(
'action' => $this->generateUrl('book_submit_search_by_theme'),
'method' => 'POST',
));
$form->add('submit', 'submit', array('label' => 'Search'));
return $form;
}
public function SubmitSearchByThemeAction(Request $request) {
$entity = new Book();
$form = $this->CreateSearchThemeForm($entity);
$form->handleRequest($request);
$theme = $form->get('themes')->getData();
$repository = $this->getDoctrine()
->getEntityManager()
->getRepository('ProjetBibliothequeBundle:Book');
$Booklist = $repository->findByTheme($theme);
return $this->render('ProjetBibliothequeBundle:Book:resultbytheme.html.twig',
array('Booklist' => $Booklist));
}
There is no need to set data_class for GET operations. Just create a basic form, may be like this:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('themes', 'choice', [
'choices' => 'themes',
'multiple' => true,
'expanded' => true
])
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(['method' => 'GET', 'csrf_protection' => false]);
}
in your action catch values from URL, pass them to findBy method or manually build you query:
public function someAction(Request $request)
{
...
$form->createForm(new FORM_TYPE, null);
....
$filters = $request->query->get($form->getName());
$result = $entityManager->getRepository(REPOSITORY)->findBy($filters, $orderBy, $limit, $offset);
...
}

Symfony2 pass option to abstract FormType

I've been stuck on this for quite some time now. I read quite some answers here on SO but I couldn't get it running.
My controller contains
$series = $this->getDoctrine()->getRepository('DemoDemoBundle:Series')->createQueryBuilder('series')
->where('series.type = 1')
->getQuery()->getResult();
$magazine = new Magazine();
$content = array(
'series' => $series
);
$form = $this->createForm(new MagazineType($this->get('translator'), $content), $magazine);
$form->handleRequest($request);
I would like to pass the variable $content to the form so that 'series' can be used in some selectboxes. MagazineType extends PublicationType. The same goes for the underlying entities. Here's how MagazineType looks:
class MagazineType extends PublicationType
{
function __construct($translator, array $series = null)
{
parent::__construct($translator, $series); //$series);
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
// TODO: Refactor constant out
parent::buildForm($builder, $options)
// ->add('dateSubmitted')
// ->add('userSubmitted')
// ->add('file')
->add('issueNumber')
->add('save', 'submit');
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'Demo\DemoBundle\Entity\Magazine',
'series' => $this->translator->trans('addPublication.noSeries')
)
);
}
and PublicationType
class PublicationType extends AbstractType
{
protected $series, $translator;
public function __construct($translator, $series)
{
$this->translator = $translator;
$this->series = $series;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options){
$builder
->add('name', 'text', array(
'label' => $this->translator->trans('addPublication.name')
))
// ->add('dateSubmitted')
// ->add('userSubmitted')
// ->add('file')
->add('series', 'choice', $this->series);//$formOptions['series']);
;
return $builder;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Demo\DemoBundle\Entity\Publication',
'series' => $this->translator->trans('addPublication.noSeries')
));
}
Maybe I'm accessing the series variable the wrong way, but I'm not even getting any further than constructing MagazineType (option series not allowed) even though I've set it in DefaultOptions as stated on http://symfony.com/doc/current/components/options_resolver.html#configure-allowed-types. I'm probably missing something here though similar questions, since I'm stuck on this the entire day.
Thx for any help!

Categories