Symfony2 - User specific form based on role access - forms

I have a form that has a category field drop down that has a (OneToMany/ManyToOne) to a post entity.
Situation: Right now the client has to select category in the drop down and they can make the mistake of selecting the wrong category and this will go to another blog (if they select the wrong one) and they will not have access to change this back to the proper category.
To alleviate this potential problem I'd like to do one of the two as a solution:
1) Set the category automatically based on the category they have access to
2) Or restrict the user to only select the category they have access to (e.g., if the user has a specific role they only get this category for the drop down)
The user has a ROLE_USER restriction that allows them to only CRUD what they have access to.
e.g.,
ROLEUSER1 only has access to /category1 (and can use CRUD on this)
ROLEUSER2 only has access to /category2 (and can use CRUD on this)
ROLEUSER3 only has access to /category3 (and can use CRUD on this)
How can I set this up so the client cannot make the mistake of selecting the wrong category?
Form
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('body')
->add('author')
->add('category')
->add('file', 'file', array(
'label' => 'Image',
'required' => false
))
->add('created');
}
Controller
public function job1CreateAction(Request $request)
{
$entity = new Post();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('job1_show', array('id' => $entity->getId())));
}
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
private function createCreateForm(Post $entity)
{
$form = $this->createForm(new PostType(), $entity, array(
'action' => $this->generateUrl('job1_create'),
'method' => 'POST',
));
$form->add('submit', 'submit', array('label' => 'Create'));
return $form;
}

how about this? :
remove 'category' field from form builder and manually set it in controller action:
if ($this->get('security.context')->isGranted('ROLEUSER1') {
$entity->setCategory(CATEGORY1);
}
EDIT:
controller action:
public function job1CreateAction(Request $request)
{
$entity = new Post();
if ($this->get('security.context')->isGranted('ROLEUSER1') {
$category1 = $this->getDoctrine()->getManager()->getRepository('MYBUNDLE:POST')->find(1); // we are getting category object. this is just an example cade, may be this will be different in your case
$entity->setCategory($category1);
}
$form = $this->createCreateForm($entity);
....
}
and form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('body')
->add('author')
//->add('category')
->add('file', 'file', array(
'label' => 'Image',
'required' => false
))
->add('created');
}

Related

Symfony2 Form Splitting in sub forms with css helper

I have large form, i need to split it in multiple form when on mobile view.
Desktop = 1 large form
Mobile = 2-3 smaller form, when i valid the 1 form, then new page 2 form, and so on..
I would like to do it in responsive way NOT sub-domaine like (http://mobile/blah.com)
PS: I want to avoid third party bundle !!
Advice, recommandation, direction anything than can help me
My controller:
public function ownerRegisterAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$owner = new Owner();
$form = $this->createCreateForm($owner);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid())
{
$password = $this->get('security.password_encoder')
->encodePassword($owner->getOwner(), $owner->getOwner()->getPassword());
$owner->getOwner()->setPassword($password);
$owner->getOwner()->setStatus('owner');
$owner->getOwner()->setIsValid(0);
$em->persist($owner);
$em->flush();
// Login users after registration
$this->get('apx_auth_after_register')->authenticateUser($tenant->getTenant());
$response = $this->forward('PagesBundle:SearchOwner:search', ['owner' => $owner]);
return $response;
}
return $this->render('::form/owner-register.html.twig', array(
'form' => $form->createView()
));
}
My Form Type :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('corporate', CorporateType::class, ['expanded' => true, 'multiple' => false, 'label' => false])
//->add('postcode', NumberType::class,['label' => false])
->add('type', TypeType::class, ['expanded' => true, 'multiple' => false,'label' => false])
->add('room', NbRoomType::class,['expanded' => true, 'multiple' => false,'label' => false])
->add('rent', NumberType::class,['label' => false])
->add('area', NumberType::class,['label' => false])
->add('images', CollectionType::class, [
'entry_type' => ImagesFlatType::class,
'allow_add' => true,
'required' => false,
'allow_delete' => true,
'label' => false,
])
->add('fee', NumberType::class, ['label' => false, 'required' => false])
// to be defined in list by city
->add('transport', TextType::class,['label' => false, 'required' => false])
->add('furnished', FurnishedType::class, ['expanded' => true, 'multiple' => false,'label' => false])
->add('vip', LesVipType::class, ['expanded' => true, 'multiple' => true,'label' => false])
->add('feature', FeatureType::class, ['expanded' => true, 'multiple' => true,'label' => false])
->add('description', TextareaType::class,['label' => false, 'required' => false])
// City ajax call to be fix
->add('location', EntityType::class, ['label' => false,
'class' => 'PagesBundle:City',
'choice_label' => 'zone']);
Thanks all
Nico
Well, I don't think it is a hard task. Actually it's seems somewhat obvious (at least for me though).
You could to use something like TabView or Accordion-like view. All of this can be achieved by using pure CSS (and maybe Javascript).
As you can see it is not related to Symfony at all. By using CSS + Media-queries I'm sure you can get done the desired UI.
Without mobile subdomain and with css help only
When you use FormBuilder::add() you create an instance of FormType which can be :
the entire form,
a field,
a sub form.
So they all behave the same way.
Then you can easily split your FormType in 3 SubFormTypes :
namespace AppBundle\Form;
/*
* When forms get heavy, it becomes handy to use aliases
* on import use statements since it eases the reading in code
* and reduces the list of imports.
*/
use Type as AppType;
use Symfony\Bridge\Doctrine\Form\Type as OrmType;
use Symfony\Component\Form\Extension\Core\Type as CoreType;
// An abstract class which all sub form will extend.
abstract class AbstractSubFormType extends AbstractType
{
/*
* We need to add a submit button on the sub form
* which will be only visible on mobile screens.
*
* Then to get help of css class :
* use ".submit-sub_owner { display: 'none'; }" for desktop screens,
* and ".submit-sub_owner { display: 'block'; }" for mobile screens.
*
* Plus we need dynamic hiding of next sub forms from controller :
* use ".sub_owner-next { display: 'none'; }" for mobile screens.
*/
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
$builder->add('submit', CoreType\SubmitType::class,
'attr' => array(
// Needed to target sub owner controller.
'formaction' => $options['sub_action'],
// Hides sub owners submit button on big screens.
'class' => 'submit_sub_owner',
)
));
}
public function configureOptions(OptionsResolver $resolver)
{
$subNextNormalizer = function (Options $options, $subNext) {
$hideNextSubOwners = isset($options['sub_next']) && $subNext;
// Sets "attr" option of this sub owner type.
$options['attr']['class'] = $hideNextSubOwners ? 'sub_owner-next' : '';
}
// Allows custom options.
$resolver->setDefaults(array('sub_action' => '', 'sub_next' => false));
// Hides sub owners exept the first stage from main controller.
$resolver->setNormalizer('sub_next', $subNextNormalizer);
}
}
// Stage 1
class SubOwnerOne extends AppType\AbstractSubFormType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
// Arbitrary cut of your example
$builder
->add('corporate', AppType\CorporateType::class)
->add('type', AppType\TypeType::class)
->add('room', AppType\NbRoomType::class);
// Call parent to optionnaly add sumbit button
parent::builForm($builder, $options);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array('expanded' => true, 'multiple' => false));
}
public function getName()
{
return 'sub_owner_1';
}
}
// Stage 2
// note that this one is tricky because there is an image that may be a file.
class SubOwnerTwo extends AppType\AbstractSubFormType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
// Arbitrary cut of your example
$builder
//->add('postcode')
->add('area')
->add('rent')
->add('images', CoreType\CollectionType::class, array(
'entry_type' => AppType\ImagesFlatType::class,
'allow_add' => true,
'allow_delete' => true,
'required' => false,
));
// Call parent to optionnaly add sumbit button
parent::builForm($builder, $options);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('data_class', CoreType\NumberType::class);
}
public function getName()
{
return 'sub_owner_2';
}
}
// Last stage
class SubOwnerThree extends AppType\AbstractSubFormType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
// Arbitrary cut of your example
$builder
->add('fee', CoreType\NumberType::class, array('required' => false))
// to be defined in list by city
->add('transport', CoreType\TextType::class, array('required' => false))
->add('furnished', AppType\FurnishedType::class) // define options in class
->add('vip', AppType\LesVipType::class) // when belongs to AppType
->add('feature', AppType\FeatureType::class) // ...
->add('description', CoreType\TextareaType::class, array('required' => false))
// City ajax call to be fix
->add('location', OrmType\EntityType::class, array(
'class' => 'PagesBundle:City',
'choice_label' => 'zone',
));
// Call parent to optionnaly add sumbit button
parent::builForm($builder, $options);
}
public function getName()
{
return 'sub_owner_3';
}
}
One FormType to wrap them all :
class OwnerType extends AbstractType
{
public function createForm(FormBuilderInterface $builder, array $options = array())
{
$sub = isset($option['sub_action']) ? $options['sub_action'] : false;
$next = isset($option['sub_next']) ? $options['sub_next'] : false;
$builder
->add('stage_one', AppType\SubOwnerOne::class, array(
'sub_action' => $sub, // get form action from controllers.
))
->add('stage_two', AppType\SubOwnerTwo::class, array(
'sub_action' => $sub,
'sub_next' => $next, // hide sub owners from main controller on mobile screens.
))
->add('final', AppType\SubFormTypeThree::class, array(
'sub_action' => $sub,
'sub_next' => $next,
))
->add('submit', CoreType\SubmitType::class, array(
'attr' => array(
// Allows using ".submit-owner { display: 'none'; }" for mobile screens.
'class' => 'submit-owner',
),
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'label' => false,
// Allows custom options
'sub_action' => '',
'sub_next' => false,
));
}
// ...
}
Controllers :
// Holds two controllers, one for owner type and another for sub owner types.
class OwnerController extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
/*
* Main controller.
*
* This handles initial request
* and renders a view with a form view of OwnerType
*/
public function ownerRegisterAction(Request $request)
{
$owner = new Owner();
// Customizes options
$form = $this->createFormBuilder($owner, array(
// Uses route name of the other controller
'sub_action' => $this->generateUrl('handle_sub_owners'),
// Hides next stages
'sub_next' => true,
))->getForm();
$form->handleRequest($request);
if($form->isSubmitted && $form->isValid()) {
$password = $this->get('security.password_encoder')
->encodePassword($owner->getOwner(), $owner->getOwner()->getPassword());
$owner->getOwner()->setPassword($password);
$owner->getOwner()->setStatus('owner');
$owner->getOwner()->setIsValid(0);
$em = $this->getDoctrine()->getManager();
$em->persist($owner);
$em->flush();
// Login users after registration
$this->get('apx_auth_after_register')->authenticateUser($tenant->getTenant());
// Forwards quiting registration process
return $this->forward('PagesBundle:SearchOwner:search', array('owner' => $owner));
}
// This view should use css rules from above comments
return $this->render('::form/owner-register.html.twig', array(
'form' => $form->createView(),
));
}
/**
* Secondary controller handles sub forms
* and renders one form view among the sub forms.
*
* #Route('/_sub_owners', name="handle_sub_owners")
*/
public function handleSubOwnersAction(Request $request)
{
// Customize sub form action
$action = array('sub_action' => $this->generateUrl('handle_sub_owners'));
// Option "sub_next" will default to false.
$form = $this->createForm(AppType\OwnerType::class, new Owner(), $action);
$form->handleRequest($request);
$subOwner1 = $form->get('stage_one');
$subOwner2 = $form->get('stage_two');
$finalOwner = $form->get('final');
// Last stage is done, reforwards to the main controller.
if ($finalOwner->isSubmitted() && $finalOwner->isValid()) {
// Submits $data to new OwnerType as $form has been submitted by "handleRequest()" call
$owner = $this->createForm(AppType\OwnerType::class, new Owner());
$owner->get('stage_one')->submit(json_decode($finalOwner->get('j_stage_one')->getData()));
$owner->get('stage_two')->submit(json_decode($finalOwner->get('j_stage_two')->getData()));
$owner->get('final')->submit($finalOwner->getData());
// Form in main controller will handle the request again,
// so we need to pass normalized data.
return $this->forward('App:Owner:ownerRegister', array(), $owner->getNormData())
}
// Stage 2 is done
if ($subOwner2->isSubmitted() && $subOwner2->isValid()) {
// Gets back json of stage 1
$finalOwner->add('j_stage_one', 'hidden', array(
// Unmaps this hidden field as it won't match any property of Owner
'mapped' => false,
'data' => $subOwner1->get('j_stage_1')->getData(),
));
// Saves json of stage 2
$finalOwner->add('j_stage_two', 'hidden', array(
'mapped' => false,
'data' => json_encode($subOwner2->getData(), true),
));
// Renders final stage
return $this->render('::form/owner-register.html.twig', array(
'form' => $finalOwner->createView(),
));
}
// Stage 1 is done
if ($subOwner1->isSubmitted() && $subOwner1->isValid()) {
// Save json of $subOwner1
$subOwner2->add('j_stage_one', 'hidden', array(
'mapped' => false,
'data' => json_encode($subOwner1->getData(), true),
));
// Render stage 2
return $this->render('::form/owner-register.html.twig', array(
'form' => $subOwner2->createView(),
));
}
// Else renders stage 1
return $this->render('::form/owner-register.html.twig', array(
'form' => $subOwner1->createView(),
));
}
}
View
{# ::form/owner-register.html.twig #}
...
<style type="text/css">
/* #media mobile screens */
.sub_owner-next, .submit-owner { display: 'none'; }
.submit-sub_owner { display: 'block'; }
/* #media desktop screens */
.submit-sub_owner { display: 'none'; }
</style>
...
{{ form_start(form) }}
{% form sub_form in form %}
{{ form_start(sub_form) }}
{{ form_widget(sub_form) }}
{{ form_end(sub_form) }}
{% endear %}
{{ form_end(form) }}
...
Alternative use Mobile Detection
There are many php libraries to detect mobile browsers.
For example https://github.com/serbanghita/Mobile-Detect
which provides a bundle for symfony :
https://github.com/suncat2000/MobileDetectBundle
So you can even use a helper in a previous implementation :
$mobileDetector = $this->get('mobile_detect.mobile_detector');
$mobileDetector->isMobile();
$mobileDetector->isTablet()
If you want to split form using responsive way, you should format form output in your view (e.g. TWIG file). Place form parts in separate containers and then use CSS media queries and JS. Good luck!
I did something a few years back in which a passed a 'step' var into the formType which I incremented after each post.
I used an if statement in the formType to build the form depending on the step value.

Cant set default data for choice field when using EventListener on Form field

I have a form with dynamic choice field "languages". The languages can be diffrent for diffrent categories. I want them tho be all checked by default, so I use 'data' option for this. But it only works if I dont use Events. If I try to use it in my $formModifier function to add languages dynamically it will not work, and all languages are unchecked... Any idea how to fix it ?
Here is my buildForm function from ProjectType class
public function buildForm(FormBuilderInterface $builder, array $options)
{
(...)
$builder
->add('category', 'choice', array(
'choices' => Category::$NAMES,
'multiple' => false,
'placeholder' => '',
))
->add('name', 'text')
->add('description', 'textarea', array(
'attr' => array('rows' => '10', 'class' => 'tinymce'),
))
->add('save', 'submit', array('label' => 'form.save'));
$formModifier = function (FormInterface $form, User $user, $category = null) {
$languages = null === $category ? array() : $this->userReputation->getAllowedLanguages($user, $category);
$form->add('languages', 'choice', array(
'multiple' => true,
'expanded' => true,
'choices' => $languages,
'data' => array_keys($languages),
));
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier, $user) {
$project = $event->getData();
$formModifier($event->getForm(), $user, $project->getCategory());
}
);
$builder->get('category')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier, $user) {
$category = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $user, $category);
}
);
}
I have this script in view to make ajax calls when category is changed by the user:
<script>
function requireReadyFunction() {
var $category = $('#project_category');
// When category gets selected ...
$category.change(function() {
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected category value.
var data = {};
data[$category.attr('name')] = $category.val();
// Submit data via AJAX to the form's action path.
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(html) {
// Replace current languages field ...
$('#project_languages').replaceWith(
$(html).find('#project_languages')
);
}
});
});
}
</script>

symfony2 neither property nor method exists in class

One route on click on button is for this controller:
public function addAction(Request $request){
$em = $this->getDoctrine()->getManager();
$spotEntity = $this->getCurrentSpot();
$permitsidrand = rand(0, 1000000000000);
$currentDate = new DateTime();
$permitsrepo = new Permits();
$permitsrepo->setCreatedat($currentDate);
$permitsrepo->setPermitid($permitsidrand);
$permitsrepo->setPermitsSpot($spotEntity);
$em->persist($permitsrepo);
$em->flush();
return $this->redirect($this->generateUrl('permits_add', array('id' => $permitsrepo->getId())));
}
So I want to make new Object and fill it with couple variables and after it I want to redirect to screen with form that will be updating my record from database which I just added.
Here is second function (this one that I am redirecting to after click in button)
public function addfullAction(Request $request, $id){
$permitsidrand = rand(0, 1000000000000);
$currentDate = new DateTime();
$permitsrepo = $this->getDoctrine()->getRepository('MainCoreBundle:Permits');
$perm= $permitsrepo->find($id);
$form = $this->createForm(new PermitsType(), $permitsrepo);
$permitsrepo->setCreatedat($currentDate);
$permitsrepo->setPermitid($permitsidrand);
$permitsrepo->setPermitsSpot($spotEntity);
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($permitsrepo);
$em->flush();
return $this->redirect($this->generateUrl('permits_show'));
}
}
return $this->render('MainAdminBundle:Permits:add.html.twig', $this->getViewConstants(array(
'form' => $form->createView(),
'rand' =>$permitsidrand
)));
}
And when I click that Button that I mantion I have this error message:
Neither property "PermitsContractor" nor method "getPermitsContractor()" nor method "isPermitsContractor()" exists in class "Main\CoreBundle\Entity\PermitsRepository"
Here is my form
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('PermitsContractor', 'entity', array(
'class' => 'MainCoreBundle:Generalcontractor',
'multiple' => false,
'expanded' => false,
'property'=>'name',
'label'=> 'Generalny wykonawca',
));
$builder->add('PermitsCompany', 'entity', array(
'class' => 'MainCoreBundle:Company',
'multiple' => false,
'expanded' => false,
'property'=>'name',
'label'=> 'Firma',
));
$builder->add('Permitname', 'text',array('label'=> "Imię", 'required'=>false));
$builder->add('Permitsurname', 'text',array('label'=> "Nazwisko", 'required'=>false));
$builder->add('expirationdate', 'date', array(
'widget' => 'single_text',
'label'=> 'Data ważności',
));
$builder->add('file', 'file', array('required'=>false, 'label'=>'Przeglądaj'));
}
public function getName() {
return 'main_admin_permits_type';
}
I checkout and I got getters and setters in Entities
The error is here
$perm = $permitsrepo->find($id);
$permitsrepo->setCreatedat($currentDate);
$permitsrepo->setPermitid($permitsidrand);
$permitsrepo->setPermitsSpot($spotEntity);
Use
$perm = $permitsrepo->find($id);
$perm->setCreatedat($currentDate);
$perm->setPermitid($permitsidrand);
$perm->setPermitsSpot($spotEntity);

How to render a choice field in a template

I use Symfony2 and twig as template engine.
In my controller I have the following form:
$usr= $this->get('security.context')->getToken()->getUser();
$associatedmissions = array($usr->getMissions());
$form = $this->createFormBuilder($product)
->add('mission', 'choice', array(
'choices' => $associatedmission,
'multiple' => false,
'expanded' => true, ))
But when I call the page, appears an error:
Catchable Fatal Error: Object of class Doctrine\ORM\PersistentCollection could not be
converted to string in
C:\BitNami\wampstack-5.4.23-
0\frameworks\symfony\vendor\symfony\symfony\src\Symfony\Component\Translation\
IdentityTranslator.php line 65
In the profiler I can see the error:
CRITICAL - Uncaught PHP Exception Twig_Error_Runtime: "An exception has been thrown
during the rendering of a template ("") in "form_div_layout.html.twig" at line 99." at
C:\BitNami\wampstack-5.4.23-0\frameworks\symfony\app\cache\dev\classes.php line 4372
Context: {"exception":"Object(Twig_Error_Runtime)"}
What happened?
It's strange because with it works properly
->add('mission', 'entity')
-UPDATE--
Here my controller:
public function createAction(Request $request)
{
$product = new Product();
$usr= $this->get('security.context')->getToken()->getUser();
$associatedmissions = array($usr->getMissions());
$form = $this->createFormBuilder($product)
->add('name', 'text')
->add('mission', 'choice', array(
'choices' => $associatedmissions,
'multiple' => false,
'expanded' => false, ))
->add('save', 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
return $this->render('AcmeGroundStationBundle:Product:tasksuccess.html.twig', array('product' => $product));
}
return $this->render('AcmeGroundStationBundle:Product:formupload.html.twig', array(
'form' => $form->createView()
));
}
aYou have this in your controller:
$usr= $this->get('security.context')->getToken()->getUser();
$associatedmissions = array($usr->getMissions());
$usr->getMissions() is returning ArrayCollection (based on relations in your Entity). This way $associatedmissions is an array with one element - [1] = ArrayCollection of related Mission Objects.
In my opinion you must add to your User entity method:
public function getMissionsArray()
{
$missionArray = array();
foreach ($this->missions as $key => $val)
{
$missionArray[$key] = $val->__toString();
}
return $missionArray;
}
And use array as choices param in your createFormBuilder statement.
When you validate your Form you have to create Mission object for every selection before persist product.
$this->getDoctrine()
->getRepository('AcmeGroundStationBundle:Mission')
->find($product->mission);
EDIT
// User Entity
// add this function - will return array of ( id => mission.name, )
public function getMissionsArray()
{
$missionArray = array();
foreach ($this->missions as $key => $val)
{
// change this to whatever human readable getter
$missionArray[$key] = $val->__toString();
}
return $missionArray;
}
// Controller
public function createAction(Request $request)
{
$product = new Product();
$usr= $this->get('security.context')->getToken()->getUser();
$associatedmissions = $usr->getMissionsArray();
$form = $this->createFormBuilder($product)
->add('name', 'text')
->add('mission', 'choice', array(
'choices' => $associatedmissions,
'multiple' => false,
'expanded' => false, ))
->add('save', 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
// save selected array item as a Mission object related to Product
$product->setMission($this->getDoctrine()
// check if Mission Entity is placed in good bundle ;)
->getRepository('AcmeGroundStationBundle:Mission')
->find($form->get('mission')->getData()));
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
return $this->render('AcmeGroundStationBundle:Product:tasksuccess.html.twig', array('product' => $product));
}
return $this->render('AcmeGroundStationBundle:Product:formupload.html.twig', array(
'form' => $form->createView()
));
}
Good Luck!
Try to use redirect. Is it work?
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
return $this->redirect($this->generatePath('current_route_name'));
}
return $this->render('AcmeGroundStationBundle:Product:formupload.html.twig', array(
'form' => $form->createView()
));
Where current_route_name is replace by your route name of current page.

Symfony 2 - How to validate Entity Field Type when it is populated using Ajax?

I am confronted to a problem that is driving me crazy for more than 3 days and I do not find any solutions. Nevertheless I found a post on stackoverflow that is EXCACTLY the problem I am facing. Unfortunately the person did manage to find a solution on his down but he or she did not shared it fully. As he explained it perfectly let just copy paste it here below:
By the way it seems that person who created that post only created his account for this problem and never came back since for other things. That it is why I allow myself to ask here again...
I have 2 entities (A and B) with a Many to One relationship between
them.
I create my form with the A entity and i use an entity field (dropdown
list) to display the rows in the B entity. I use a query builder to
filter them. If don't change the values in the list (ie. with ajax),
everything is working fine.
But if I change dynamicly the values in the dropdown, when I submit
the form I have this error "This value is invalid"
It's because the submitted value isn't included in the "array"
returned by the query builder.
It seems that this validation is automatic in symfony for entity field
(I don't use any asserts on this field). I'd like to get rid of this.
But how ?
It seems that I need to implement Form Events. Unfortunatally I do not get it. I read the documentation which is very poor on that subject, read a lot of posts, searched on the Internet but did not found anything.
Here below my personal form type. What I do is the following. I create the first entity field type with the mapped property set to false and filter the entity just to get the departements. Then I create another entity type called localisation. By default I filter the entity to get nothing (''). What I do then to populate it is to use Jquery. But unfortunatelly I am confro,ted to the same problem as the other buddy (see above).
use Symfony\Component\Form\FormBuilderInterface;
use FOS\UserBundle\Form\Type\RegistrationFormType as BaseType;
use Doctrine\ORM\EntityRepository;
use Auth\GeoBundle\Form\LocalisationType;
class RegistrationFormType extends BaseType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder->add('departement', 'entity', array(
'mapped' => false,
'empty_value' => '',
'class' => 'AuthGeoBundle:Localisation',
'property' => 'departement',
'query_builder' => function ($repository) {
return $repository
->createQueryBuilder('e')
->add('groupBy', 'e.departement')
;
},
));
$builder->add('localisation', 'entity', array(
'empty_value' => '',
'class' => 'AuthGeoBundle:Localisation',
'property' => 'formLabel',
'query_builder' => function ($repository) use ($dpt) {
return $repository
->createQueryBuilder('e')
->where('e.departement = :dpt')
->setParameter('dpt', '')
->add('orderBy', 'e.ville ASC')
;
},
));
//some other fields here...
}
public function getName()
{
return 'auth_user_registration';
}
}
I finally manage to find a solution using the form events. I played with the "tutorial" at http://symfony.com/doc/current/cookbook/form/dynamic_form_generation.html and got it working :) Here below the code I used in case somebody interested.
My formType:
<?php
//src/Auth/UserBundle/Form/Type/RegistrationFormType.php
namespace Auth\UserBundle\Form\Type;
use Symfony\Component\Form\FormBuilderInterface;
use FOS\UserBundle\Form\Type\RegistrationFormType as BaseType;
use Doctrine\ORM\EntityRepository;
use Auth\GeoBundle\Form\LocalisationType;
use Auth\UserBundle\Form\EventListener\IsAdminFieldSubscriber;
class RegistrationFormType extends BaseType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
//NE PAS EFFACER -> exempled e comment ajouter un champ qui n'a rien à voir avec nos entitys
//$builder->add("firstName", "text", array("mapped" => false));
$builder->add('departement', 'genemu_jqueryselect2_entity', array(
'mapped' => false,
'empty_value' => '',
'class' => 'AuthGeoBundle:Localisation',
'property' => 'departement',
'query_builder' => function ($repository) {
return $repository
->createQueryBuilder('e')
->add('groupBy', 'e.departement')
;
},
));
$dpt = "";
$builder->add('localisation', 'genemu_jqueryselect2_entity', array(
'empty_value' => '',
'class' => 'AuthGeoBundle:Localisation',
'property' => 'formLabel',
'query_builder' => function ($repository) use ($dpt) {
return $repository
->createQueryBuilder('e')
->where('e.departement = :dpt')
->setParameter('dpt', $dpt)
->add('orderBy', 'e.ville ASC')
;
},
));
$builder->add('sexe', 'genemu_jqueryselect2_choice', array(
'empty_value' => '',
'choices' => array(
'homme' => 'Homme',
'femme' => 'Femme',
),
'configs' => array(
'minimumResultsForSearch' => 5,
)
));
$builder->add('date_naissance', 'date', array(
'empty_value' => '',
'widget' => 'choice',
'attr' => array('class' => 'input-small'),
'years' => range(1900,2100),
'months' => range(1,12),
'days' => range(1,31),
));
$builder->add('petit_mot');
$subscriber = new IsAdminFieldSubscriber($builder->getFormFactory());
$builder->addEventSubscriber($subscriber);
}
public function getName()
{
return 'auth_user_registration';
}
}
my EventListener:
<?php
//src/Auth/UserBundle/Form/EventListener/isAdminFieldSubscriber.php
namespace Auth\UserBundle\Form\EventListener;
use Symfony\Component\Form\Event\DataEvent;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvents;
class IsAdminFieldSubscriber implements EventSubscriberInterface
{
/**
* #var FormFactoryInterface
*/
private $factory;
/**
* #param FormFactoryInterface $factory
*/
public function __construct(FormFactoryInterface $factory)
{
$this->factory = $factory;
}
/**
* #return array
*/
public static function getSubscribedEvents()
{
return array(
FormEvents::PRE_BIND => 'preBind',
);
}
/**
* Called before form data is set
*
* #param DataEvent $event
*/
public function preBind(DataEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
if (null === $data) {
return;
}
$dpt = $data['localisation'];
$form->add($this->factory->createNamed('localisation', 'entity', null, array(
'empty_value' => '',
'class' => 'AuthGeoBundle:Localisation',
'property' => 'formLabel',
'query_builder' => function ($repository) use ($dpt) {
return $repository
->createQueryBuilder('e')
->where('e.id = :dpt_id')
->setParameter('dpt_id', $dpt)
->add('orderBy', 'e.ville ASC')
;
},
)));
}
}
You explain the problem yourself:
"It's because the submitted value isn't included in the "array" returned by the query builder."
You can use data transformers to solve this issue.