symfony 2.6 buildForm versus createFormBuilder - symfony

someone can explain me the difference between buildForm and CreateFormBuilder?
what is the best way to create forms? I'm reading symblog and it uses:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class EnquiryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
$builder->add('email', 'email');
$builder->add('subject');
$builder->add('body', 'textarea');
but in documentation symfony i find use of "createFormBuilder"
// src/Acme/TaskBundle/Controller/DefaultController.php
namespace Acme\TaskBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
public function newAction(Request $request)
{
// createFormBuilder is a shortcut to get the "form factory"
// and then call "createBuilder()" on it
$form = $this->createFormBuilder()
->add('task', 'text')
->add('dueDate', 'date')
->getForm();
return $this->render('AcmeTaskBundle:Default:new.html.twig', array(
'form' => $form->createView(),

The first example you've shown is the right way to do it. Why?
Best practices. A form type should live in its own namespace - BundleName\Form\Type for instance. It's a better practice to do it that way, because you're free to re-use the form type anywhere you want in your application. Everything your form needs is placed in one file, easier to understand, not just by you, also by someone who can work on your project in future. That would be the first place every developer should look for, if something needs to be changed/added. Once you start adding event listeners, custom validators and more into your forms, you will understand that a controller is not a suitable place for a form to be defined.
DRY - every developer is aiming to write better code everyday. One of the most important concepts regarding controllers is - keep it as thin as possible. Let the controller action do only what it's supposed to do, nothing more - nothing less. Once your form types are defined, then its only a matter of few lines to create and render your form.
To answer your first questions - no, there is not much of a difference, whether you create your form in separate class or not. There is a lot more to discuss on this matter, but I believe this would be enough for you to understand the idea behind form type as a class. My suggestion to you, is to keep your forms in their own namespace.
Hope this can clarify things for you.

Related

Symfony2 - Keep form validation after modifying field in SUBMIT event

I need to modify a field in the SUBMIT form event, but when I do any validation rules on the field are lost.
This is all that's happening in the form type (the title field isn't actually being changed I'm just using it as an example):
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add("title");
$builder->addEventListener(FormEvents::SUBMIT, function(FormEvent $event) {
$form = $event->getForm();
$form->add("title");
});
}
Any validation rules for 'title' are now lost, either annotation rules defined with the entity or using a separate validator class.
Can I do anything to keep the validation or is it intended that validation rules don't get run for fields which are modified in the SUBMIT event?
If you can handle the FormEvents::POST_SUBMIT event instead of FormEvents::SUBMIT you will keep the validation. You will need to make sure that the listener is on the child form that you want to edit, otherwise you will have an issue with not being able to add a field to a submitted form.
In this instance you're not actually modifying a field you're adding a new one with $form->add('title') which will replace the existing 'title' field within the form (which is why the validation constraints are disappearing). You might want to look into validation groups for the type of functionality you're aiming for unless you want to elaborate on what you're doing within the submit event?

Global values: Define as service or define abstract Controller class?

In my Symfony2 app, I want to globally fetch a value from my database on each template and don't want to call on each Controller. I know I could define that as a service and inject that service into my twig templates (by defining it as a twig global).
Is that the common and recommended way? Or should I rather create an abstract Controller class where I fetch that value in my constructor and then inherit from all my other Controllers?
Note: It is actually not a static value that is the same for all users, but it is a user specific value which is different for each user.
If this variables are used to render the same spot on your page you can render an embedded controller. Like this:
<div id="sidebar">
{{ render(controller('YourBundle:User:stats')) }}
</div>
This will inject whole output of YourBundle/UserController/statsAction to the #sidebar div. Inside this action you can extract all inforamtion that you need.
If you need to use this variables in other way maybe you should look at response event.
Are you familiar with event listeners? http://symfony.com/doc/current/cookbook/service_container/event_listener.html
An event listener can be used to inject twig globals.
class ModelEventListener extends ContainerAware implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
KernelEvents::CONTROLLER => array(
array('doProject', -1300),
),
KernelEvents::VIEW => array(
array('doView', -2100),
),
);
}
public function doProject(FilterControllerEvent $event)
{
$project = $whatever_is_needed_to_find_the_project();
if (!$project) throw new NotFoundHttpException('Project not found ' . $projectSearch);
// Add to request
$event->getRequest()->attributes->set('project',$project);
// Give all twig templates access to project
$twig = $this->container->get('twig');
$twig->addGlobal('project',$project);
}
# services.yml
cerad_core__model__event_listener:
class: '%cerad_core__model__event_listener__class%'
calls:
- [setContainer, ['#service_container']]
tags:
- { name: kernel.event_subscriber }
If it's a user value like you said you can get app.user.XXX on every twig template you need without processing nothing ;)

Symfony FormType getParent vs Inheritance

When creating a custom field in Symfony, there is a method we define getParent
We define our class by extending from AbstractType class, then return a parent type using getParent method. instead of extending from parent class.
I want to know the philosophy behind this approach.
Is it possible to define my custom type like:
class ImageType extends FileType
{
public function getName()
{
return 'image';
}
}
instead of this :
class ImageType extends AbstractType
{
public function getParent()
{
return 'file';
}
public function getName()
{
return 'image';
}
}
If can, then what is the difference between these two approach?
Thanks!
There are two main differences:
The first one is about FormTypeExtensions. These extensions modify certain form types (e.g: they could change/add some default options, or even add a field).
Using the first approach (e.g. Inheritance), all extensions for the FileType type will be applied to the ImageType, but using the second approach (e.g. getParent), they won't, thus you have more control over your structure.
The second difference is about modifying the behaviour of the parent form inside child form, using buildForm and buildView.
Using the first approach (e.g. Inheritance), will override the base class's methods if you provide them in child, but the second approach (e.g. getParent) will add the child's logic to that of parent.
Consider the following example:
// FileType
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->add('name', 'text');
}
// ImageType
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->add('email', 'email');
}
Inheritance:
form fields: [email]
getParent
form fields: [name] [email]
No, you need to extend using AbstractType. This is used for displaying and building a form and is not a simple entity that you are extending. The base type, FileType in your case, relates to an file with specific methods and you will be allowed to easily override them but extending through AbstractType and can add new fields. If you extended FileType, I do not think Symfony2 would load any new functions properly.
I think the first method is more compact and would like to use it, but I think this would cause problems if you are adjusting the buildView or setDefaultOptions, or adding another method that was not part of the base type.

Symfony2 FormBuilder isn't rendering text fields

I'm using Symfony's FormBuilder to create a form and render it via Twig.
I use this as my Type:
<?php
namespace Vendor\AppBundle\Form;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\AbstractType;
class RequestType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', 'text');
$builder->add('email', 'email');
$builder->add('question', 'textarea');
}
public function getDefaultOptions(array $options)
{
return array('data_class' => 'Vendor\\AppBundle\\App\\Request');
}
/**
* Returns the name of this type.
*
* #return string The name of this type
*/
public function getName()
{
return 'request';
}
}
When I render my form (with form_widget(form.field)) everything looks great, except for the name field, that doesn't output any input field. If I change to something like "email", it works perfectly.
I'm using Sf2.3 BETA 1. Any thoughts on why does this happen with text fields only? It's woth noting that the labels, fieldsets, and everything is outputted, except the actual <input> tag.
EDIT 1: This is the Controller Code, in case you need it.
EDIT 2: It's worth noticing that this is an update from a Sf2.1 app to Sf2.3 BETA 1. The code has been updated, but perhaps something's wrong with that?
In this case, it was something that had to do with the fact that this code is a refactor of really old (+2 years) code.
The issue was that the form widget was being replaced with another one, and that "other one" was messing with the output, since Twig's functions aren't the same, nor the structure.

containerAware and Controller in symfony2

FOSUserBundle profile controller
use Symfony\Component\DependencyInjection\ContainerAware;
class ProfileController extends ContainerAware
some functions ok ... but when i try then creat form
$form = $this->createForm
This error appear: Call to undefined method ProfileController::createForm()
BUT when i change it to this:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class ProfileController extends Controller
The form is rendered... so ... i dont know how can i add this controller to my class and dont remove the ContainerAware ? :/
//
MY solution ?
instead of containeraware i use
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
And then
class ProfileController extends Controller implements ContainerAwareInterface
But i dont know i cant see a different i am noob now so... is it good solution or i will broke something?
To answer your original question,
Replace:
$form = $this->createForm
With:
$form = $this->container->get('form.factory')->create($type, $data, $options);
The createForm method is just a convenience method defined in Symfony\Bundle\FrameworkBundle\Controller\Controller. For various reasons, 3rd party libraries tend not to extend the Controller class. Hence createForm is not available.
The real question is: why are you trying to extend the Profile controller? In most cases it is not necessary. It's better to do your customization by listening to events. That of course assumes you are using the development version of FOSUserBundle.
Controller is already ContainerAware - from Controller declaration:
class Controller extends ContainerAware
Have a look at this blog Symfony2: Moving Away From the Base Controller by Richard Miller

Resources