Symfony 2 - Gedmo Uploadable error durng edition - symfony

I have everything configured as in documentation and everything works perfect if I upload all files.
But when I want change only other elements in form without changing photo i have received following error message:
You must pass an instance of FileInfoInterface or a valid array for entity of class
public function updateAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('CmUserBundle:User')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find User entity.');
}
$deleteForm = $this->createDeleteForm($id);
$editForm = $this->createEditForm($entity);
$editForm->handleRequest($request);
$container = $this->container;
if ($editForm->isValid()) {
$uploadableManager = $this->get('stof_doctrine_extensions.uploadable.manager');
$uploadableManager->markEntityToUpload($entity, $entity->getPath());
$em->flush();
return $this->redirect($this->generateUrl('user_edit', array('id' => $id)));
}
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
);
}
Form clas:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class UserType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', null, array('attr' => array('class' => 'form-control')))
->add('username', null, array('attr' => array('class' => 'form-control')))
->add('path', 'file', array(
'data_class' => null
))
->add('firstName', null, array('attr' => array('class' => 'form-control')))
->add('lastName', null, array('attr' => array('class' => 'form-control')))
->add('localization', null, array('attr' => array('class' => 'form-control')))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'CmUserBundle\Entity\User',
));
}
/**
* #return string
*/
public function getName()
{
return 'appbundle_user';
}
}
How to prevent from update entity by empty input fields? during editForm->handleRequest($request);
Any ideas?

Try $form->submit($request->request->all(), false) instead of $form->handleRequest($request). This will prevent from clearing entity's properties which are not present in incoming POST data.

Related

Custom Variables in custom FormType

Got some question about passing custom variables to a custom FormType
I have a custom FormType named KontoType:
I pass some custom variables to it and this works like as expected, if i override the method buildForm and dump the passed $options array, the mandant exists and is a entity.
But how the heck can i now pass this custom variable to the function getChoices() which loads the choices based on the mandant in this custom FormType?
Even if i did reset the $options in the override buildForm function like $options['choices'] = $this->getChoices($options['mandant']) the select box is empty if i render this form.
<?php
namespace App\Form\Type;
use App\Entity\Core\Finanzen\Konto;
use App\Entity\Core\Organisation\Mandant;
use App\Services\LocaleService;
use Doctrine\Common\Persistence\ObjectManager;
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\Translation\TranslatorInterface;
class KontoType extends AbstractType
{
/**
* #var ObjectManager
*/
private $manager;
/**
* #var TranslatorInterface
*/
private $translator;
/**
* #var LocaleService
*/
private $localeService;
public function __construct(ObjectManager $manager, TranslatorInterface $translator, LocaleService $localeService)
{
$this->manager = $manager;
$this->translator = $translator;
$this->localeService = $localeService;
}
private function getChoices(Mandant $mandant=null)
{
return $this->manager->getRepository(Konto::class)->findBuchbar(true, $mandant);
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'label' => 'konto.name',
'help' => 'konto.help',
'choices' => null,
'attr' => array(
'class' => 'bs-select',
'aria-hidden' => 'true',
'ref' => 'input',
'multiple' => false,
'tabindex' => 1,
'data-live-search' => true,
'data-size' => 6
),
'choice_label' => function ($choiceValue, $key, $value) {
return $choiceValue->getKonto()." ".$this->localeService->doTranslate($choiceValue);
},
'choice_value' => function(Konto $konto = null) {
return $konto ? $konto->getId() : '' ;
},
'required' => true,
'multiple' => false,
'empty_data' => null,
'label_attr' => array(
'class' => 'control-label'
),
'placeholder' => 'message.bitte wählen',
'mandant' => null
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$options['choices'] = $this->getChoices($options['mandant']);
parent::buildForm($builder, $options); // TODO: Change the autogenerated stub
}
public function getParent() {
return ChoiceType::class;
}
}

FormType - ManyToMany on himself

I try to do a Parents, Children relation on an Entity, but I've a problem when I submit the form. (It's a ManyToMany on himself)
To do it, I've a ManyToMany relation on my entity like it:
class Strain
{
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Strain", inversedBy="children")
*/
private $parents;
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Strain", mappedBy="parents")
*/
private $children;
public function __construct()
{
$this->parents = new ArrayCollection();
$this->children = new ArrayCollection();
}
public function addParent(Strain $strain)
{
$this->parents->add($strain);
}
public function removeParent(Strain $strain)
{
$this->parents->removeElement($strain);
}
public function getParents()
{
return $this->parents;
}
public function getChildren()
{
return $this->children;
}
I think it's okay, I've the foreign keys, an intermediate table strain_strain, with 2 columns: strain_source and strain_target.
My FormTypes:
class StrainType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('parents', CollectionType::class, array(
'entry_type' => StrainParentType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'required' => false,
))
;
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Strain'
));
}
And the second:
class StrainParentType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('gmoStrain', EntityType::class, array(
'class' => 'AppBundle\Entity\Strain',
'choice_label' => 'systematicName',
'placeholder' => '-- select a parent --',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('strain')
->orderBy('strain.systematicName', 'ASC');
}
))
;
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Strain'
));
}
When I send the form, symfony return this error:
Neither the property "strain" nor one of the methods "getStrain()", "strain()", "isStrain()", "hasStrain()", "__get()" exist and have public access in class "AppBundle\Entity\Strain".
If someone have an idea :/ Because I don't know how to do it.
EDIT:
The problem is in the FormType, because I need a Collection of EntityType, I've do 2 FormType, but I can do it in on FormType and use entry_options to define config of EntityType, like this:
class StrainType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('parents', CollectionType::class, array(
'entry_type' => EntityType::class,
'entry_options' => array(
'class' => 'AppBundle\Entity\Strain',
'choice_label' => 'systematicName',
'placeholder' => '-- select a parent --',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('strain')
->orderBy('strain.systematicName', 'ASC');
}
),
'by_reference' => false,
'allow_add' => true,
'allow_delete' => true,
'required' => false,
))
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Strain'
));
}

On update don't update an entity replace it

I have a Branch entity that has an Address entity, one address can be link to many other types of entities, what I want to do is when I edit the Address in the Branch entity instead of editing the address with the new data I want to create a new Address entity and attach it to the Branch, what is happening right now is that its editing the address, the problem with this is that I have other entities linked to that address.
This is my form:
class MBBranchType extends AbstractType{
protected $em;
function __construct(EntityManager $em){
$this->em = $em;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('branchName')
->add('branchCode')
->add('destiny', 'entity', array(
'class' => 'ATRBundle:Destiny',
'empty_value' => '-- Seleccione --',
'mapped' => true,
'query_builder' => function(EntityRepository $er){
return $er->createQueryBuilder('d')
->orderBy('d.destinyDesc', 'ASC');
}))
->add('startOperation', 'text')
->add('stopOperation', 'text', array('required' => ''))
->add('authorizationL1', 'checkbox', array('required' => ''))
->add('authorizationL2', 'checkbox', array('required' => ''))
->add('authorizationL3', 'checkbox', array('required' => ''))
->add('address', new AddressType($this->em, new Address()), array('required' => ''))
->add('active', 'checkbox', array('required' => ''));
$builder->get('startOperation')->addModelTransformer(new StringToDateTransformer());
$builder->get('stopOperation')->addModelTransformer(new StringToDateTransformer());
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MB2\ATRBundle\Entity\MBBranch'
));
}
/**
* #return string
*/
public function getName()
{
return 'mb2_atr_mb_branch';
}
}
This is my Action
public function editMBBranchAction()
{
$id = $this->request->query->get('id');
$entity = $this->em->getRepository('ATRBundle:MBBranch')->find($id);
$form = $this->createForm(new MBBranchType($this->getDoctrine()->getManager()), $entity);
$form->handleRequest($this->request);
if ($form->isValid()) {
if($entity instanceof MBBranch){
$addressEntity = $this->em->getRepository('ATRBundle:Address')->findOneBy(
array('street'=>(string)$entity->getAddress()->getStreet()
, 'exteriorNum' => $entity->getAddress()->getExteriorNum();
, 'interiorNum' => $entity->getAddress()->getInteriorNum();
, 'settlment' => $entity->getAddress()->getSettlment()
)
);
if($addressEntity instanceof \MB2\ATRBundle\Entity\Address){
$entity->setAddress($addressEntity);
}else{
$otherAddress = new Address();
$otherAddress->setStreet($entity->getAddress()->getStreet());
$otherAddress->setExteriorNum($entity->getAddress()->getExteriorNum());
$otherAddress->setInteriorNum($entity->getAddress()->getInteriorNum());
$otherAddress->setSettlment($entity->getAddress()->getSettlment());
$entity->setAddress($otherAddress);
}
$this->em->flush();
}
return $this->redirect($this->generateUrl('admin', array('action' => 'list', 'entity' => $this->entity['name'])));
}
return $this->render($this->entity['templates']['edit'], array(
'form' => $form->createView(),
'entity' => $entity,
));
}
As you can see it checks if the new Address entity exist, if it does exist it sets it on the $entity->setAddress($addressEntity);, If it does not exist it created a new Address() and sets the values, This does work, it created a new Address and saves it on the table, but for some reason it also updateing the old Address entity.
I found the solution
I added a $this->em->detach($entity->getAddress()); before attaching a new Address, please see example.
public function editMBBranchAction()
{
$id = $this->request->query->get('id');
$entity = $this->em->getRepository('ATRBundle:MBBranch')->find($id);
$form = $this->createForm(new MBBranchType($this->getDoctrine()->getManager()), $entity);
$form->handleRequest($this->request);
if ($form->isValid()) {
if($entity instanceof MBBranch){
$this->em->detach($entity->getAddress());
$addressEntity = $this->em->getRepository('ATRBundle:Address')->findOneBy(
array('street'=>(string)$entity->getAddress()->getStreet()
, 'exteriorNum' => $entity->getAddress()->getExteriorNum();
, 'interiorNum' => $entity->getAddress()->getInteriorNum();
, 'settlment' => $entity->getAddress()->getSettlment()
)
);
if($addressEntity instanceof \MB2\ATRBundle\Entity\Address){
$entity->setAddress($addressEntity);
}else{
$otherAddress = new Address();
$otherAddress->setStreet($entity->getAddress()->getStreet());
$otherAddress->setExteriorNum($entity->getAddress()->getExteriorNum());
$otherAddress->setInteriorNum($entity->getAddress()->getInteriorNum());
$otherAddress->setSettlment($entity->getAddress()->getSettlment());
$entity->setAddress($otherAddress);
}
$this->em->flush();
}
return $this->redirect($this->generateUrl('admin', array('action' => 'list', 'entity' => $this->entity['name'])));
}
return $this->render($this->entity['templates']['edit'], array(
'form' => $form->createView(),
'entity' => $entity,
));
}

Passing user to symfony2 formType with PUGX multiuser

I'm using PUGX MultiuserBundle because i have 3 differents type of users, with differents informations.
Everything works well, i can register those differents users.
What i want to do is : register user one (it s ok) => this user can create his partner and choose in which establishement he put him.
So, in my second formType, i have to pass user one so i can get all his establishement. But i don t know how to do it with PUGX bundle
My controller :
class BarmanController extends Controller
{
/**
* Register new Barman entities.
*
* #Route("/register", name="barman_register")
* #Template()
*/
public function registerAction()
{
$user = $this->container->get('security.context')->getToken()->getUser();
return $this->container
->get('pugx_multi_user.registration_manager')
->register('Cac\UserBundle\Entity\Barman', $user);
}
}
The PUGX RegistrationManager :
/**
*
* #param string $class
* #return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function register($class, $user)
{
$this->userDiscriminator->setClass($class);
$this->controller->setContainer($this->container);
$result = $this->controller->registerAction($this->container->get('request'));
if ($result instanceof RedirectResponse) {
return $result;
}
$template = $this->userDiscriminator->getTemplate('registration');
if (is_null($template)) {
$engine = $this->container->getParameter('fos_user.template.engine');
$template = 'FOSUserBundle:Registration:register.html.'.$engine;
}
$form = $this->formFactory->createForm();
return $this->container->get('templating')->renderResponse($template, array(
'form' => $form->createView(),
));
}
And my formType for the second user :
class BarmanFormType extends AbstractType
{
private $class;
/**
* #param string $class The User class name
*/
public function __construct($class)
{
$this->class = $class;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$id = 5;
$builder
->add('username', null, array('label' => 'Nom d\'utilisateur'))
->add('plainPassword', 'repeated', array(
'label' => 'Mot de passe',
'type' => 'password',
'options' => array('translation_domain' => 'FOSUserBundle'),
'first_options' => array('label' => 'Mot de passe'),
'second_options' => array('label' => 'Confirmez votre mot de passe'),
'invalid_message' => 'fos_user.password.mismatch',
))
->add('email', 'repeated', array(
'type' => 'email',
'options' => array('translation_domain' => 'FOSUserBundle'),
'first_options' => array('label' => 'E-mail'),
'second_options' => array('label' => 'Confirmez votre e-mail'),
'invalid_message' => 'fos_user.email.mismatch',
))
->add('bar', 'entity', array('class' => 'CacBarBundle:Bar', 'property' => 'name', 'attr' => array('placeholder' => 'Bar'),
'query_builder' => function(\Cac\BarBundle\Entity\BarRepository $er) use ($id) {
return $er->getBarByAuthorId($id);
}
));
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => $this->class,
'intention' => 'registration',
));
}
public function getName()
{
return 'cac_barman_registration';
}
}
I also have a general register controller that override FOS User registration controller :
class RegistrationController extends Controller
{
public function registerAction(Request $request)
{
/** #var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
$formFactory = $this->get('fos_user.registration.form.factory');
/** #var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
/** #var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
$dispatcher = $this->get('event_dispatcher');
$user = $userManager->createUser();
$user->setEnabled(true);
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
$form = $formFactory->createForm();
$form->setData($user);
$form->handleRequest($request);
if ($form->isValid()) {
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_SUCCESS, $event);
$userManager->updateUser($user);
if (null === $response = $event->getResponse()) {
if (strpos($request->getPathInfo(), "/barman/") === false) {
$url = $this->generateUrl('fos_user_registration_confirmed');
$response = new RedirectResponse($url);
}
else
{
$this->get('session')->getFlashBag()->add('notice','Le barman a était créé !');
$response = new Response('barman');
}
}
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
return $response;
}
return $this->render('CacUserBundle:Registration:register.html.twig', array(
'form' => $form->createView(),
));
}
}
I manage to get the user in the PUGX part, but i don't know ow to pass it from the RegistrationManager to my FormType
I tried lots of possibilities, i think i m close but can't find a solution to make it work

how to dynamically set cascade validation of form from controller

My form looks like this :
class CpanelRetailerForm extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('name', 'text', array(
'attr' => array(
'class' => 'text-input',
'size' => '50'
),
'required' => false
))
->add('email', 'email', array(
'attr' => array(
'class' => 'text-input',
'size' => '50'
),
'required' => false
))
->add('addUser', 'checkbox', array(
'label' => 'Add User account',
'required' => false,
'mapped' => false
))
->add('user',new CpanelUserForm());
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'Acme\TestBundle\Entity\Retailer',
//'cascade_validation' => true
));
}
public function getName() {
return 'retailer';
}
}
I want to dynamically set this line from controller depending on whether addUser field is checked or unchecked.
cascade_validation' => true
Here is my controller code:
$form = $this->createForm(new CpanelRetailerForm(), new Retailer());
$form->
if ($this->getRequest()->isMethod('POST')) {
$form->bind($this->getRequest());
if ($form->get('addUser')->getData()) {
// then set the cascade_validation to true here
}
}
How can I do this inside controller?
My attempt :
added this line in my form class:
$builder->addEventListener(
FormEvents::POST_SUBMIT, function(FormEvent $event) {
$form = $event->getForm();
$addUser = $form->get('addUser')->getData();
$validation = false;
if ($addUser) {
$validation = true;
}
$resolver = new OptionsResolver();
$resolver->setDefaults(array(
'cascade_validation' => $validation
));
$this->setDefaultOptions($resolver);
}
);
This didnot work for me. Although I receive data in $addUser, cascade_validation is not added
How can I do this inside controller?
You can´t! Thats the simple answer. Lets take a look at following simple form class:
class TestType extends AbstractType {
/**
* #var boolean
*/
private $myOption;
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$this->myOption = false;
$builder
->addEventListener(FormEvents::POST_SET_DATA, function(FormEvent $event) {
dump('formEvents::PRE_SET_DATA');
})
->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
dump('FormEvents::POST_SET_DATA');
})
->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) {
dump('FormEvents::PRE_SUBMIT');
})
->addEventListener(FormEvents::SUBMIT, function(FormEvent $event) {
dump('FormEvents::SUBMIT');
})
->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
dump('formEvents::POST_SUBMIT');
})
->add('name', TextType::class)
->add('send', SubmitType::class);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver) {
$resolver->setRequired(array(
'my_option'
));
$resolver->setDefaults(array(
'my_option' => $this->setMyOption()
));
}
/**
* #return bool
*/
public function setMyOption() {
dump($this->myOption);
return $this->myOption;
}
}
Lets take in how you render and handle a form inside a Controller:
public function formAction(Request $request) {
$form = $this->createForm(TestType::class);
dump('calledCreateForm');
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
dump('finished');
dump($form->getData());
die();
}
return $this->render('#TestPra/Test/test_form.html.twig', array(
'form' => $form->createView()
));
}
After submitting the form you get the following output order:
$this->setMyOption() > null
FormEvents::PRE_SET_DATA
FormEvents::POST_SET_DATA
calledCreateForm
FormEvents::PRE_SUBMIT
FormEvents::SUBMIT
FormEvents::POST_SUBMIT
finished
The first thing allways gets called is configureOptions and because you don´t have any data of the filled form before calling handleRequest there is no way to change the options of the allready created form without manipulating Symfonys form component.

Resources