Symfony 2 - add options into 'Choice' field after form is created - php

Can we edit the possible options for a choice field after the field has been created?
Let's say, the possible options for the choice field(a drop down box for categories) comes from my database. My controller would look like this:
public function addAction(Request $request){
//get the form
$categories = $this->service->getDataFromDatabase();
$form = $this->formFactory->create(new CategoryType(), $categories);
$form->handleRequest($request);
if ($form->isValid()) {
// perform some action, such as saving the task to the database, redirect
}
return $this->templating->renderResponse('TestAdminBundle:Categories:add.html.twig',
array('form' => $form->createView())
);
}
This works. $categories is populated as a dropdown box so the user can select a category. What I don't like about this code is that it has to hit the "getDataFromDatabase" service again when the user hits submit and the form validates the input. This feels unnecessary to me; ideally it should only need to hit the service when validation fails and the form has to be regenerated for the user. I'm hoping to make the controller look something like this:
public function addAction(Request $request){
//get the form
$form = $this->formFactory->create(new CategoryType());
$form->handleRequest($request);
if ($form->isValid()) {
// perform some action, such as saving the task to the database, redirect
}
$categories = $this->service->getDataFromDatabase();
$form->setData($categories); //this tells the choice field to use $categories to populate the options
return $this->templating->renderResponse('TestAdminBundle:Categories:add.html.twig',
array('form' => $form->createView())
);
}

You need to use EventSubscriber, check the docs, here: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-underlying-data

Related

How to loop form/new entity in Symfony

I'm having an issue with looping a form in Symfony using while loop. When the user enters one student period and it matches a registration reiterate the form to let them enter a 2nd student period and then a 3rd. I'm not doing it correctly or could I reiterate entity=new Student(); to let them enter two entities .
public function createAction(Request $request){
$entity = new Student();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$student = $em->getRepository('AcmeDemoBundle:Payrollperiod')
->findOneBy([
'begindate'=>$form->get('beginDate')->getData(),
'lastdate'=>$form->get('lastDate')->getData()
]);
$registration = $em->getRepository('AcmeDemoBundle:Payrollweek')
->findBystartdateAndenddate(
$form->get('beginDate')->getData(),
$form->get('lastDate')->getData()
);
$counter = count($registration);
while($counter<=2) {
if ($student){
$this->addFlash('error', 'Duplicate error: Student Period already existed.' );
return $this->redirect($this->generateUrl('student'));
}
elseif ($registration){
foreach ($registration as $reg) {
$reg->setStudentid($entity);
}
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('payrollperiod'));
}
else{
$this->addFlash('error', ' does not match .');
return $this->redirect($this->generateUrl('student'));
}
}
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
}
Read the documentation here http://symfony.com/doc/current/reference/forms/types/form.html#allow-extra-fields on adding fields. Your UI/Front-end should created more fields when the user needs to add a second or more periods. I should generate proper Symfony form elements and then when you post, you can handle it like a standard form post, iterating over the Request since the periods will be submitted as an array.
I did something like this in the past I adding a new form row was an AJAX call to a template that described the form input fields so I wasn't having to craft form HTML in Javascript/jQuery. .append() the template results to the form-- post and process the request.

How to use nested form handlers in Symfony 3

I'm learning Symfony to move an old website from flat-PHP to a framework.
In the old website I have a page with two levels of forms: the first one is just a button with which the user accepts some conditions, while the second one sends a message to the website admin. When the user clicks the button in the first form, the page is refreshed and the second form appears. The important thing is that the user can't get access to the second form without pushing the first button.
Right now, the action method is this one:
/**
* #Route("/ask-consultation", name="ask_consultation")
*/
public function askConsultationAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$form = $form = $this->createFormBuilder()
->add('submit', SubmitType::class, array('label' => 'Confermo'))
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$consultation = new Consultation;
$form = $this->createForm(AskConsultationType::class, $consultation);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$consultation = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($consultation);
$em->flush();
return $this->redirectToRoute('homepage');
}
return $this->render('visitor/consultation/ask_step2.html.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'sidebars' => $em->getRepository('AppBundle:Sidebar')->findAllOrderedBySortingPosition(),
'form' => $form->createView(),
]);
}
When I go to /ask-consultation it shows me the button to accept certain conditions; when I click the button, it shows me the form to send the message, but when I send it, I don't get redirected to homepage, but again to the first page of /ask-consultation.
I understand why this code doesn't work, but I can't understand how to make it work. One solution could be some sort of modal dialog for the first form, but if possible I'd prefer to handle all the passages in PHP. Is it possible to split the form handling without changing the route?
The most important thing in my case is that the user can't get to the second form without first having clicked on the first button.
Render everything in a single form but use javascript (jQuery) to hide the ask-consultation part of the form. When the user clicks "Confirm", then unhide the form. This avoids the controller altogether for the confirmation step. You don't seem to be recording this confirmation anyway, so just let the client side handle things. You can still check (on form submission) to see that the "confirmation" was accepted, for example by setting a hidden variable through javascript.
It turns out the answer was quite simple.
In my mind, I had to handle the second form inside the first if ($form->isSubmitted() && $form->isValid()), but of course that's not the path taken by PHP. Everytime the route gets loaded, askConsultationAction() is run from its first instruction. I simply had to initialize both forms (with different variable names, obviously), and handle the forms one after the other, rather than one inside the other.
This code works like a charm:
/**
* #Route("/ask-consultation", name="ask_consultation")
*/
public function askConsultationAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$consultation = new Consultation;
$conditions_form = $this->createFormBuilder()
->add('submit', SubmitType::class, array('label' => 'Confermo'))
->getForm();
$consultation_form = $this->createForm(AskConsultationType::class, $consultation);
$consultation_form->handleRequest($request);
$conditions_form->handleRequest($request);
if ($conditions_form->isSubmitted() && $conditions_form->isValid()) {
return $this->render('visitor/consultation/ask_step2.html.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'sidebars' => $em->getRepository('AppBundle:Sidebar')->findAllOrderedBySortingPosition(),
'form' => $consultation_form->createView(),
]);
}
if ($consultation_form->isSubmitted() && $consultation_form->isValid()) {
$consultation = $consultation_form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($consultation);
$em->flush();
return $this->redirectToRoute('homepage');
}
return $this->render('visitor/consultation/ask_step1.html.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'sidebars' => $em->getRepository('AppBundle:Sidebar')->findAllOrderedBySortingPosition(),
'form' => $conditions_form->createView(),
]);
}
Maybe, it would be even better if I used an else... if....

Yii2 required validation on update

I've Yii2 form containing form fields depending on action of page. Ex. Few fields appears when then action is create and few appears when action is update. I want to add required validation based on this scenario.
Ex.
<?= $form->field($model, 'unique_identifier')->textInput(['maxlength' => 45]) ?>
I am showing this field only when action => 'update'.
Now I want to add required validation for this and I tried this:
[['unique_identifier'], 'required', 'on' => 'update'],
But above validation not working. If I remove on=>update then its validating on both create and update scenario.
Any help would be appreciated.
ActiveRecord does not set scenario automaticaly when you update or create items. You must override update() method in your model and set scenario that you need. E.g. in your case
public function update($runValidation = true, $attributeNames = null)
{
$this->scenario = 'update';
return parent::update($runValidation, $attributeNames);
}
Also you can set scenario in your actionUpdate
public function actionUpdate($id)
{
$model = $this->findModel($id);
$model->scenario = 'update';
//load data from request, save model etc.
}

How to populate data if zend form is not validated?

module.config contains form, that is injected into controller
'passwordForm' => function($sm){
$form = new \Application\Form\PasswordForm();
$form->setInputFilter(new \Application\Form\PasswordInputFilter());
return $form;
},
Controller:
if($this->getRequest()->isPost()){
$form->setData($this->getRequest()->getPost());
if($form->isValid()){
//ok
}
}
return array('form' => $form);
However, if the form is not validated, I see empty fields at form view <?=$this->formRow($this->form->get('passwordOld'));?>. If I echo its value, I see it displayed: <?php var_dump($this->form->get('passwordOld')->getValue());?>
How can I make visible values of not validated form? The key point is that the form is not binded to any object.
Password form element is intentionally done in such way for security reasons.
You must never (re)populate form with passwords.

How to pass variable from action to form

I am sure I am going about this the wrong way, but I need to unset an array key from one of my choices in a sfWidgetFormChoice. The only way to get that variable to the Form is from the action. Here's what I have:
Action:
$id = $request->getParameter('id');
$deleteForm = new UserDeleteForm();
$choices = array();
$choices = $deleteForm->getWidgetSchema('user')->getAttribute('choices');
unset($choices[$id]); //I obviously don't want the user to be able to transfer to the user being deleted
$this->deleteForm = $deleteForm;
Form:
$users = Doctrine_Core::getTable('sfGuardUser')->getAllCorpUsers()->execute();
$names = array();
foreach($users as $userValue){
$names[$userValue->getId()] = $userValue->getProfile()->getFullName();
};
// unset($names[$id]); //this works, but I can't figure out how to get $id here.
$this->widgetSchema['user'] = new sfWidgetFormChoice(array(
'choices' => $names
));
$this->validatorSchema['user'] = new sfValidatorChoice(array(
'required' => true,
'choices' => $names
));
Understanding forms and actions:
Usually we will setup a form with fields, print it in a html page and fill the form with data. Pressing the submit form button will send all the data to a method defined in your form action html attribute.
The method will receive and get a $request , with a lot of parameters and also the form with the data. Those values will be processed in the action.
Lets look how it exactly works in symfony:
Define and Setup a symfony form, like the one you have shown above.
Print the form and in the action parameter point to the submit method
which will receive the request:
<form action="currentModuleName/update"
Symfony will automatically send the request to the action.class.php
of your module, and will look for and send the data to the function
executeUpdate
public function executeUpdate(sfWebRequest $request){ //...
$this->form = new TestForm($doctrine_record_found);
$this->processForm($request, $this->form); }
After some checks, symfony will process the form and set a result
template.
processForm(sfWebRequest $request, sfForm $form)
{ ... } $this->setTemplate('edit');
In the processForm of your module action.class.php, you should process all the received values (request) also with the form:
protected function processForm(sfWebRequest $request, sfForm $form)
{
$form->bind($request->getParameter($form->getName()), $request->getFiles($form->getName()));
if ($form->isValid())
{
$formValues = $this->form->getValues();
$Id = $formValues['yourWidgetName'];
}
}
You may check the following link for an example like yours, about how to process a sfWidgetFormChoice.
And now answering to the real question, in order to select the deleted users, add the following code in your action:
//process the form, bind and validate it, then get the values.
$formValues = form->getValues();
$choicesId = $formValues['choices'];
Pass variable from action to the form:
Excuse me if I have not understand your question at all but in case you need to pass some parameters from your action to the form, send the initialization variables in an array to the form constructor:
Pass a variable to a Symfony Form
In your case, get the list of users, delete the user you dont want and send the non deleted users to the form constructor.
You will need to redeclare/overwrite your form again in the configure() function so that you could change the initialization of the form. Copy and paste the same code into the configure() function and comment the line: //parent::setup();
class TbTestForm extends BaseTbTestForm
{
public function configure()
{
//.. copy here the code from BaseTbTestForm
//parent::setup();
$vusers = $this->getOption('array_nondeleted_users');
//now set the widget values with the updated user array.
}
}

Categories