I have a form called ProfileType, when i submit this form I want to process something so a form event subscriber i thought should do the job so I went and implemented that but when i submit the form the events are not getting fired and I am seeing the events in profiler under tab "Not called events"
So this is what I did, I am not sure what I am missing here.
This is my ProfileType class and as you will notice i have ->addEventSubscriber(new SkillsListener())
namespace AppBundle\Form;
use AppBundle\EventListener\SkillsListener;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ProfileType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('street')
->add('city')
->add('state')
->addEventSubscriber(new SkillsListener())
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\User',
'validation_groups' => 'profile'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_user';
}
}
I then created my listner
namespace AppBundle\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class SkillsListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
FormEvents::PRE_SUBMIT => 'onPreSubmit',
FormEvents::PRE_SET_DATA => 'onPreSetData',
);
}
public function onPreSetData(FormEvent $event)
{
$user = $event->getData();
$form = $event->getForm();
dump($user, $form);
die;
}
public function onPreSubmit(FormEvent $event)
{
$user = $event->getData();
$form = $event->getForm();
dump($user, $form);
die;
}
}
I did dump() and die just to debug what i get so i can implement the process i need but the event does not fire.
I thought may be I need to add something in services.yml but that does not help either
app.form_event_listener.skills_listener:
class: AppBundle\EventListener\SkillsListener
tags:
- { name: kernel.event_subscriber }
I do not get any error, its just that the event does not fire. What am i missing here? Any help will be appreciated.
UPDATE 1
This is the controller linked with form
/**
* #Route("/profile/edit", name="internee_profile_edit")
* #param Request $request
*
* #return \Symfony\Component\HttpFoundation\Response
*/
public function editProfileAction( Request $request ) {
$em = $this->getDoctrine()->getManager();
/** #var User $user */
$user = $this->getUser();
$profileForm = $this->createForm( ProfileType::class, $user );
$profileForm->handleRequest( $request );
if ( $profileForm->isValid() ) {
$em->persist( $user );
$em->flush();
$this->addFlash( 'success', $this->get( 'translator' )->trans( 'user.profile.success.saved' ) );
}
return $this->render( 'AppBundle:jobseeker:profile-edit.html.twig', array( 'form' => $profileForm->createView() ) );
}
UPDATE 2
I have updated my ProfileType to use a listener and that is not working either.
public function buildForm( FormBuilderInterface $builder, array $options ) {
$builder
->add( 'name' )
->add( 'street' )
->add( 'city' )
->add( 'state' )
;
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
array( $this, 'onPreSetData' )
);
}
public function onPreSetData(FormEvent $event)
{
dump($event);
}
Related
I'm new to symfony and still learning, my question is how do I populate a select drop-down in a form with an static array of choices. Say I have a class named Cake, I'd like to be able to fill a drop-down for the status of Cake from the array statuses created in the same CakeEntity:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\CakeRepository")
*/
class Cake
{
/**
* #ORM\Column(type="string", length=50)
*/
private $status;
private $statuses = array(
'not_ready' => 'Not Ready',
'almost_ready' => 'Almost Ready',
'ready'=>'Ready',
'too_late'=>'Too late'
);
public function getStatus(): ?string
{
return $this->status;
}
public function setStatus(string $status): self
{
$this->status = $status;
return $this;
}
public function getStatuses()
{
return $this->statuses;
}
}
My Controller looks like:
namespace App\Controller;
use App\Entity\Cake;
use App\Form\CakeType;
use App\Repository\CakeRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* #Route("/cake")
*/
class CakeController extends AbstractController
{
/**
* #Route("/new", name="cake_new", methods={"GET","POST"})
*/
public function new(Request $request): Response
{
$cake = new Cake();
$form = $this->createForm(CakeType::class, $cake);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$cake->setCreatedAt(\DateTime::createFromFormat('d-m-Y', date('d-m-Y')));
$cake->setCreatedBy(1);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($cake);
$entityManager->flush();
return $this->redirectToRoute('cake_index');
}
return $this->render('cake/new.html.twig', [
'cake' => $cake,
'form' => $form->createView(),
]);
}
My CakeEntity:
<?php
namespace App\Form;
use App\Entity\cake;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
class CakeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
->add('status', ChoiceType::class,
[
'choices'=>function(?Cake $cake) {
return $cake->getStatuses();
}
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Cake::class,
]);
}
}
When trying to browse /cake/new I get the error:
An error has occurred resolving the options of the form "Symfony\Component\Form\Extension\Core\Type\ChoiceType": The option "choices" with value Closure is expected to be of type "null" or "array" or "\Traversable", but is of type "Closure".
You could declare getStatuses on Cake as static, or use public constants. E.g.:
class Cake
{
// with static variables
private static $statuses = [
'not_ready' => 'Not Ready',
'almost_ready' => 'Almost Ready',
'ready' => 'Ready',
'too_late' => 'Too late',
];
public static function getStatuses()
{
return self::$statuses;
}
// or with public const
public const STATUSES = [
'not_ready' => 'Not Ready',
'almost_ready' => 'Almost Ready',
'ready' => 'Ready',
'too_late' => 'Too late',
];
}
This seems reasonable, as the return value is not instance but class specific.
You could then use:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('status', ChoiceType::class, [
'choices'=> Cake::getStatuses(),
]);
// or
$builder->add('status', ChoiceType::class, [
'choices'=> Cake::STATUSES,
]);
}
If the choices actually depend on a given Cake instance, you could pass it via the options array or use form events.
I want to know how I can recover the current user in my FormType (EntityType)?
Currently, I can only retrieve the list of registered users but not the current (connected) user.
My current FormType
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class OneNewCarType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'author',
EntityType::class, [
'label' => 'Ville',
'class' => User::class,
'attr' => [
'class' => 'selectpicker'
],
'choice_label' => function ($author) {
return $author->getCity();
}
]
);
}
public function getBlockPrefix()
{
return 'oneNewCarType';
}
}
I know how to recover it directly in my controller. But, I do not know how to do it when one uses the stepper bundle CraueFormFlowBundle
This is my current controller
/**
* #Route("/classified/{id}/edit", name="classified_edit")
* #param CarVehicle $carVehicle
* #param ObjectManager $manager
* #param CreateVehicleFlow $createVehicleFlow
* #return RedirectResponse|Response
*/
public function edit(CarVehicle $carVehicle, ObjectManager $manager, CreateVehicleFlow $createVehicleFlow)
{
$flow = $createVehicleFlow;
$flow->bind($carVehicle);
$form = $flow->createForm();
if ($flow->isValid($form)) {
$flow->saveCurrentStepData($form);
if ($flow->nextStep()) {
$form = $flow->createForm();
} else {
$manager->persist($carVehicle);
$manager->flush();
$flow->reset();
$this->addFlash(
'success',
"Votre annonce <i>{$carVehicle->getId()}</i> a bien été mise à jour"
);
return $this->redirect($this->generateUrl('account_index'));
}
}
return $this->render('front/classified/edit_vehicle.html.twig', [
'form' => $form->createView(),
'flow' => $flow,
'carVehicle' => $carVehicle,
]);
}
Thanks for the help !
With Symfony\Component\Security\Core\Security you can get user where you want. Inject this into FormType and use $user = $this->security->getUser();
private Security $security;
public function __construct(Security $security)
{
$this->security = $security;
}
In Symfony 5, With use
Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface
you can get user where you want. Inject this into FormType and use
$user = $this->token->getToken()->getUser();
private $token;
public function __construct(TokenStorageInterface $token)
{
$this->token = $token;
}
I have created an update password form and whenever I try to submit the data I got error like The class XXX was not found in the chain configured namespaces. What am I doing wrong in here? The error occurs when I try to save the data in the database. I have tried the answer from implementing update password
Here are my codes.
This is my ChangePasswordType.php
<?php
namespace RetailMapping\Bundle\UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ChangePasswordType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('oldPassword', 'password');
$builder->add('newPassword', 'repeated', array(
'type' => 'password',
'invalid_message' => 'The password fields must match.',
'required' => true,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password'),
));
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'RetailMapping\Bundle\UserBundle\Form\Model\ChangePassword',
));
}
/**
* #return string
*/
public function getName()
{
return 'retailmapping_bundle_userbundle_password_chnage';
}
}
This is my ChangePassword.php
<?php
namespace RetailMapping\Bundle\UserBundle\Form\Model;
use Symfony\Component\Security\Core\Validator\Constraints as SecurityAssert;
use Symfony\Component\Validator\Constraints as Assert;
class ChangePassword
{
/**
* #SecurityAssert\UserPassword(
* message = "Wrong value for your current password"
* )
*/
protected $oldPassword;
/**
* #Assert\Length(
* min = 6,
* minMessage = "Password should by at least 6 chars long"
* )
*/
protected $newPassword;
public function setOldPassword($oldPassword)
{
$this->oldPassword = $oldPassword;
}
public function getOldPassword()
{
return $this->oldPassword;
}
public function setNewPassword($newPassword)
{
$this->newPassword = $newPassword;
}
public function getNewPassword()
{
return $this->newPassword;
}
}
This is my UserController.php
<?php
namespace RetailMapping\Bundle\UserBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use RetailMapping\Bundle\UserBundle\Form\ChangePasswordType;
use Symfony\Component\HttpFoundation\Request;
use RetailMapping\Bundle\UserBundle\Form\Model\ChangePassword;
class UserController extends Controller
{
public function editPasswordAction(Request $request)
{
$form = $this->createForm(new ChangePasswordType(), new ChangePassword());
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
$form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($data);
$em->flush();
dump($form->getData());
die;
}
return $this->render('User/editPassword.html.twig', [
'form' => $form->createView(),
]);
}
}
By default your entity is mapped to Entity namespace.
DoctrineExtension code on github
protected function getMappingObjectDefaultName()
{
return 'Entity';
}
You need to place your ChangePassword.php within Entity folder.
UPDATE:
Similar question - you can look at
I have created an update password form and whenever I try to submit the data I got error like The class 'RetailMapping\Bundle\UserBundle\Form\Model\ChangePassword' was not found in the chain configured namespaces AppBundle\Entity, RetailMapping\Bundle\UserBundle\Entity, RetailMapping\Bundle\CatalogBundle\Entity, RetailMapping\Bundle\LocationBundle\Entity, RetailMapping\Bundle\ClassificationBundle\Entity, RetailMapping\Bundle\RetailOutletBundle\Entity, FOS\UserBundle\Model. What am I doing wrong in here? The error occurs when I try to save the data in the database.
Here are my codes.
This is my ChangePasswordType.php
<?php
namespace RetailMapping\Bundle\UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ChangePasswordType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('oldPassword', 'password');
$builder->add('newPassword', 'repeated', array(
'type' => 'password',
'invalid_message' => 'The password fields must match.',
'required' => true,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password'),
));
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'RetailMapping\Bundle\UserBundle\Form\Model\ChangePassword',
));
}
/**
* #return string
*/
public function getName()
{
return 'retailmapping_bundle_userbundle_password_chnage';
}
}
This is my ChangePassword.php
<?php
namespace RetailMapping\Bundle\UserBundle\Form\Model;
use Symfony\Component\Security\Core\Validator\Constraints as SecurityAssert;
use Symfony\Component\Validator\Constraints as Assert;
class ChangePassword
{
/**
* #SecurityAssert\UserPassword(
* message = "Wrong value for your current password"
* )
*/
protected $oldPassword;
/**
* #Assert\Length(
* min = 6,
* minMessage = "Password should by at least 6 chars long"
* )
*/
protected $newPassword;
public function setOldPassword($oldPassword)
{
$this->oldPassword = $oldPassword;
}
public function getOldPassword()
{
return $this->oldPassword;
}
public function setNewPassword($newPassword)
{
$this->newPassword = $newPassword;
}
public function getNewPassword()
{
return $this->newPassword;
}
}
This is my UserController.php
<?php
namespace RetailMapping\Bundle\UserBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use RetailMapping\Bundle\UserBundle\Form\ChangePasswordType;
use Symfony\Component\HttpFoundation\Request;
use RetailMapping\Bundle\UserBundle\Form\Model\ChangePassword;
class UserController extends Controller
{
public function editPasswordAction(Request $request)
{
$form = $this->createForm(new ChangePasswordType(), new ChangePassword());
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
$form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($data);
$em->flush();
dump($form->getData());
die;
}
return $this->render('User/editPassword.html.twig', [
'form' => $form->createView(),
]);
}
}
There's no mention of an Entity in the post you linked. Because you made the class an Entity, it's become a database table, and fields like id's are expected.
Double check the answer you tried to implement.
For making a password update form you need to make PasswordUpdateModel and PasswordUpdateType, after processing form you can assign it to UserEntity fields.
I hope this helps.
I have a form type and an other form type include the first one in one of his fields.
The second form type is used to display a list of entities
The first form type:
<?php
namespace Test\GameBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Doctrine\ORM\EntityRepository;
class CityType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('nameEn')
->add('latitude')
->add('longitude')
->add('country','entity',array(
'class' => 'GeoQuizzGameBundle:Country',
'property' => 'nameEn',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('c')
->orderBy('c.nameEn', 'ASC');
},
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Test\GameBundle\Entity\City'
));
}
/**
* #return string
*/
public function getName()
{
return 'test_gamebundle_city';
}
}
The second entity:
namespace Test\AdminBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Test\GameBundle\Form\CityType;
class CityListType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('cities','collection', array(
'type'=>new CityType(),
'allow_add' => true,
'by_reference' => false
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Test\AdminBundle\Entity\CityList'
));
}
/**
* #return string
*/
public function getName()
{
return 'test_adminbundle_citylist';
}
}
and the form creation in the controller:
public function listAction(Request $request)
{
$cityRepository = $this->getDoctrine()->getRepository("GeoQuizzGameBundle:City");
//get continents
$cities = $cityRepository->findBy(
array(),
array('nameEn' => 'ASC')
);
$cityList = new CityList();
foreach($cities as $city){
$cityList->addCity($city);
}
$form = $this->createForm(new CityListType(),$cityList);
$form->handleRequest($request);
if($form->isValid()){
$em = $this->getDoctrine()->getManager();
foreach ($cityList->getCities() as $city){
if(!$this->isCity($cities, $city)){
$em->persist($city);
}
}
$em->flush();
return $this->redirect($this->generateUrl('geo_quizz_admin_city_list'));
}
return $this->render('GeoQuizzAdminBundle:City:list.html.twig',array(
'form' => $form->createView()
));
}
I have 196 request for 124 fields because the country list is requery on each row, is there a solution to prevent it ?
I can do the query in the controller and pass my country array as argument of the form type, is it clean ?
You can use choice field type instead of entity. All you need to do is have choices parameter containing your list of countries.
$countryChoices is an associative array of countries which you can fetch once and use it in buildForm method. The way I would do that is making your form a service, and passing ObjectManager to contructor:
services.yml:
services:
your_form:
class: Test\GameBundle\Form\CityListType
arguments: [#doctrine.orm.entity_manager]
tags:
- { name: form.type, alias: yourFormAlias }
Your CityType class:
class CityType extends AbstractType
{
/**
* #param ObjectManager $objectManager
*/
public function __construct(ObjectManager $objectManager)
{
$this->objectManager = $objectManager;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$countryChoices = array();
//by using result cache your query would be performed only once
$countries = $this->objectManager
->getRepository('GeoQuizzGameBundle:Country')
->createQueryBuilder('c')
->orderBy('c.nameEn', 'ASC')
->getQuery()
->useResultCache(true)
->getResult();
foreach($countries as $country) {
$countryChoices[$country->getId()] = $country->getNameEn();
}
$builder
->add('country','choice',array(
'choices' => $countryChoices,
'label' => 'Country',
))
;
}
}
You will also need to start call your form like a service.