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.
Related
Looking for a straightforward way to add constraints dynamically to all of my form fields. So far I've hit upon the idea of using a form type extension, which kind of works: I can modify the form view and then manually check the view on form submission.
However, is there a smarter way to add real Symfony-based constraints in real-time?
(Note that the constraints need to be added to the form in real-time as the form loads based on user configuration in the database.. Predefined form groups and the like won't work.)
I would suggest to use form events.
Use the PRE_SUBMIT event to edit the form before validation.
Recreate your fields with $event->getForm()->add(...) adding your constraints.
Of course you can automatically add the listener to all form using a FormExtension which adds the listener.
EDIT : Some examples from Alsatian67/FormBundle
Your extension should looks like :
class ExtensibleExtension extends AbstractTypeExtension
{
private $extensibleSubscriber;
public function __construct($extensibleSubscriber) {
$this->extensibleSubscriber = $extensibleSubscriber;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Only apply on base form
if($builder->getForm()->isRoot())
{
$builder->addEventSubscriber($this->extensibleSubscriber);
}
}
public function getExtendedType()
{
return FormType::class;
}
}
And your EventListener / EventSubscriber should iterate on all the children :
foreach($event->getForm()->all() as $child){
$childName = $child->getName();
$type = get_class($child->getConfig()->getType()->getInnerType());
$options = $child->getConfig()->getOptions();
$options['constraints'] = array(/* ... */);
$form->add($childName,$type,$options);
}
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.
A few years ago I made a SilverStripe website and added too many fields to Page.php. I'm reworking some of this at the moment but cannot afford do reinvent the Project - now on SilverStripe 3.1.10.
I thought to declutter the UI for Page Sub-Classes, that do not need all the inherited fields, with a few Extensions.
An example how this extension could look
class NoClutter extends Extension {
public function updateCMSFields(FieldList $fields) {
$fields->removeFieldFromTab("Root.Main", "MenuTitle");
$fields->removeFieldFromTab("Root.Main", "Workflow");
}
}
config.yml
RedirectorPage:
extensions:
- NoClutter
This works on all classes for fields added in SiteTree (such as the MenuTitle field), but not for fields added in Page (such as the Workflow field). If the Extension is on UserDefinedForm, Workflow is also removed. But it does not work if the extension is on RedirectorPage. MenuTitle on the other hand is removed in both classes. My guess it's about order. My project is After: 'framework/','cms/' and hope I can make an extension like NoClutter work within the project.
How can I achieve this or how else could I work around the problem?
You need to add $this->extend('updateCMSFields', $fields) at the end of your Page getCMSFields() function.
class Page extends SiteTree {
// ...
public function getCMSFields() {
// call updateCMSFields after adding your fields
SiteTree::disableCMSFieldsExtensions();
$fields = parent::getCMSFields();
SiteTree::enableCMSFieldsExtensions();
// ...
$this->extend('updateCMSFields', $fields);
return $fields;
}
}
$this->extend('updateCMSFields', $fields) declares where your code updateCMSFields() function will get called.
The problem you are having is updateCMSFields() is getting called before you add your custom fields in the Page getCMSFields() function. So you are trying to remove the Workflow field before it is added. This is because the updateCMSFields extension hook is declared in the parent SiteTree getCMSFields() function.
UserDefinedForm solves this by calling $this->extend('updateCMSFields', $fields) at the bottom of its getCMSFields(). SiteTree::disableCMSFieldsExtensions() is required before parent::getCMSFields() is called for the extension hook to work.
I use KnpDoctrineBehaviors (Sluggable,Translatable, etc ). Field that I use for slug, I have only in MyClassTranslations. So when I add Sluggable for my translations class, I have for each i18n entry different slug. How to solve this? Thank you!
You can override the getter for title in your entity, and just call the translate method from KnpDoctrineBehaviors on your entity. It will try to get the title translation in your current locale, or fallback to default locale translation.
<?php
namespace AppBundle\Entity;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
use AppBundle\Entity\AppTrait\TranslatableEntity;
class SomeEntity
{
use ORMBehaviors\Translatable\Translatable;
use ORMBehaviors\Sluggable\Sluggable;
public function getSluggableFields()
{
return ['title'];
}
public function getTitle()
{
return $this->translate(null,true)->getTitle();
}
This implies that the slug might be updated often if you update the title with different locale, you might want to avoid this, and just use english title for slug generation:
public function getTitle()
{
return $this->translate('en')->getTitle();
}
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.