Symfony2 custom form field - php

I am very new to Symfony, so question might seem a little simple, but I need a help.
I have generated new bundle.
I have added a new route in Me\MyBundle\Resources\config\routing.yml:
my_homepage:
pattern: /
defaults: { _controller: MeMyBundle:Default:index }
Bundle controller looks in simple like this:
namespace Me\MyBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
public function indexAction()
{
$form = $this->createFormBuilder()
->getForm()
;
return $this->render('MeMyBundle::index.html.twig', array(
'form' => $form->createView(),
'param1' => 'some_string_1',
'param2' => 'another string',
));
}
}
In the twig template I can read and process proper params, as I want.
Whole action happens in the generated form, where there are AJAX requests routed to another controller.
What I want to achieve is create a new custom form field, which could be reued in same form multiple times, with different params.
For example, I would like my indexAction() would have looked like this:
public function indexAction()
{
$paramsArr_1 = array(
'param1' => 'some_string_1',
'param2' => 'another string',
);
$paramsArr_2 = array(
'param1' => 'some_string_2',
'param2' => 'another fine string',
);
$form = $this->createFormBuilder()
->add(myCustomField, $paramsArr_1)
->add(myCustomField_2, $paramsArr_2)
->getForm()
;
return $this->render('MeMyBundle::index.html.twig', array(
'form' => $form->createView()
));
}
Yes, I did see this article, but it did not help me much. I could not get it working.
Any help is much appreciated.

From what I know form fields extends the base form class, so your 'myCustomField' can be another form actually.
Check this: http://symfony.com/doc/current/reference/forms/types/form.html
As you know each Form object has attached an object to it, so instead of your arrays you could create a new object with those values set on it, and then add that form how many times your want with objects containing different data.

Related

Symfony2 create filled Form based on two diffrent types (editAction)

I was looking for a answer, but i never found something. .
My starting Position:
This classes / files are included
Controller, DataDFu1Controller.php
Form DataAPatientType.php ,DataDFu1Type.php
Entity DataAPatient, DataDFu1
views/DataDfu1/ form.html.twig
The DataDFu1Controller contains (to the overview) the indexAction, newAction and
editAction and so on.
Both Formtypes (DataAPatientType.php ,DataDFu1Type.php) comes in one Form (look Method) this form goes to be rendered later in the form.html.twig file for the newAction and the editAction
For the newAction i did it so:
private function createNewForm(DataAPatient $entity)
{
$form = $this->createForm($this->get('data_livebundle.form.dataapatienttype'), $entity, array(
'action' => $this->generateUrl('dataapatient_new'),
'method' => 'POST',
));
return $form->add('dFu1', new DataDFu1Type());
}
later the form comes rendered. . .
So first i create "DataAPatientType.php" Form and then i add the "DataDFu1Type.php" to the form.
In the view -> form.html.twig it looks like that.
for DataDFu1Type:
{{ form_widget(form.dFu1.fu1Examiner1)}}
for DataAPatientType:
{{ form_label(form.pSnnid, 'SNN-ID (if known)', {'label_attr':{'style':'margin-top:3px'}})}}
So i can get a variable or a function with the suffix 'dfu1' after the form.
Everything works so fine. I hope the condition are understandible till now..
Now my Problem:
I have to create also an editAction which opend of course the same view-> form.html.twig with the filled values from a dataset (entity). In this process i don't understand how i can create the Form Object based also (DataAPatientType, DataDFu1Type) with the corresponding data. -> I'm trying to be more specific
private function createEditForm(DataDFu1 $entity)
{ /*
* This function shoud create the editform which insists
* DataAPatientType.php ,DataDFu1Type.php included the data from
* $entity. I have the opportunity to get the entity for DataDFu1Type
* easy directly with the Primary Key and the data for DataAPatientType
* over a Foreign Key which is safed in the $entity
*
*/
}
So i only dont understand how i can create a Form based on two types (DataAPatientType.php ,DataDFu1Type.php) with the corresponding Data inside, that i can render it like in the newAction.
For one Form i did it everytime like so and it works.. but for two types i tried a lot things which didnt worked. Have somebody a experiance? or a Solution for this Problem?
the syntax of the form.html.twig isnt changeable so the form has to be rendered equivalent like in the newAction
Example for creating a form based only on one Type and not two
private function createEditForm(Event $entity)
{
$form = $this->createForm($this->get('qcycle_eventbundle.form.eventtype'), $entity, array(
'action' => $this->generateUrl('event_edit', array('id' => $entity->getId())),
'method' => 'POST'
));
$form->add('preview', 'button', array('label' => 'Preview', 'attr' => array('data-preview' => 'preview')))
->add('submit', 'submit', array('label' => 'Save Changes'))
->add('sendAndSave', 'submit', array('label' => 'Send Mail & Save'));
return $form;
}
i really hope, that my problem and Question understandable
thanks
mjh
If i understand you have this form:
class DataAPatientType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('dFu1', new DataDFu1Type());
$builder->add('pSnnid', 'text');
[...]
}
}
Then in create
$form = $this->createForm(DataAPatientType(), new DataAPatient());
And in edit you can simply do something like
private function createEditForm(DataDFu1 $entity)
{
$form = $this->createForm(DataAPatientType(), entity); //here the form will be "populated" by the entity data
So if you want to set some default value or overwrite an existing value for example, you would use
private function createEditForm(DataDFu1 $entity)
{
entity->setPSnnid('whatever')
$form = $this->createForm(DataAPatientType(), entity); //here the form will be "populated" by the entity data

How to configure cakephp custom route class

I have create custom router class in cakephp 2.x, I'm just follow this blog post. In my app i don't have /Routing/Route folders and I create folders and put StaticSlugRoute.php file to it. In that file include following code
<?php
App::uses('Event', 'Model');
App::uses('CakeRoute', 'Routing/Route');
App::uses('ClassRegistry', 'Utility');
class StaticSlugRoute extends CakeRoute {
public function parse($url) {
$params = parent::parse($url);
if (empty($params)) {
return false;
}
$this->Event = ClassRegistry::init('Event');
$title = $params['title'];
$event = $this->Event->find('first', array(
'conditions' => array(
'Event.title' => $title,
),
'fields' => array('Event.id'),
'recursive' => -1,
));
if ($event) {
$params['pass'] = array($event['Event']['id']);
return $params;
}
return false;
}
}
?>
I add this code but it didn't seems to working (event/index is working correct).I want to route 'www.example.com/events/event title' url to 'www.example.com/events/index/id'. Is there any thing i missing or i need to import this code to any where. If it is possible to redirect this type of ('www.example.com/event title') url.
Custom route classes should be inside /Lib/Routing/Route rather than /Routing/Route.
You'll then need to import your custom class inside your routes.php file.
App::uses('StaticSlugRoute', 'Lib/Routing/Route');
Router::connect('/events/:slug', array('controller' => 'events', 'action' => 'index'), array('routeClass' => 'StaticSlugRoute'));
This tells CakePhp to use your custom routing class for the URLs that look like /events/:slug (ex: /events/event-title).
Side Note: Don't forget to properly index the appropriate database field to avoid a serious performance hit when the number of rows increases.

Modifying or Override value of a posted form field in symfony 2

I have a symfony 2 form that simply posts the comment. I want to remove html characters from the posted form before saving it to database. I read somewhere that you can't directly change the value of posted form and you need to create an event listener. I have created an event listener for this purpose to achieve this goal. So i created an event listener and below is the code.
<?php
// src/Adhl/FrontBundle/EventListener/StripHtmlSubscriber.php
namespace Adhl\FrontBundle\EventListener;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class StripHtmlSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return array(FormEvents::PRE_SET_DATA => 'preSetData');
}
public function preSetData(FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
$event->getData()->setDetails(strip_tags($event->getData()->getDetails()));
$form->add('details', 'textarea', array(
'label' => false,
'attr' => array(
'cols' => '30',
'class' => 'text-input span8'
)
)
);
}
}
$event->getData()->setDetails() changes the value of posted field "details"
but $event->getData()->getDetails() does not returns anything.
I want to get the post field that has the name "details", strip html tags from it and save it back to same posted key.
In simple PHP I could do it as:
$_POST['details'] = strip_tags($_POST['details']);
Can anyone please tell me what am i doing wrong?
How does your FormType class look like?
do you bind it on a entity or array?
If you do the entity binding, then simply in the entity modify the setDetails method in your entity to
setDetails($details) {
$this->details = strip_tags($details);
}
if you have a array binding then in the action or subscriber the getData() will return an associatoive array not a entity
$data = $event->getData();
$data['details'] = strip_tags($data['details']);
$event->setData($data);

Can FormBuilder be used in a Service class to build forms not connected to an Entity class?

I would like to use Symfony2's FormBuilder Component to build dynamic forms within a service class. However, these forms will not be linked to any entities immediately (so I don't think the Form Class service type would apply.)
I need to ask our users a series of questions with a myriad of outcomes depending on how the questions are answered.(For example: Do you like cheesecake? If yes, the user will be directed to another form asking what kind of cheescake; if no, ask if you like ice cream.. etc.) To do this I created a Dialoguer service which is instantiated with the FormFactory service. From services.yml:
dialoguer:
class: My\MainBundle\Services\Dialoguer
arguments: ['#forms.factory']
Each step of the dialog process is handled by a different dialogue class - all of which are children of an AbstractDialogue class. The dialoguer service looks at data that has already been answered and finds the appropriate dialog to ask next.
The dialoguer service passes the #forms.factory service to the appropriate dialog class, in this case Start:
namespace My\MainBundle\Services\Dialoguer\Individual;
use My\MainBundle\Services\Dialoguer\AbstractDialogue as AbstractDialogue;
class Start extends AbstractDialogue
{
static $stem = 'check_registration';
protected $fields = array(
'over_thirtyfive' => array(
'type' => 'choice',
'options' => array(
'label' => 'Are you over 35?',
'choices'=>array('yes'=>'Yes','no'=>'No'),
'expanded'=> true,
'multiple'=>false
)
)
);
....
function __construct( $formFactory )
{
$this->formFactory = $formFactory;
}
From the parent, AbstractDialogue class, the get_form method goes through each field and adds it the form based on the specifications above.
public function get_form( $form_data = array() )
{
$defaults = array_merge( $this->defaults, $form_data );
$builder = $this->formFactory->create();
$builder->setData( $defaults );
foreach( $this->fields as $field => $type_ar )
{
$builder = $builder->add( $field, $type_ar['type'], $type_ar['options'] );
}
//return $builder->getForm();
return $builder;
}
And, finally in the controller class:
$dialogue = $this->get('dialoguer')->process($request, 'Individual');
$form = $dialogue->get_form()->getView();
$args = compact('form');
return $this->render("MyMainBundle:Forms:process_app.form.html.twig", $args);
Finally, here's the issue:
Above, my get_form method passes a Symfony\Component\Form\Form instance, which doesn't have the getView method - so it throws an error.
What do I need to do get a form view using this non-traditional method of creating a form?
(Or is this not going to work?)
I had a typo in my Controller that prevented the form from accessing the View. 'getView' should be changed to 'createView'. Whoops.
I also made a change to my service, changing '$this->formFactory->create()' to '$this->formFactory->createBuilder();'
For folks in the future, I'll walk through the script in the service class and the accompanying apis:
public function get_form( $form_data = array() )
{
$defaults = array_merge( $this->defaults, $form_data );
$builder = $this->formFactory->createBuilder();
As I mentioned in the original post - $this->formFactory is made available to my class by importing '#form.factory' through my service configuration. That's createBuilder instantiates an instance of Symfony\Component\Form\FormBuilder ~ http://api.symfony.com/2.0/Symfony/Component/Form/FormBuilder.html
$builder->setData( $defaults );
foreach( $this->fields as $field => $type_ar )
{
$builder = $builder->add( $field, $type_ar['type'], $type_ar['options'] );
}
return $builder->getForm();
}
The getForm method instantiates an instance of Symfony\Component\Form\Form ~ http://api.symfony.com/2.0/Symfony/Component/Form/Form.html
And then in my Controller -
$form = $dialogue->get_form()->createView();
The createView method creates an instance of Symfony\Component\Form\FormView which can the be rendered in Twig:
{{ form(form) }}
It works.

Using Zend_Validate_Identical in Form Validation

I'm trying to use the 'Identical' validator to validate whether two passwords are the same in my registration form, but it keeps trying to validate against the actual word I enter for the token rather than the form element that I want to validate against. Code looks like this: (This is my form model constructor..)
$password = new Zend_Form_Element_Password('password');
$password->addValidator('Regex',false,array('pattern' => '/^.*(?=.{6,20})(?=.*[\d])(?=.*[a-zA-Z])/'))
->addValidator('StringLength',false,array('max'=>20))
->setRequired(true);
$password2 = new Zend_Form_Element_Password('password2');
$password2->setRequired(true);
$password2->addValidator('Identical',false,array('token'=>'password'));
$register = new Zend_Form_Element_Submit('register');
$this->setDecorators(array(
array('ViewScript',
array('viewScript' => '_form_registration.phtml'))
)
);
$this->addElements(array($firstName,$lastName,$email,$city,$password,$password2,$register));
Instead of validating against the form element called 'password' it keeps trying to match against the actual string 'password'
The work around I have is that I create a validator after the data has been posted to the controller, and validate against the post data, but if there is any more modular way to do this (AKA leaving the logic within the form constructor) I would love to know.
Thank you in advance
Are you outputting your form correctly?
I see that the decorator you're using is ViewScript so I'm guessing that you are coding the form's html yourself in some other script.
If so, are you following the Zend way of assigning names and id values to your elements? If you aren't, when you pass in the values to your form the context might not be set up correctly and it won't find the 'password' element that you need to check against.
My suggestion right now is to ouput the form using the form default decorators and look at how the ids and names look for the elements. Then, try to copy those names in the form.phtml that you're using.
After add the Identical Validator on your 'password2' element.
Try to overload isValid() function into your Form Object like this:
public function isValid ($data)
{
$this->getElement('password2')
->getValidator('Identical')
->setToken($data['password'])
->setMessage('Passwords don\'t match.');
return parent::isValid($data);
}
I have been having the exact same issue.
It was fixed by rewriting the code with an outside function to validate identical as such.
<?php
class RegisterForm extends Zend_Form
{
/**
* create your form
*/
public function init()
{
$this->addElements(array(
new Zend_Form_Element_Password('password',
array( 'label' => 'Password:',
'required' => true,
'filters' => array('StringTrim', 'StripTags'),
'validators' => array(array(StringLength', false, array(5, 25)))
)
),
new Zend_Form_Element_Password('pass_twice',
array('label' => 'Pass Twice',
'required' => true,
'filters' => array('StringTrim', 'StripTags'),
'validators' => array('Identical')
)
)
);
}
public function isValid($data)
{
$passTwice = $this->getElement('pass_twice');
$passTwice->getValidator('Identical')->setToken($data['password']);
return parent::isValid($data);
}
}
?>
Solution from: http://emanaton.com/node/38

Categories