13
votes

According to the Symfony 2.4 Documentation, any form field that is not required, but submitted without any value (default value for choice fields or empty value for text fields), will be saved to the entity with a NULL value. So if your entity field is defined as NOT NULL (e.g. not nullable=true), when you persist the entity you will get a nasty error:

SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'shell' cannot be null

So the documentation says that if you don't want it to use NULL as the default value, you can kindly specify the empty_data attribute on a text or choice field. However, this is not working for me.

Entity Field (not nullable):

/**
 * @ORM\Column(type="string")
 */
protected $shell = '';

Form Builder (not required):

->add('shell', 'choice', array(
    'label' => 'Shell',
    'choices' => array('shell' => 'Fancy Pants'),
    'required' => false,
    'empty_data' => ''
))

Am I misunderstanding this empty_data attribute? Am I missing some important setting elsewhere? What is the recommended way of doing this?

UPDATE

This Github ticket explains that this was an issue back in 2012, and it hasn't been fixed yet.

That means that everyone who is using the form builder, is forced to make any field that is not required into a nullable field? That seems pretty opinionated for the framework... there are lots of reasons we don't want to use NULL when a default '' or 0 doesn't have unique meaning and we don't need a NULL. For many queries, having to check for the presence of both field=0 OR field=NULL, is a pain.

Is there some better solution other people are using?

4
Have you found any way around this problem? - greg
The closest I've come is by adding adding one blank space for empty_data. 'empty_data' => ' '. Seems a bit hacky... - greg
@greg This appears to still be a bug with no activity on that github report. Anders below mentions a potential work around if you absolutely need to set as 0. I have not tested that. - Chadwick Meyer
Is this issue possibly fixed in v3.2? Because I don't get it with 3.2, while on 3.0.9 I do (tying a value "" to a form, sets the field to NULL, while empty_data being set to ""). - userfuser

4 Answers

5
votes

I do it the way suggested, but sets a default value in the Entity class. So insted of setting null the Entity is fixing that it sets 0 or something others.

public function __construct() { 
      $this->shell = 0;
}

Or, you can take care of it in the setter:

public function setShell($shell) {
      $this->shell = $shell;

      if ( !is_numeric($shell) ) {
             $this->shell = 0;
      }
      return $this;
}

Maybe not the best practice, but It works to not have nullable values.

4
votes

Another possible workaround is to create a simple DataTransfromer and attach it to the required field. Example:

<?php
namespace Interprac\Bundle\UtilityBundle\Form\DataTransformer;

use Symfony\Component\Form\DataTransformerInterface;

class NullToEmptyTransformer implements DataTransformerInterface
{
    /**
     * Does not transform anything
     *
     * @param  string|null $value
     * @return string
     */
    public function transform($value)
    {
        return $value;
    }

    /**
     * Transforms a null to an empty string.
     *
     * @param  string $value
     * @return string
     */
    public function reverseTransform($value)
    {
        if (is_null($value)) {
            return '';
        }

        return $value;
    }
}

Attach the transformer to a single field:

$nameSuffix = $builder->create('name_suffix', 'choice', array(
        'label'   => 'Name Suffix',
        'choices' => PR::getSMSFSuffixes(),
))->addModelTransformer(new NullToEmptyTransformer());

$builder->add($nameSuffix);
1
votes

You can since 2.6, use a combination of placeholder and empty data in your form class.

        $builderInterface->add('gender', 'choice', array(
            'required' => false,
            'choices' => array(
                'M'     => 'Male',
                'F'     => 'Female',
                'I'     => 'I dont want to specify'
            ),
            'placeholder' => 'Choose Gender',
            'empty_data'  => 'I'
        ));
1
votes
  1. Do not use empty_data option.
  2. And use the placeholder attribute.
  3. Ex:
    array(
       'label' => 'textExample',
        'required' => false,
        'disabled' => false,
        'read_only' => true,
        'attr' => array('placeholder' => 'DefaultValue')
        )