28

I'm currently trying to use the Symfony2 Form Component though the Silex microframework.

My login form is generated as follows:

$app = $this->app;

$constraint = new Assert\Collection(array(
    'username' => new Assert\NotBlank(),
    'password' => new Assert\NotBlank(),
));

$builder = $app['form.factory']->createBuilder('form', $data, array('validation_constraint' => $constraint));

$form = $builder
    ->add('username', 'text', array('label' => 'Username'))
    ->add('password', 'password', array('label' => 'Password'))
    ->getForm()
;

return $form;

The issue is that the resulting form is being created as follows:

<fieldset>
    <input type="hidden" value="******" name="form[_token]" id="form__token">
    <section class="">
        <label class=" required" for="form_username">Username</label>
        <div><input type="text" value="" name="form[username]" id="form_username" class="text"></div>
    </section>
    <section class="">
        <label class=" required" for="form_password">Password</label>
        <div><input type="password" value="" name="form[password]" id="form_password" class="password"></div>
    </section>
    <section>
        <div><button class="fr submit">Login</button></div>
    </section>
</fieldset>

Whereas I want the name and id attributes to be as follows:

<div><input type="text" value="" name="username" id="username" class="text"></div>
...
<div><input type="password" value="" name="password" id="password" class="password"></div>

I've scoured the web and discovered advice on the 'property_path' option but I believe this is to do with the class used to process the data when used in the actual Symfony2 framework itself.

I've been through the Form Component files and the point as which this is being set is in:

Symfony/Component/Form/Extension/Core/Type/FieldType.php - line 71

public function buildView(FormView $view, FormInterface $form)
{
    $name = $form->getName();

    if ($view->hasParent()) {
        $parentId = $view->getParent()->get('id');
        $parentFullName = $view->getParent()->get('full_name');
        $id = sprintf('%s_%s', $parentId, $name);
        $fullName = sprintf('%s[%s]', $parentFullName, $name);
    } else {
        $id = $name;
        $fullName = $name;
    }
    ...

Unforunately the FormFactory utilises the FormBuilder which then works with the Form class and I've not had enough time to analyse the whole inner workings of the Component.

I do know that fields are added into the 'children' array within the FormBuilder with a corresponding list of options. When the getForm function is called, a new Form is instantiated and each child FieldType is entered into the Form using the add() method. This Form->add() method automatically sets the Form as the parent of each child:

public function add(FormInterface $child)
{
    $this->children[$child->getName()] = $child;

    $child->setParent($this);

    if ($this->dataMapper) {
        $this->dataMapper->mapDataToForm($this->getClientData(), $child);
    }

    return $this;
}

Without starting to override these classes just to remove this does anyone else know of the best method of just displaying the fields name?

It is possible to just pull 'name' instead of 'full_name' in the form_div_layout.html.twig widget_attributes block but I wasn't sure as to whether this was ideal (as the id remains unchanged) or if there was another method or injectable options that would do the trick.

thingygeoff
  • 283
  • 1
  • 3
  • 6

3 Answers3

28

In Symfony3, override AbstractType::getBlockPrefix in child class to return null.

mailo
  • 2,491
  • 20
  • 19
25

Instead of the createBuilder function use:

$builder = $app['form.factory']->createNamedBuilder(null, 'form', $data, array('validation_constraint' => $constraint));

First parameter being the form name.

Example by Bernhard Schussek himself at https://stackoverflow.com/a/13474522/520114

Community
  • 1
  • 1
webda2l
  • 6,570
  • 2
  • 23
  • 28
  • Ahhh, now why didn't I look on github, that would have made sense. Thanks :) – thingygeoff Dec 07 '11 at 16:58
  • Ah ha! you have updated your answer - an ideal solution! Thanks – thingygeoff May 24 '13 at 09:36
  • Is it possible to do something like this when using FormTypes as services? I don't think you can access createNamedBuilder in that context and using FormType::getName() { return ''; } causes problems referencing the FormType as a service. – caponica Aug 29 '13 at 13:32
  • @caponica exactly. Have you found any way to do it? – mmoreram Sep 25 '13 at 16:08
  • @mmoreram - yes, Bernhard pointed out (on the other thread) that it's easy: http://stackoverflow.com/questions/13384056/symfony2-1-using-form-with-method-get/13474522#13474522 You just use $builder->getFormFactory() (it's a while since I looked at this so don't remember the details off-hand!) – caponica Sep 27 '13 at 14:18
  • FormFactory -> `createNamed` could be used to get a `Form` instance instead of a builder – Leto Aug 11 '14 at 13:53
  • In symfony 4, in the controller you can: `$this->container->get('form.factory')->createNamed(null, DemoEntity::class, $myEntity);` – Nuryagdy Mustapayev May 31 '20 at 18:52
  • Using `null` as the form name does not work anymore in Symfony 5, because the form name is typed as `string`, so `null` leads to a `TypeError`. An empty string can be used instead and has the same effect as using `null` in previous Symfony versions. – iquito Nov 02 '20 at 12:11
9

If you are not using child forms you can set getName method of your form as empty string:

class FormType extend AbstractType {
    // ...

    public function getName() {
        return '';
    }
}
Artem L
  • 9,115
  • 1
  • 18
  • 14