Symfony: use of query builder in a Form - symfony

I have a form that I am building using the FormBuilder:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('rosters', 'entity', array(
'class' => 'ReliefAppsPlatformBundle:Roster',
'property' => 'display',
'query_builder' => function(RosterRepository $r) use ($user) {
return $r->createQueryBuilder('r')
->leftJoin('r.members', 'm')
->addSelect('m')
->where('(m.rights = :adminRight or m.rights = :managerRight) and m.user = :user')
->setParameter('adminRight', RosterMember::ADMIN_LEVEL)
->setParameter('managerRight', RosterMember::MANAGER_LEVEL)
->setParameter('user', $user);
},
'required' => true,
'expanded' => true,
'multiple' => true
))
->add('save', 'submit')
;
}
As you can see I my QueryBuilder I use $user (the current user) as a parameter.
My controler looks like that:
public function createAction(Request $request, Event $event)
{
$alert = new RosterEvent;
$alert->setEvent($event);
$user = $this->getUser();
$form = $this->createForm(new RosterEventType(), $alert, $user);
$form->handleRequest($request);
if ($form->isValid()) { ....
My issue is that I need to pass the $user to the formbiulder. But I get "Catchable Fatal Error: Argument 3 passed to Symfony\Bundle\FrameworkBundle\Controller\Controller::createForm() must be of the type array, object given,..."
I know the problem is how I pass $user from the controller to the formbuilder. But I have no clue how to do that.
Any ideas?

As mentioned in the documentation, the third parameter ($options = array()) of method createForm need an array and not a object.
This line is wrong
$form = $this->createForm(new RosterEventType(), $alert, $user);
The $options parameter can be used for example like this
$form = $this->createForm(new TaskType(), $task, array(
'action' => $this->generateUrl('target_route'),
'method' => 'GET',
));
If you want pass a parameter to the form, you can try something like this
Controller
$form = $this->createForm(new RosterEventType($user), $alert);
Form
protected $user;
public function __construct (User $user)
{
$this->user = $user;
}
Hope it will help.

The third parameter of createForm is expecting an array of options so use this when creating the form:
$form = $this->createForm(new RosterEventType(), $alert, array('user'=>$user));
Then you will need to get your RosterEventType to allow the 'user' option to be provided. You can do this by overriding the setDefaultOptions function of your AbstractType and provided a default value for the option. For example you can add the following to your RosterEventType class:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'user' => false,
));
}
If you do this then you can access the user object in your buildForm function using $options['user'].

Related

Set current entity to an other

I work with symfony 2.8 and I have two entities : Cv and FormationCv ,
One Cv can have Many Formation Cv, so I try to do it with relation OneToMany and with collection Type
CvType
class CvForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
//..
->add('formations', CollectionType::class, array(
'entry_type' => FormationCvForm::class,
'allow_add' => true,
'by_reference' => false,
)) ;
}
FormationCvType
class FormationCvForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('titre')
->add('etablissement')
->add('description')
->add('dateDebut', DateType::class, array(
'widget' => 'choice', 'translation_domain' => 'FOSUserBundle','data' => new \DateTime()))
->add('dateFin', DateType::class, array(
'widget' => 'choice',
))
;
}
Controller
public function createAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$cv = new Cv();
$form = $this->createForm('Front\FrontBundle\Form\CvForm', $cv);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user=$this->get('security.context')->getToken()->getUser();
$cv->setEtudiant($user);
$cv->setTelephone($user->getTel());
$cv->setDateNaissance($user->getBirthday());
$cv->setActif(false);
$em = $this->getDoctrine()->getManager();
$em->persist($cv);
$em->flush();
$formations=$cv->getFormations();
$formation= array();
foreach ($formation as $formations) {
$formation->setCv($cv->getId());
$em->persist($formation);
$em->flush();
}
return $this->redirectToRoute('cv_show', array('id' => $cv->getId()));
}
return $this->render("FrontBundle:CV:createCv.html.twig", array(
'form' => $form->createView(),
));
}
The problem that if I submit the form , in the table of FormationCv always get Null ,
Someone help me please ?
Your foreach is not going to run a single iteration:
$formations=$cv->getFormations();
$formation= array();
foreach ($formation as $formations) { // You're looping over $formation here, which is an empty array as per the line before this
$formation->setCv($cv->getId());
$em->persist($formation);
$em->flush();
}
Remove $formation= array(); and switch your variables in the foreach condition: foreach ($formations as $formation) {.
Alternatively/Preferably: Get rid of the whole block and let doctrine do the work by setting up your entities to cascade persist operations and telling it about the inverse-side (mappedBy / inversedBy).

doctrine queryBuilder where IN collection

On my entity I have an array collection of users
/**
* #ORM\ManyToMany(targetEntity="\UserBundle\Entity\User", mappedBy="acr_groups")
*/
protected $users;
public function __construct() {
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
}
In my FormType I want to filter out those groups wherein current user is a member:
$builder
->add('acr_group', EntityType::class, array(
'label' => 'ATS',
'class' => 'HazardlogBundle:ACRGroup',
'query_builder' => function (EntityRepository $er) use ($user) { // 3. use the user variable in the querybilder
$qb = $er->createQueryBuilder('g');
$qb->where(':user IN (g.users)');
$qb->setParameters( array('user' => $user) );
$qb->orderBy('g.name', 'ASC');
return $qb;
},
'choice_label' => 'name'
))
My problem is obviously on this line:
$qb->where(':user IN (g.users)');
How can I use my collection of users as the argument for the IN()?
Try below code
$user = array(12,211,612,84,63,23); // Assuming User Ids whose groups you want to retrive
$builder
->add('acr_group', EntityType::class, array(
'label' => 'ATS',
'class' => 'HazardlogBundle:ACRGroup',
'query_builder' => function (EntityRepository $er) use ($user) {
$qb = $er->createQueryBuilder('g');
$qb->innerJoin('g.users', 'u'); // Inner Join with users
$qb->where('u.id IN (:user)');
$qb->setParameters( array('user' => $user) );
$qb->orderBy('g.name', 'ASC');
return $qb;
},
'choice_label' => 'name'
))
I have tried it in symfony 2.3 with doctrine2. You can use select function with createQueryBuilder() to get specific columns.
$q = $this->createQueryBuilder('v')
->select('v')
->andWhere('v.workingHours IN (:workingHours)')
->setParameter('workingHours', $workingHours);
From : Doctrine 2 WHERE IN clause using a collection of entities
Or according to doctrine documentation : http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/query-builder.html#the-expr-class
To insert IN condition in query builder with doctrine you can use expr()
$qb->add('select', new Expr\Select(array('u')))
->add('from', new Expr\From('User', 'u'))
->add('where', $qb->expr()->orX(
$qb->expr()->eq('u.id', '?1'),
$qb->expr()->like('u.nickname', '?2')
))
->add('orderBy', new Expr\OrderBy('u.name', 'ASC'));
Syntaxe of IN :
$qb->expr()->in('u.id', array(1, 2, 3))
Also, Make sure that you do NOT use something similar to $qb->expr()->in('value', array('stringvalue')) as this will cause Doctrine to throw an Exception. Instead, use $qb->expr()->in('value', array('?1')) and bind your parameter to ?1
I ended up turning thing around a bit after unsuccessfully attempting some of your solutions. I manually created an array of the IDs I wanted.
There is probably a native way of doing this, seems like a pretty standard thing... this works however.
// 1. to inject user entity into this builder first make a construct function (remember to inject it from controller!)
function __construct($user)
{
$this->user = $user;
}
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$user = $this->user; // 2. instantiate the variable we created in our construct above
//create group list array
$groupList = $this->user->getACRGroups();
$gla = array();
foreach ($groupList as $g) {
$gla[] = $g->getId();
};
$builder
->add('acr_group', EntityType::class, array(
'label' => 'ATS',
'class' => 'HazardlogBundle:ACRGroup',
'query_builder' => function (EntityRepository $er) use ($gla) { // 3. use the user variable in the querybilder
$qb = $er->createQueryBuilder('g');
$qb->where('g.id IN (:gla)');
$qb->setParameters( array('gla' => $gla) );
$qb->orderBy('g.name', 'ASC');
return $qb;
},
'choice_label' => 'name'
))

symfony2 + EntityType + queryBuilder and default top value/entry

I have an entity (Category) where the user can choice a parent (always Category) for the new/edit action where the link is the vocabolaryId field.
This is my CategoryType.
class CategoryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$vocId = $options['data']->getVocabularyId();
$builder
->add('name')
->add('vocabularyId',HiddenType::class,[
'data' => $vocId,
])
->add('description')
->add('weight',null,[
'label' => 'Posizione'
])
;
$builder->add('parent',EntityType::class,array(
'class' => 'AppBundle:Category',
'query_builder' => function (EntityRepository $er) use ($vocId) {
return $er->createQueryBuilder('c')
->where('c.vocabularyId = ?1')
->orderBy('c.name')
->setParameter(1,$vocId);
}
));
}
To get the list of all category that have the same vocabularyId I use the "query_builder" parameter.
When I submit the form to symfony without choice a parent (or with an empty table) it replies me with : " This value is not valid." (for the parent field).
How can I set a "null" parent ? I mean a category that have no parent.
EDIT:I have added "'required' => false," like say by Stephan Vierkant, but now I have another error: "Warning: spl_object_hash() expects parameter 1 to be object, string given"
this is my controller's newAction function:
/**
* Creates a new Category entity.
*
* #Route("/new", name="admin_category_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request, $vocId)
{
$category = new Category($vocId);
$form = $this->createForm('AppBundle\Form\CategoryType', $category);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
dump($category);
exit();
$em->persist($category);
$em->flush();
return $this->redirectToRoute('admin_category_show', array('vocId' => $vocId, 'id' => $category->getId()));
}
return $this->render('category/new.html.twig', array(
'category' => $category,
'form' => $form->createView(),
'vocId' => $vocId
));
}
Set required (docs) to false:
$builder->add('parent',EntityType::class,array(
'class' => 'AppBundle:Category',
'required' => false,
'query_builder' => function (EntityRepository $er) use ($vocId) {
return $er->createQueryBuilder('c')
->where('c.vocabularyId = ?1')
->orderBy('c.name')
->setParameter(1,$vocId);
}
));
You can set a placeholder (docs) if you don't want an empty option.

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

passing data from controller to Type symfony2

if i show a field of type "entity" in my form, and i want to filter this entity type based on a argument I pass from the controller, how do i do that.. ?
//PlumeOptionsType.php
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('framePlume', 'entity', array(
'class' => 'DessinPlumeBundle:PhysicalPlume',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('pp')
->where("pp.profile = :profile")
->orderBy('pp.index', 'ASC')
->setParameter('profile', ????)
;
},
));
}
public function getName()
{
return 'plumeOptions';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Dessin\PlumeBundle\Entity\PlumeOptions',
'csrf_protection' => true,
'csrf_field_name' => '_token',
// a unique key to help generate the secret token
'intention' => 'plumeOptions_item',
);
}
}
and inside the controller, i create the form :
i have that argument that i need to pass in my action code:
$profile_id = $this->getRequest()->getSession()->get('profile_id');
...
and then i create my form like this
$form = $this->createForm(new PlumeOptionsType(), $plumeOptions);
the $plumeOptions is just a class to persist. But it has a one-to-one relationship with another class called PhysicalPlume. Now, when i want to display the 'framePlume' in my code, i want to show a filtered PhysicalPlume entity.
You can pass parameters to the form class as follows:
//PlumeOptionsType.php
protected $profile;
public function __construct (Profile $profile)
{
$this->profile = $profile;
}
Then use it in the query_builder of your buildForm:
$profile = $this->profile;
$builder->add('framePlume', 'entity', array(
'class' => 'DessinPlumeBundle:PhysicalPlume',
'query_builder' => function(EntityRepository $er) use ($profile) {
return $er->createQueryBuilder('pp')
->where("pp.profile = :profile")
->orderBy('pp.index', 'ASC')
->setParameter('profile', $profile)
;
},
));
And finally in your controller:
// fetch $profile from DB
$form = $this->createForm(new PlumeOptionsType($profile), $plumeOptions);
You can use $plumeOptions to pass everything your argument, but you'll need to add a getDefaultOptions() in PlumeOptionsType to specify the default value for your option.
See for instance https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php to see what this method should look like.

Resources