I need to create a form where the elements (texbox, select, ..) will be dynamically inserted. Right now I have created a empty Form file with just a hidden element and them in my controller I go inserting elements according to certain conditions.
My form file:
class Form_Questions extends Zend_Form {
public function __construct() {
parent::__construct($options);
$this->setName('Questions');
// Hidden Label for error output
$hiddenlabel = new Zend_Form_Element_Hidden('hiddenlabel');
$hiddenlabel->addDecorator(new Form_Decorator_HiddenLabel());
$this->addElements( array($hiddenlabel) );
}
}
In the controller I have something like:
...
$form = new Form_Questions();
$request = $this->getRequest();
if ($request->isPost())
{
$formData = $request->getPost();
if ($form->isValid($request->getPost()))
{
die(var_dump($form->getValues()));
}
}
else
{
//... add textbox, checkbox, ...
// add final submit button
$btn_submit = new Zend_Form_Element_Submit('submit');
$btn_submit->setAttrib('id', 'submitbutton');
$form->addElement($btn_submit);
$this->view->form = $form;
}
The form displays fine but the validation is giving me big trouble. My var_dump() only shows the hidden element that is staticly defined in the Form file. It does not save the dinamic elements so altought I can get them reading what's coming via POST, I can not do something like
$form->getValue('question1');
It behaves like if Zend uses the Form file to store the values when the submit happend, but since the elements are created dinamically they do not persist (either their values) after the post so I can not process them using the standar getValue() way.
I would appreciate any ideas on how to make them "live" til after the post so I can read them as in a normal form.
The form which you are calling isValid() and getValues() methods on is actually your "empty" form - you have instantiated it only a few lines up and haven't added any elements to it at that point.
Remember that POST only sends an array of fieldName => fieldValue type, it doesn't actually send a Zend_Form object.
It is difficult to suggest a new solution without knowing what you are trying to achieve. It is generally better to add all possible elements to your Zend_Form right away, and then only use the ones you need in the view scripts, i.e. echo $this->form->myField;. This will allow isValid() to process all the elements of the form.
It sounds like the form is dynamic in the sense that the questions come from a db, not in then sense that the user modifies the form itself to add new questions.
Assuming this is the case, then I wouldn't add the question fields in the controller. Rather, I'd pass the questions to the form in the constructor and then add the question fields and the validators in the form's init() method. Then in the controller, just standard isPost() and isValid() processing after that.
Or, if you are saying that the questions to be added to the form are somehow a consequence of the hidden label posted, then perhaps you need two forms and two actions: one for the hidden field form and another for the questions.
Ok, the simplest solution I came up with - to my case and considering the really of the code I am currently playing with was to load all the questions I need from the database using a method from my Model (something like fetchQuestions()), them in my controller I go throught the recordset and create the form elements according to the current question of the recordset.
The elements are stacked in an array that is passed to my Form constructor. In the form constructor I read the array and generate all the dynamic elements. I them just echoed the form to the view.
I have not seem why it would be a bad idea to override the Form constructor as I also could not use any of the set/get methods to pass this to my form.
Related
I'm using PHP framework Agile Toolkit version 4.3.2 (latest at this moment). I'm on a page that extends the default Page class.
Suppose I have a form object like:
$form = $this->add('Form');
$form->addField('text', 'name', 'Name');
$form->addSubmit('Save');
How do I get the form object's HTML? I want to send the form's HTML to another template part, something like:
$this->template->trySetHTML('Content', $form);
The function from above works if I use HTML code instead of the $form object.
But in this case when I refresh the page, instead of the form HTML appears a string like: Object Form(22f8a7bc__ancedsearch_form)
I tried: $form->render() or $form->getHTML() but these functions don't work.
So please tell me, how do I render an object in agile toolkit? How can I get the object's HTML code.
Edit
I'm extending the grid layout. For each column I add a search filtering option. I have extended the Grid_Advanced.php in order to be able to customize it. On each column, below the table header (column name), I'm inserting a form with an input (I'm sending the column name field):
$header_col->trySetHTML('advance_search_filter', $form_html);
The $form_html is returned from a file that extends atk4/lib/Filter.php (it's similar to quicksearch). The quicksearch automatically adds the html to the grid, but in my case I need it to be added to the table's head, after the column name. That's why I am trying to get the HTML of the form.
In this file I have the init function that looks something like:
public function init()
{
parent::init();
$this->addClass('grid-extended-search atk-box ui-widget ui-widget-content');
$this->default_controller = 'Controller_..._MVCForm';
$this->template->trySet('fieldset', 'atk-row');
$this->bs = $this->addSubmit('Search');
$this->save = $this->bs;
}
But this doesn't return nothing so I created a function to return template's HTML. But the form was empty, so I recreated the fields (which is bad):
$m = $this->view->model
foreach($this->fields as $f) {
$field = $this->view->columns[$f];
if($m->hasField($f)) {
if($field['type'] == 'text') {
$field_html = $this->addField('line', $f, $field['descr']);
$form_html .= $field_html->getInput();
}
$this->template->setHTML('Content', $form_html);
}
}
Any idea? Why the form is empty? I used addField to add the fields to the existing but the fields probably exist. I don't know how to get them, to get the existing form's HTML.
For most views:
$html = $view->getHTML();
However it might be more tricky for a Form.
In the solution that you have described (after edit), it seems that you don't really need all the functionality of the Form
To get HTML of an individual Field:
$field->getInput();
That will give you the "input" element that you can place inside your column headers. You can also use "Form_Plain" to wrap your GRID inside a <form> tag.
You would need to handle submission manually, though.
Let say I have an HTML form with bunch of fields. Some fields belong to Product, some to Order, some to Other. When the form is submitted I want to take that request and then create Symfony forms for Product, Order, and Other in controller. Then I want to take partial form data and bind it with appropriate forms. An example would something like this:
$productArray = array('name'=>$request->get('name'));
$pf = $this->createForm(new \MyBundle\Form\ProductType(), $product);
$pf->bind($productArray);
if($pf->isValid()) {
// submit product data
}
// Do same for Order (but use order data)
// Do same for Other (but use other data)
The thing is when I try to do it, I can't get $form->isValid() method working. It seems that bind() step fails. I have a suspicion that it might have to do with the form token, but I not sure how to fix it. Again, I build my own HTML form in a view (I did not use form_widget(), cause of all complications it would require to merge bunch of FormTypes into one somehow). I just want a very simple way to use basic HTML form together with Symfony form feature set.
Can anyone tell me is this even possible with Symfony and how do I go about doing it?
You need to disable CSRF token to manually bind data.
To do this you can pass the csrf_protection option when creating form object.
Like this:
$pf = $this->createForm(new \MyBundle\Form\ProductType(), $product, array(
'csrf_protection' => false
));
I feel like you might need a form that embed the other forms:
// Main form
$builder
->add('product', new ProductType)
->add('order', new OrderType);
and have an object that contains association to these other objects to which you bind to the request. Like so you just have to bind one object with the request and access embedded object via simple getters.
Am I clear enough?
I'm using Zend Framework 1.12, making a page which have multiple forms in it. I use a single master form and subforms in it. Therefore, I have just one Validation code part.
These subforms point to different tables in database. Aim is that, if there is a row in database about that form, form should take values from database as default, to give user chance of change that data. And if there is not a row in database, input of this form will be inserted to db.
At first, I can take values from db and show them as values of form elements. But when I change it and try to take values with
$form->getValues();
I cannot access the values entered (or edited) in page, I just re-access the values in database which was put in form as default. This form should be able to edit always, and I have multiple forms for different kinds of data, which will do same thing too. What must I be doing wrong ? Any idea?
(addition) here is a summary of the relevant piece of my controller code:
$masterform = new Application_Form_GeneralForm(); // a class which extends Zend_Form
$form1 = new Application_Form_SmallForm(); // a class which extends Zend_Form_Subform
$masterform->addSubform($form1, 'form1');
// so far, for form 1, no problem. My second form will be
// added to the masterform after this first form is submitted,
// which works fine.
$form2 = new Application_Form_AnotherSmallForm(); // a class which extends Zend_Form_Subform
$request = $this->getRequest();
if ($request->isPost()){
if ($generalform->isValid($request->getPost())) {
$form2->loadValues(); // the part that form elements are filled with data
// taken from db, a method defined in `AnotherSmallForm`
// class. Just assigning values to elements with `setValue()`
$form2->saveValues(); // Here is the problem, another method to save the
// current values to db. (defined in form class). I have to do this in this fragment of code, so i don't know to
// use which order ( saveValues() and loadValues() methods' order )`
$masterform->addSubform($form2, 'form2');
}
}
So, 1st step: $form1 is added to $masterform.
2nd step: $masterform submitted (it only includes $form1 now), then $form2 is added to $masterform. before it is added, the values for $form2 is loaded inside form elements.
3rd step: $masterform submitted, (so is $form1 and $form2). If any change of the values in $form2 , they must be updated in db by this submission.
This is goal of this code, which could not be accomplished because of 3rd step.
There you have your problem, after sending the post values, you are rewriting it with default db values.
if ($request->isPost()){
.........................
$form2->loadValues(); // here you're rewriting it!
$form2->saveValues();
simple change the order, first saveValues() then loadValues().
I finally figured it out. Even if subforms are created in conditions respect to each other, they should be created out of the if($request->isPost()) condition block. So, if you have to add them with steps (like I have to do it, $form2 created after $form1 is submitted, and $form1 remains in page), you should test if their requirements are satisfied directly and individually. This approach of mine does not work. It should be like this:
$masterform = new Application_Form_GeneralForm();
$form1 = new Application_Form_SmallForm();
$masterform->addSubform($form1, 'form1');
$form2 = new Application_Form_AnotherSmallForm();
if (/* the requirement you want to check if $form2 should be added or not,
and this could easily be checking some value which is submitted with
$form1, so you have already checked if $form1 has been submitted */ )
$masterform->addSubform($form2, 'form2'); // we add it if requirement is met
$request = $this->getRequest();
if ($request->isPost()){
if ($generalform->isValid($request->getPost())) {
$form2->saveValues(); // be sure it saves the final values added to form
////.......
}
}
and, I stopped having loadValues() here, and I call that method inside the init() method of form class. So, as konradwww said, saveValues() should happen first, and loadValues() next. This could done by using the loadValues() in form class, it belongs to the process of initiation of the form anyway.
I have a form that is supposed to create a very simple new entry into the database. One of the fields in this table is related to another table. Specifically, there is an event that people attend, and I need to assign people to an event. Each event can have several people attending, and there can be several events.
When an admin user adds members to an event, I don't need them selecting the event on the actual form, this should be passed via the URL. e.g.
event/new/id/1
However, I am really struggling as to how to include that ID in the form. If I try and manually set the field value, I get the error Cannot Update Form Fields
e.g.
$this->['attendanceSuccess_id'] = 1;
If I try and hide the field:
$this->widgetSchema['attendanceSuccess_id'] = new sfWidgetFormInputHidden();
The form shows but obviously no value is passed and I get "that fields is required error"
This seems like a really simple and common thing to do, but I can't find any solutions! How can I pass a URL value into a form where the field points to another class?
Here's what I've done. Basically your form holds a hidden with the id, and your action gets it from the request and puts in into the form as an option. In the form you can also set the relationship to the Doctrine objects.
Your form should have something like (I don't know what your form should extend exactly since you are using Doctrine and mine was in Propel)
class SomethingForm extends BaseSomethingForm {
public function __construct($object = null, $options = array(), $CSRFSecret = null)
{
parent::__construct($object, $options, $CSRFSecret);
if (isset($options['person_id']))
{
$this->getObject()->setPersonId($options['person_id']);
$this->setDefault('person_id', $options['person_id']);
}
}
}
public function configure()
{
$this->setWidget('person_id', new sfWidgetFormInputHidden());
...more stuff...
}
Then your action says:
$this->form = new SomethingForm(null, array(
'person_id' => $request->getParameter('person_id')
));
Lets say for example I am creating a an online shop. I have a controller called products and within that controller I have a function called create_product. Create_product calls a view that displays a form where users get to enter new products into the database.
When the user fills in the form to create a product, should I send the action back to the create_product controller and handle it with an IF statement? or offload to another function?
Example
<form method="post" action="www.example.dev/products/create_product/add">
//the above form would post back to the original controller
function create_product()
{
if(uri->segment(3) == "add")
{
//call a model to do all the database stuff
}
load->view->create_product_form;
}
Is this the best way to handle this or should I be passing it off to another function?
Don't cram a ton of stuff in one function using the URI segment to filter it. createProduct() can list the products available for creation (in a CRUD format, I assume), and the submission of the form should ping another controller with the POSTed data. Perhaps insertProduct(), where the data is sanitized and sent to the model for insertion to the database.
Separation of concerns! Keep the functions as separate as possible with good descriptors for the names of the functions.
I (personally) would have a function that set the form parameters and "launch" the view with that form, and another function used to validate and call the model to put the values of that form into the database. I believe that is really up to you, but the code would be cleaner if you divide the controller with several functions depending on what they actually do.
I like the way symfony deals with forms & form submission. It is in one function (action)
simplified code:
executeCreate() {
$this->form = new Form()
if($r->isMethod('POST')) {
//handle submission
bind();
save();
}