I have three entity. Profile, Car and Trip. When user(Profile) create Trip, he can chose the Car(only his own) and assign it to the Trip. I know the field must be the entity type. But I dont know how I can set to chose list a cars of current user(Profile). Any ideas? Thanks.
Filter the cars by user, I think this example is what you need:
$builder->add('car', 'entity', array(
'class' => '/path/to/entity/Car',
'property' => 'title',
'empty_value' => 'Choose a car',
'query_builder' => function(EntityRepository $em) use ($userId) {
return $em->createQueryBuilder('c')
->join('c.user', 'u')
->where('u.id = :userId')
->setParameter('userId', $userId);
}
)
)
You can add $userId as one form option:
$form = $this->createForm(new MyFormType(), $object, array( 'userId' => $userId ));
And inside your form retrieve it:
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'userId' => function (Options $options, $value) {
return $options['userId'];
}
));
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
if($options['userId']){
$userId = $options['userId'];
}
}
How I mentioned. other solution, even personally I don't prefer it:
$form = $this->createForm(new MyFormType($userId), $object);
And in your form, store it in a protected variable to be used later in your query:
/**
* Class MyFormType
*/
class MyFormType extends AbstractType
{
protected $userId;
/**
* #param $userId
*/
public function __construct($userId) {
$this->userId = $userId;
}
}
Take a look at this Symfony cookbook entry:
http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#how-to-dynamically-generate-forms-based-on-user-data
Related
I have a problem with a nested form. I can't get the values I want to pass.
Here is a simple example to reproduce my problem, I would like to pre-fill a form about a user according to the selected house in my form.
Here are the files, if you want to test. I would like to inject the values of roger and billy the good way but my user fields are always empty
The models
class Test
{
/**
* #var string|null
*/
private $house;
/**
* #var TestUser|null
*/
private $user;
// Getters & Setters of course...
}
class TestUser
{
/**
* #var string|null
*/
private $name;
/**
* #var int|null
*/
private $age;
// Getters & Setters again...
}
The main form
class TestType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('house', ChoiceType::class, [
'choices' => [
'first' => 1,
'second' => 2,
],
]
);
$builder->get('house')->addEventListener(FormEvents::POST_SUBMIT, [$this, 'addUser']);
}
function addUser(FormEvent $event)
{
$form = $event->getForm()->getParent();
$house = $event->getForm()->getData();
if (!$house) {
return;
}
// here is the part where I choose the user I want to use
// for the rest of the example (which does not work)
$testUser = $house === 1
? (new TestUser())->setName('roger')->setAge(65)
: (new TestUser())->setName('billy')->setAge(22);
$builder = $form->getConfig()->getFormFactory()->createNamedBuilder('user', TestUserType::class, $testUser, [
'auto_initialize' => false,
]);
$form->add($builder->getForm());
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Test::class,
]);
}
}
The user form type
class TestUserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, ['label' => 'username'])
->add('age', IntegerType::class, ['label' => 'age']);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => TestUser::class,
]);
}
}
The controller
public function testForm(Request $request): RedirectResponse|Response
{
// ...
$test = new Test();
$form = $this->createForm(TestType::class, $test);
$form->handleRequest($request);
// ...
}
The view
{{ form_start(formtest) }}
<p>
<button class="btn btn-primary" type="submit">test go</button>
</p>
{{ form_end(formtest) }}
all help is welcome
Is setter actually returning $this?
When ->setAge(65) is executed.
Because it's not clear, it's not in your code you provided here.
you need to link the $user $age $house and $name to the input fields you have.
The reason why you always get an empty output is do to the face that non of the variables refer to any data source.
I am working on project with symfony 3 and doctrine .
I have a many to many relationship between Pack and Produit :
Pack Entity :
class Pack
{
/**
* #var ArrayCollection | Produit[]
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Produit", inversedBy="packs")
* #ORM\JoinTable(name="link_pack")
*/
private $produits;
public function __construct()
{
$this->produits = new ArrayCollection();
}
/**
* #return Produit[]|ArrayCollection
*/
public function getProduits()
{
return $this->produits;
}
public function addProduit(Produit $produit)
{
if ($this->produits->contains($produit)) {
return;
}
$this->produits[] = $produit;
}
public function removeProduit(Produit $produit)
{
if (! $this->produits->contains($produit)) {
return;
}
return $this->produits->removeElement($produit);
}
}
Produit Entity :
class Produit
{
/**
* #var ArrayCollection | Pack[]
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Pack", mappedBy="produits")
*/
private $packs;
public function __construct()
{
$this->packs = new ArrayCollection();
}
/**
* #return Pack[]|ArrayCollection
*/
public function getPacks()
{
return $this->packs;
}
public function addPack(Pack $pack)
{
if ($this->packs->contains($pack)) {
return;
}
$pack->addProduit($this);
$this->packs[] = $pack;
}
}
I want to assign products to a pack , so I have a form that contains products in a select field. (the pack and products are created before).
The form type :
class PackAffectProduitType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('produits', EntityType::class, array(
'class' => Produit::class,
'choice_label' => 'libelle',
'multiple' => true,
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Pack::class,
));
}
}
The controller :
public function affectProduitsAction(Pack $pack, Request $request)
{
$form = $this->createForm(PackAffectProduitType::class, $pack);
$form->handleRequest($request);
dump($pack);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
dump($pack);
$em->flush();
// ...
}
//...
}
The problem is when I select the products and submit, It assigns the selected products to the pack but the products assigned before are deleted. I want them to still assigned, So how to solve this problem ?
If you want your view to add OR remove values, if you manually generate your field view structure, you need not to forget to fill already set values (e.g. the products previously added to the Pack).
For example by adding the selected attribute to the choices of your select. Doing so will pre-fill your field with current values therefore removing at submit only those which have been deliberately unselected.
If you don't want your view to display nor know anything about already set Produit entities on this pack and just blindly perform adds, you can add mapped => false to your field options and handle manually the addition of your products to your pack in your controller.
This will only allow you to add though, since it does not have knowledge of already set values, and you would have to make another case/action to perform deletion. Above way is better in my opinion.
In this last case, your controller would look like :
public function affectProduitsAction(Pack $pack, Request $request)
{
$form = $this->createForm(PackAffectProduitType::class, $pack);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
if(is_array($form->get('produits')->getData()){
foreach($form->get('produits')->getData() as $produit){
$pack->addProduit($produit);
}
}
$em->flush();
// ...
}
//...
}
And your FormType :
class PackAffectProduitType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('produits', EntityType::class, array(
'class' => Produit::class,
'mapped' => false,
'choice_label' => 'libelle',
'multiple' => true,
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Pack::class,
));
}
}
Try to add 'by_reference' => false option to your form, it will force to use setters and getters from your entity
I am trying to validate a form in Symfony 2.3. I need to get the current User id and submit in my database.
My controller :
public function giftcardAction(Request $request)
{
$giftcard = new Giftcard();
$giftcard->setDate(new \DateTime());
$user = $this->container->get('security.context')->getToken()->getUser();
$userId = $user->getId();
var_dump($userId);
$form = $this->createFormBuilder($giftcard)
->add('amount', 'integer')
->add('onBehalfOf')
->add('towards')
->add('message', 'text')
// ->add($userId)
->add('submit', 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($giftcard);
$em->flush();
}else{
throw new NotFoundHttpException("Page not found");
}
}
return $this->render('FrontBundle:Forms:giftcard.html.twig', array(
'form' => $form->createView(),
));
}
My FormType
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class GiftCardType extends AbstractType
{
private $userId;
public function __construct(array $userId)
{
$this->userId = $userId;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('date')
->add('onBehalfOf')
->add('towards')
->add('message')
// ->add('UserId')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Front\Bundle\Entity\GiftCard',
));
}
/**
* #return string
*/
public function getName()
{
return 'front_bundle_giftcard';
}
}
At the moment i can get the user id in var_dump in the controller. But how I can pass the variable in the form. Has anyone may help me. Thank you.
I have a question for you and to give you some idea of what i'm doing, i will try and explain the idea. I have a system where user's are able to add geckos to a database, when that gecko is added, it saves the user id into a column called user_id - this works perfect and sets me up for what i am trying to achieve now.
I have a system where user's are able to add weight entries for that gecko, problem is, right now it just loads every gecko in the database, not the one's that are specific to that user.
Here is a portion from my Weight.php entity:
/**
* #ORM\ManyToOne(targetEntity="Gecko", inversedBy="weights")
* #ORM\JoinColumn(name="gecko_id", referencedColumnName="id")
*/
private $geckoId;
Which is linked to this part in the Gecko.php:
/**
* #ORM\OneToMany(targetEntity="Weight", mappedBy="geckoId", cascade={"persist", "remove"})
*/
private $weights;
And here is the user part inside Gecko.php entity which links the current user's id to save to the database:
/**
* #ORM\ManyToOne(targetEntity="Breedr\UserBundle\Entity\User", inversedBy="geckos")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
And the linked part in the User.php entity:
/**
* #ORM\OneToMany(targetEntity="Breedr\GeckoBundle\Entity\Gecko", mappedBy="user", cascade={"persist", "remove"})
*/
protected $geckos;
Now, here is my Weight entities Form (WeightType.php):
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('weighedDate')
->add('weight')
->add('geckoId')
;
}
Which gives you a drop down based on the parts above that look like this:
So what i am now trying to achieve is to ONLY show the geckos that are linked with the current user's ID. What is the best way to achieve this?
Thanks in advance :)
Andy
EDIT:
Here is my WeightType file:
<?php
namespace Breedr\GeckoBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class WeightType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('weighedDate')
->add('weight')
->add('geckoId')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Breedr\GeckoBundle\Entity\Weight'
));
}
/**
* #return string
*/
public function getName()
{
return 'breedr_geckobundle_weight';
}
}
EDIT 2:
Here is my create form snippet:
private function createCreateForm(Weight $entity)
{
$form = $this->createForm(new WeightType(), $entity, array(
'action' => $this->generateUrl('weight_create'),
'method' => 'POST',
));
$form->add('submit', 'submit', array('label' => 'Create'));
return $form;
}
You must use entity Field Type + query_build option. Thus You can build a custom query in order to filter the results, for instance:
<?php
namespace AppBundle\Form\Type;
use Doctrine\ORM\EntityRepository;
// ...
$builder->add('users', 'entity', array(
'class' => 'AcmeHelloBundle:User',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->orderBy('u.username', 'ASC');
},
));
On your specific case your form type might looks like something like this:
<?php
# maybe you need to fix some namespaces...
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class WeightType extends AbstractType
{
/** #var int */
protected $currentUserId;
/**
* param int $currentUserId It can be passed from controller
* when creating form instance
*/
public function __construct($currentUserId)
{
$this->currentUserId = $currentUserId;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$id = $this->currentUserId;
$builder->add('users', 'entity', array(
'class' => 'GeckoBundle:Gecko',
'query_builder' => function (EntityRepository $er) use ($id) {
return $er->createQueryBuilder('g')
->where('user_id = ?')
->setParameter(0, $id);
},
));
}
}
On the controller...
<?php
//...
public function add()
{
$currentUserId = $this->getUser()->getId(); # may be it...
$form = $this->createForm(new WeigthType($currentUserId));
}
Symfony 2.3
I'm embedding some Forms to be able to change each user property related to permissions. I've created an UserAdminType which is displayed for each user in the same page:
<?php
namespace Msalsas\UserAdminBundle\Form;
use Msalsas\UserBundle\Entity\User;
use Msalsas\UserBundle\Entity\ExtendedUser;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
class UserAdminType extends AbstractType
{
private $user;
public function __construct(User $user)
{
$this->user = $user;
$this->extendedUser = new ExtendedUser($this->user);
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$extendedUser = $this->extendedUser;
$builder
->add('extendedRole',
'choice', array('choices' => array(
$extendedUser::ROLE_1 => "Role 1",
$extendedUser::ROLE_2 => "Role 2",
$extendedUser::ROLE_3 => "Role 3",
),
'label' => $this->user->getUsername()
))
->add('Change roles for '.$this->user->getUsername(), 'submit')
;
$builder->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
if( ! $form->getClickedButton('Change roles for '.$this->user->getUsername()) )
{
// Here I should avoid submitting the form
}
}
);
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Msalsas\UserBundle\Entity\ExtendedUser',
'empty_data' => function (FormInterface $form) {
return null;
}
));
}
/**
* #return string
*/
public function getName()
{
return 'extendedUserRoleForm';
}
}
The problem is that when I submit one of those forms, all other forms are also submitted, returning an error, because the extendedUser uses a constructor to initialize the object with the User as parameter:
Catchable Fatal Error: Argument 1 passed to Msalsas\UserBundle\Entity\ExtendedUser::__construct() must be an instance of Msalsas\UserBundle\Entity\User, none given, called in .../vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/FormType.php on line 140 and defined
I've also tried to set the empty_data with a new default ExtendedUser:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Msalsas\UserBundle\Entity\ExtendedUser',
'empty_data' => function (FormInterface $form) {
return $this->extendedUser;
}
));
}
Now, when submitting the form, the new entity is persisted, but the other forms are still submitted, and returning an error: This form should not contain extra fields. This seems to be due to the duplicated property name (extendedRole).
How could I avoid the other forms to be submitted?
I've found out the solution here.
Each Form must have different name. So I've added a $name property, and assigned it in the constructor:
private $user;
private $name = 'default_name_';
private $extendedUser;
public function __construct(User $user, $formName)
{
$this->user = $user;
$this->extendedUser = new ExtendedUser($this->user);
$this->name = $this->name.$formName;
}
//... (No event required)
/**
* #return string
*/
public function getName()
{
return $this->name;
}
The $formName parameter is relative to the current user. In this way, only the "clicked" form is submitted. Wish it helps.