Dynamic form (switch entity) symfony2 - php

I'm making a page for adverts. An advert can be of different types and therefore have different data. For instance, a vehicle would have the make and the model as extra data.
Right now, I've got one base doctrine entity Advert which contains the data that every advert requires. Different adverts in turn innherits this data (doctrine2 discriminatormap)
I need to populate the form dynamically (with ajax and symfony2 forms) if the user choose to create a vehicle ad I want to display the options for a vehicle advert. But I also need to change the entity to be of the form AdvertVehicle.
Is this possible? I did read the cookbook entry at the symfony2 homepage
"How to Dynamically Modify Forms Using Form Events":
This should be handled by making an AJAX call back to your application. In that controller, you can submit your form, but instead of processing it, simply use the submitted form to render the updated fields. The response from the AJAX call can then be used to update the view.
I understand how to make an ajax call back to my controller, and i understand how to use the form-events but how do I get the response of a rendered select-box (containing vehicle models for instance) back? With a new AbstractType? or formbuilder?
And then when the user actually submits the form I need to use the entity of the selected advert type. Can I change the entity according to the users choice in the form dynamically?
Edit
I checked the form innheritance out that's great, thank you. I extend the AdvertType and override the buildForm() method and before I add the items I need for the AdvertVehicleType I call the parent method.
Futher Explanation
Every advert entity contains price, description, title and category. Some adverts contains more, such as make and model. They are differentiated by the discriminatormap (doctrine2)
Example:
// -- Entity
class CarAdvert extends Advert {
protected $model;
protected $make;
}
// -- Entity
// -- This uses discriminator mapping
class Advert {
protected $title;
protected $description;
protected $price;
protected $category;
}
if the user selects the category cars I want to use the CarAdvert entity (for validation and persistance) if the user selects the house hold itemcategory I just want to use the normal Advert entity.
One major problem is still that I cannot figure out how to render the extended form via ajax. Any tips on this part? When the user selects car as a category, I want the form to be updated (via jQuery/ajax) but how do I make a controller that retrieves just the extended part of the form and sends the html back as a response (without using twig and rendering it in a view, is this possible)?
Solution:
See answer below!

Solution:
The solution to my problem was to create a few extra functions in the controller to solve the issue where I want to be able to change the entity and form "on the fly" from a selection by the user..
public function indexAction(Request $request)
{
$form = $this->getForm($request);
$form->handleRequest($request);
return array(
'form' => $form->createView(),
'request' => $request->request,
);
}
Where getForm retrieves the form, (e.g AdvertVehicleType for vehicles or AdvertType for a "default" advert).
The getForm method looks like this:
private function getForm(Request $request)
{
$categoryTitle = 'NONE';
$categoryId = $request->request->get('advert', false)['category'];
if ($categoryId) {
$categoryTitle = $this->getDoctrine()->getRepository('Bundle:Category')->find($categoryId)->getTitle();
}
return $this->createForm($this->getFormType($categoryTitle), $this->getEntity($categoryTitle));
}
here I retrieve the categoryID (that is selected in the form in the request) and retreives the formType with getFormTypeand the entity with getEntity.
private function getEntity($categoryTitle)
{
$entity = new Advert();
switch ($categoryTitle) {
case Category::CARS:
$entity = new AdvertCar();
}
return $entity;
}
private function getFormType($categoryTitle)
{
switch ($categoryTitle) {
case Category::CARS:
return new AdvertCarType();
default:
return new AdvertType();
}
}
To be able to update this "on the fly" with ajax (but it also works if the user tries to submit the form) I created another action in the controller.
This action renders the parts of the form that I want to update (on ajax call), I do this by actually picking out what I don't need in the form with twig setting the form objects to rendered like so:
{% do form.title.setRendered %}
(this is just an example I actually do this for all the form objects that I don't want to render.
I then simply just call:
{{ form_rest(form) }}
which will retrieve the "rest" of the form which is different for different categories.
Now let's say you have state and than town to select. First select the state then you render the towns for that state in twig (but then you can actually just render the part you need, e.g {{ form_row(form.towns) }} and you return this rendered template as a json-response and just put it in the div you want with jquery.
$html = $this->renderView('#Bundle/NewAddPage/filter_area.twig', array('form' => $form->createView()));
and then returning the $html variable in the response.
I hope this helps, and that the explanation is good enough, if not just make a comment and I'll update this with my answer!

Related

Bind data into my form in ZF2

I have a form which I use in my viewscript. My viewscript looks as follows:
<?php
$this->titel = "Arbeitskalender Termine";
$this->headTitle($this->titel);
foreach($this->aktermine as $termin) :
$this->nr=$this->escape($termin->nr);
$this->kopfnr=$this->escape($termin->kopfnr);
$this->datum=$this->escape($termin->datum);
$this->zeit=$this->escape($termin->zeit);
$this->thema=$this->escape($termin->thema);
echo $this->form ;
endforeach;
?>
I get my form (it is a table) I get the same repeats of the form like records in my table. But I don´t see any record in the form fields. What´s wrong? How can I get the values of my dataset objects in each field?
If I use a viewscript in html it works fine.
Use the the form’s bind() method to attach your model to the form. The value for each field will be extracted from the model and displayed in the form.
This is used in two ways:
When displaying the form, the initial values for each element are
extracted from the model.
After successful validation in isValid(), the data from the form is put back into the model.
To use this method you need to implement getArrayCopy() and exchangeArray() in your model Aktermine.
So in your action you'll have something like this:
$form = new YourForm();
$form->bind($aktermine);
Please see the example of Editing an Album, in the documentation.
Read also Binding Objects to Forms
If you are using doctrine, just add getArrayCopy() into your entity like this:
public function getArrayCopy(){
return get_object_vars($this);
}
And then within your controller action :
$form->bind($yourEntity);

Pass a Parameter to a Controller in Laravel

I would like to pass the id of an object to a controller in laravel. Basically I've a page that displays the information relating to a book. The user has the option to add themes to that book. The line below is the link they click in order to open the create theme form:
Add Theme
and the ThemeController method:
public function create(){
$books = Book::all();
return View::make('theme.create', compact('books'));
}
Currently this brings up a Form with a list of all of my books in a dropdown. I would rather just pass the id of the book the user was on prior to being brought to this form and use that instead. Is there any way I can do that? I've also included my routes.php file:
Route::model('book', 'Book');
//Show pages
Route::get('/books', 'BookController#index');
Route::get('book/create', 'BookController#create');
Route::get('book/edit/{book}', 'BookController#edit');
Route::get('book/delete/{book}', 'BookController#delete');
Route::get('book/view/{book}', 'BookController#view');
////Handle form submissions
Route::post('book/create', 'BookController#handleCreate');
Route::post('book/edit', 'BookController#handleEdit');
Route::post('book/delete', 'BookController#handleDelete');
//Themes
Route::model('theme', 'Theme');
Route::get('/themes', 'ThemeController#index');
Route::get('theme/create', 'ThemeController#create');
Route::get('theme/edit/{book}', 'ThemeController#edit');
Route::get('theme/delete/{book}', 'ThemeController#delete');
Route::get('theme/view/{book}', 'ThemeController#view');
Route::post('theme/create', 'ThemeController#handleCreate');
Route::post('theme/edit', 'ThemeController#handleEdit');
Route::post('theme/delete', 'ThemeController#handleDelete');
You can still use normal $_GET methodology.
On your route:
book/create
You could simply request.
book/create?book_id=xxyy
Then in your controller create function
public function create(){
$bookId = Input::get('book_id');
$books = Book::all()->where('book_id' => $bookId);
return View::make('theme.create', compact('books'));
}
PLEASE NOTE:
I haven't used Laravel in a while, don't trust that elequont query :P.. I was just putting a theoretical "where" in there.
And here http://laravel.com/docs/requests#basic-input, you will note this:
"You do not need to worry about the HTTP verb used for the request, as
input is accessed in the same way for all verbs."
So whether you're using get, put, post or delete, whatever you wrapped in your header, you will be able to find using Input Class.
EDIT
Just needed to add, that you won't need to modify your routes at all. This is just a Simple GET parameter in a GET request. Just like in any other application.

How to render a form of another class view in Yii?

In Yii, I have a view belonging to class A, and in a view corresponding to this class, I want to add a form to create a model of another class.
So, in protected/views/pictures/myview.php, I have:
<?php
/* #var $this PicturesController */
/* #var $model Pictures */
$objectForm = new Objects();
$newForm = ObjectsController::renderPartial('create',array('model'=>$objectForm),true);
?>
And I am trying to add a form to render protected/views/objects/create.php. But the above code doesn't work since the view is still trying to load the create form from the Pictures class. Since I am obtaining errors saying that same properties of Objects are not defined, because the system is loading the Pictures create form.
How can I add the create form of the Objects model?
Note: I added "applications.controllers.*" to the import array in main.php, but I understand this is a bad practice. Is there any possible solutions that doesn't involve me to do this?
Thanks.
In this case you just needed to link the correct view in the renderPartial:
$objectForm = new Objects();
$newForm = ObjectsController::renderPartial('/objects/create',array('model'=>$objectForm),true); // "/objects/create", and not "create".
Those errors were giving because Yii understood you were rendering to the create view of pictures, and you gave it the model of Objects (rather than the model of Pictures).
If we were talking about a render call in the controller, and you want to render a view from another action that belongs to another controller, you do not render to this view directly. You have to "redirect" to the action of this other controller (that belongs to the other class).
try like this this
$this->renderPartial('application.staff.views.default.create',array());
this is the format
$this->renderPartial('context_text.views.display._display_text');

Processing Zend_Form dynamically generated elements

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.

Pre-load value into field in symfony form, getting error "Cannot Update form fields"

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')
));

Categories