Pass one id to a composite key in form - symfony

I have a form in which I chose student id and a id of a course which make a composite key in a UserCourse entity, and with that a status of that course (passed, enrolled etc).
What I want is to pass the value of currently logged in student to the form to populate the userid field with current user id, so that it only has to choose the course and status and submit it.
I have tried using default_value and data => $userId but failed.
This is the UserController
/**
* #Route("/courses/{userId}/new", name="new_usercourse")
*/
public function newMylistAction(Request $request, $userId)
{
$userCourse = new UserCourse();
$userCourse->setUserId($userId);
$form = $this->createForm('AppBundle\Form\UserCourseType', $userCourse);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($userCourse);
$em->flush();
return $this->redirectToRoute('mycourses');
}
return $this->render('student/new_mycourses.html.twig', array(
'usercourse' => $userCourse,
'form' => $form->createView()
));
}
This is the UserCourseType Form. userId is also an EntityType
namespace AppBundle\Form;
use AppBundle\Entity\Course;
use AppBundle\Entity\UserCourse;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class UserCourseType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('userId', null)
->add('courseId', EntityType::class, array(
'class' => 'AppBundle\Entity\Course',
'choice_label' => 'name'
))
->add('status', ChoiceType::class, array(
'choices' => array(
'Passed' => 'passed',
'Enrolled' => 'enrolled',
'Null' => '',
)))
->add('save', SubmitType::class, array('label' => 'Create'));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => UserCourse::class
));
}
}
Hope you can help

before flush() set user id
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$userCourseRepo=$em->getRepository('AppBundle:UserCourse');
$userCourseIddata = $userCourseRepo->find($id);
$userCourse->setUserId($userCourseIddata);////set userid
$em->persist($userCourse);
$em->flush();
return $this->redirectToRoute('mycourses');
}

Related

Symfony 5 why can't I use FormsEvents::POST_SUBMIT in my form event listener?

I'm trying to add an EventListener to my Symfony form but I have a problem with the first parameter $listener of $builder->addEventListener. I want to use FormEvents::POST_SUBMIT to generate a new field after the submit. Basically I want to display list of cities based on the postal code.
The error tells me that the object is of the wrong type but I don't see which object I could use instead because the documentation tells me to do so. I'm working on Symfony 5.2
Here is my form code and the error :
<?php
namespace App\Form;
use App\Entity\Advert;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Event\PostSubmitEvent;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
class CreateAdvertType extends AbstractType
{
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$repositoryCities=$this->entityManager->getRepository('App\Entity\Cities');
$cities = $repositoryCities->findByPostal("86330");
$repositoryMake=$this->entityManager->getRepository('App\Entity\Make');
$makes = $repositoryMake->findAll();
$builder
->add('car_make',EntityType::class, array(
'class' => 'App\Entity\Make',
'choices' => $makes,
))
->add('car_model')
->add('car_motorisation')
->add('car_fuel',ChoiceType::class, array(
'choices' => [
'Diesel' => 'diesel',
'Essence' => 'essence',
'Electrique' => 'electrique',
'Hybride' => 'hybride',
],
))
->add('price', IntegerType::class, array(
'attr' => array(
'min' => 0,
'max' => 20,
)
))
->add('code_postal')
->add('description')
->add('save', SubmitType::class, ['label' => 'Create Task'])
->addEventListener(FormEvents::POST_SUBMIT,function (FormEvents $event){
$repository=$this->getDoctrine()->getManager()->getRepository('App\Entity\Cities');
$form = $event->getForm();
$cities = $repository->findByPostal($form->getData()['code_postal']);
$form->add('city' ,EntityType::class, array(
'class' => 'App\Entity\Cities',
'choices' => $cities
));
})
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Advert::class,
]);
}
}
Argument 1 passed to App\Form\CreateAdvertType::App\Form\{closure}() must be an instance of Symfony\Component\Form\FormEvents, instance of Symfony\Component\Form\Event\PreSetDataEvent given, called in /var/www/html/trymycar/vendor/symfony/event-dispatcher/EventDispatcher.php on line 230
Should be FormEvent (singular not plural).
// plural HERE ---v singular HERE ---v
->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
// ...
})

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).

Symfony2 Choice Form Data from ORM

Have this form:
class ScanType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('scantarget', 'entity', array(
'class' => 'AppBundle:Website',
'property' => 'url'
));
}
public function getDefaultOptions(array $options) {
return array(
'data_class' => 'AppBundle\Entity\Website',
);
}
}
Need to populate it with only urls for the particular userid
In the controller:
/**
* #Route("/verification-ok", name="verifyurlok")
*/
public function verifyurlActionOK(Request $request)
{
$user = $this->getUser();
if($user)
{
$userid=$user->getId();
$websites = $this->getDoctrine()->getRepository('AppBundle:Website')->findByUser($userid);
$form = $this->createForm(ScanType::class, $websites);
However, the $websites is not passed properly to FormBuilder and in my Select box I see all entries :( All possible values for Website entity.
How to Display only passed $websites in the form (select box)? So only websites for a specific userid?
Thanks,
Update 1
Form
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class ScanType extends AbstractType
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$user = $this->tokenStorage->getToken()->getUser();
if (!$user) {
throw new \LogicException(
'The FriendMessageFormType cannot be used without an authenticated user!'
);
}
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($user) {
$form = $event->getForm();
$formOptions = array(
'class' => 'AppBundle\Entity\Website',
'property' => 'user',
'query_builder' => function (EntityRepository $er) use ($user) {
// build a custom query
return $er->createQueryBuilder('u')->addOrderBy('user', 'DESC');
// or call a method on your repository that returns the query builder
// the $er is an instance of your UserRepository
// return $er->createOrderByFullNameQueryBuilder();
},
);
// create the field, this is similar the $builder->add()
// field name, field type, data, options
$form->add('url', ScanType::class, $formOptions);
}
);
}
public function getDefaultOptions(array $options) {
return array(
'data_class' => 'AppBundle\Entity\Website',
);
}
}
Controller:
$form = $this->createForm(ScanType::class);
config.yml
services:
app.form.scan:
class: AppBundle\Form\ScanType
arguments: ['#security.token_storage']
tags:
- { name: form.type }
Unfortunately, get this error:
request.CRITICAL: Uncaught PHP Exception Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException: "The options "class", "property", "query_builder" do not exist. Defined options are: "action", "allow_extra_fields", "attr", "auto_initialize", "block_name", "by_reference",
Solution:
Thanks #Medard
Form:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ScanType extends AbstractType
{
private $websites;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->websites = $options['trait_choices'];
$builder->add('scantarget', 'entity', array(
'class' => 'AppBundle:Website',
'property' => 'url',
'choices' => $this->websites
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => null,
'trait_choices' => null,
));
}
}
Controller:
$form = $this->createForm(ScanType::class, $websites, array(
'trait_choices' => $websites
));
Works!!!
Well, you are not setting the choices. Try this:
class ScanType extends AbstractType
{
private $websites;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->websites = $options['trait_choices'];
$builder->add('scantarget', 'entity', array(
'class' => 'AppBundle:Website',
'property' => 'url',
'choices' => $this->websites
));
}
public function setDefaultOptions(array $options) {
return array(
'data_class' => 'AppBundle\Entity\Website',
'trait_choices' => null,
);
}
}
In the controller pass through the websites:
$form = $this->createForm(ScanType::class, $websites, array(
'trait_choices' => $websites,
));
Your form displays all the Website entries because you're not restricting them when building the form, in the add() method.
You should read this section fo the cookbook : http://symfony.com/doc/current/form/dynamic_form_modification.html#form-events-user-data

Form mapped to class by different name

I would like to associate my class with the forms. Is it possible that I gave different names for forms than they are in the class? For example class have property username but I would like to be mapped in the form to input of id login
use Symfony\Component\Form\FormBuilderInterface;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('login', 'text', array (
'label' => 'Login'
)) //<- different name mapped by login
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\User',
));
}
User entity:
class User {
protected username;
//...
}
Yes. You are looking for property_path. See http://symfony.com/doc/2.8/reference/forms/types/form.html#property-path
$builder->add('login', 'text', array(
'label' => 'Login',
'property_path' => 'username',
));
Yes you can. Add 'mapped' => false option to the field which you want to have different name than it is in entity. This tells symfony that when the form will be submitted, it would not try to map that field to entity. But here, you will need to do additional work - 1. add data from entities username property to the unmapped one, and 2. when submitting the form, map manually submitted login data to the username property. These steps can be done via Form Events, like this:
use Symfony\Component\Form\FormBuilderInterface;
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Set other fields...
$builder->addEventListener(FormEvents::PRE_SET_DATA, [$this, 'onPreSetData']);
}
function onPreSetData(FormEvent $event)
{
$form = $event->getForm(); // The form
$data = $event->getData(); // It will be User entity
$form
->add('login', 'text', array (
'label' => 'Login',
'mapped' => false,
'data' => $data->getUsername(),
));
}
function onPostSubmit(FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
$data->setUsername($form->get('login')->getData());
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\User',
));
}
I'm not sure why you want to do this, but I showed you one way of doing this. Another and simplier way like qooplmao commented would be using property_path.

How to create a form to edit user roles with FriendsOfSymfony UserBundle

I'm trying to create a controller where I can edit the roles of a user (just that, nothing else) and I'm king of stuck.
I've created a form type:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'roles', 'choice', [
'choices' => ['ROLE_ADMIN', 'ROLE_USER', 'ROLE_CUSTOMER'],
'expanded' => true,
'multiple' => true,
]
)
->add('send', 'submit');
}
First, what would be the best way to retrieve the roles? Is there any way to associate a label to them?
In the controller I have this:
/**
* User role edition
*
* #Route(
* path="/edit-roles",
* name = "backoffice_user_edit_roles",
* requirements = {
* "id_user" = "\d*",
* },
* methods = {"GET"}
* )
*
* #Security("has_role('ROLE_ADMIN')")
*
* #Template
*/
public function editRolesAction($id_user)
{
$user = $this->user_repository->findOneById($id_user);
$form = $this->form_factory->create('dirital_user_roles_form_type', $user);
return [
'form' => $form->createView(),
'user' => $user
];
}
Problems that I have:
The form doesn't get populate with the current user roles, how should I do that?
When receiving the form, how can I update the user?
Thanks a lot
Actually it was easier than I thought – this is the form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'roles', 'choice', [
'choices' => ['ROLE_ADMIN' => 'ROLE_ADMIN', 'ROLE_USER' => 'ROLE_USER', 'ROLE_CUSTOMER' => 'ROLE_CUSTOMER'],
'expanded' => true,
'multiple' => true,
]
)
->add('save', 'submit', ['label' => 'ui.button.save']);
}
And the controller:
public function editRolesAction(Request $request, $id_user)
{
$user = $this->user_repository->findOneById($id_user);
$form = $this->form_factory->create('dirital_user_roles_form_type', $user);
$form->handleRequest($request);
if($form->isValid())
{
$this->addFlash('success', 'section.backoffice.users.edit_roles.confirmation');
$this->em->persist($user);
$this->em->flush();
$this->redirectToRoute('backoffice_user_edit_roles', ['id_user' => $user->getId()]);
}
return [
'form' => $form->createView(),
'user' => $user
];
}
The only part that remains to do is grabbing the form choices from the config instead of hardcoding them.
To rehuse easily in other controllers or actions, I like more this approach:
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\CallbackTransformer;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add(
'roles', 'choice', [
'choices' => ['ROLE_ADMIN' => 'ROLE_ADMIN', 'ROLE_USER' => 'ROLE_USER', 'ROLE_CUSTOMER' => 'ROLE_CUSTOMER'],
'expanded' => true,
'multiple' => true,
]
)
->add('save', 'submit', ['label' => 'ui.button.save']);
;
// Data transformer
$builder->get('roles')
->addModelTransformer(new CallbackTransformer(
function ($rolesArray) {
// transform the array to a string
return count($rolesArray)? $rolesArray[0]: null;
},
function ($rolesString) {
// transform the string back to an array
return [$rolesString];
}
));
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
without touching the controller.
I added the whole file to see the clases you need to import

Resources