【问题标题】:Symfony2 forms interpret blank strings as nullsSymfony2 表单将空白字符串解释为空值
【发布时间】:2012-04-13 07:06:28
【问题描述】:

我有一个 Symfony2 表单,其中包含多个字段,包括一个名为 recap 的可选文本字段。

当其中有一些文本时,这个recap 字段完美保存,但是当该字段留空时,我收到此错误:

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

没错 - recap 列不能是 null。我是故意这样设置的。 Null 表示未知。当用户将recap留空时,recap的值是未知的;它是空白的。

我的问题是如何让 Symfony 在 recap 为空白时将其保存为 '',而不是 null

【问题讨论】:

  • 这不是Doctrine的更多问题吗?在这种情况下,您可以将类属性的默认值设置为空字符串,我们使用自定义 DBAL\Types,它将空值转换为空字符串。
  • 我认为你在 Doctrine 的结尾是对的。将默认值设置为空值对我来说不起作用,尽管我在多个地方看到了应该起作用的说法,这让我感到困惑。

标签: symfony


【解决方案1】:

转到您的实体并转到变量的声明。

/**
 * @var string $name
 *
 * @ORM\Column(type="string", length=50)
 */
public $recap = '';

您可以为 $recap 指定一个默认值。

或者当你有函数 setRecap 时,你可以检查是否为空或未设置并设置你期望的值。

public function setRecap($recap) {
   $this->recap = !isset($recap) ? '': $recap;
}

或者您将表单类型中的默认值设置为 '' 但我认为这不是您所期望的。

【讨论】:

  • 有趣。您的两个建议都不是单独使用的,但是当它们一起使用时,它们就成功了。谢谢!
  • @ThomasDecaux:那么您应该使用教义;)那是错误的...教义覆盖并仅构建不存在的功能。
  • 很酷,但是,将代码与自动生成的代码混合总是一个坏习惯,而且这个问题与表单组件有关,因此解决方案必须在同一范围内
  • @ThomasDecaux:不,您绝对可以修改“生成”代码(甚至不一定需要生成)。 symfony.com/doc/current/book/… "此命令确保为 Product 类生成所有 getter 和 setter。这是一个安全的命令 - 您可以反复运行它:它只生成不存在的 getter 和 setter(即它不会取代您现有的方法)。”您不应该将实体视为某种 Doctrine 魔法,它们只是普通的 PHP 类。
  • 这是一个非常讨厌的 hack。如果 Sf2 或 Doctrine 修复了空字符串的错误处理,您可能会得到意想不到的结果。
【解决方案2】:

没错,为此浪费了很多时间;-(“转换”在组件 Form.php 的第 1113 行中被硬编码!

private function viewToNorm($value)
{
    $transformers = $this->config->getViewTransformers();

    if (!$transformers) {
        return '' === $value ? null : $value;
    }

    for ($i = count($transformers) - 1; $i >= 0; --$i) {
        $value = $transformers[$i]->reverseTransform($value);
    }

    return $value;
}

在我看来,这是一个很大的错误(因为是 DataTransformer 的作用,而不是 Form)。所以解决方案是创建自己的 DataTransformer 并将其与文本类型相关联。目前我在 Symfony2 上浪费了很多时间,总是在资源中寻找这些小技巧;-(

【讨论】:

  • 我正接近记录一个关于此的错误,因为它看起来非常愚蠢。如果你仔细阅读他们的文档,它基本上说如果一个文本字段被标记为必填,一个空字符串将作为一个空字符串结束,但如果该字段是可选的,一个空字符串将被转换为null。见empty_datarequired
  • @thomas-decaux 我对 Symfony2 也有同样的痛苦——你还在使用它还是找到了更好的解决方案?
  • @jxmallett 你记录过错误吗?对于 Symfony 来说,这将是一张很棒的 DX(开发者体验)门票
【解决方案3】:

我对此问题有另一种解决方案:

创建虚拟类

class EmptyString {
    public function __toString() {
        return '';
    }
}

并设置

$builder->add('recap', 'text', [
    'empty_data' => new EmptyString(),
]);

实体存入数据库时​​,会自动转为空字符串。

【讨论】:

  • 这也是我的解决方案。发现空文本框被当成空值,然后直接强制成sql语句,绕过了Entity setter,这样就抓不到空值了。
  • 为什么不只做'empty_data'=>''?
  • @the_nuts 因为它被转换为 null,至少在 symfony 2.4 中
【解决方案4】:

我认为您以错误的方式处理问题:当用户将 recap 留空时,值 is unknown 因为未提供。例如,您没有说一个空的生日字段意味着生日等于“”,但该生日是未知的。地址等也是如此......

因此,如果您的用户不需要填写此字段,那么似乎最相关的响应实际上是将您的字段设置为可为空

【讨论】:

  • 我不同意这一点。 “补充信息”文本字段可以为空(没有补充信息)或 null(未知或不相关)
【解决方案5】:

刚刚在 Symfony2 中遇到了类似的错误:

我的实体:

/**
 * @ORM\Column(type="boolean")
 */
protected $Valid;

我的表格:

$builder->add('Valid', 'hidden');

现在对于真值,字段以 value="1" 正确呈现,但对于假值,字段以 value="" 呈现,这导致:

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

虽然我希望它呈现为 value="0"。

当然可以增强 setValid() 方法来设置正确的值:

public function setValid($valid)
{
    $this->Valid = ($valid ? true : false);
}

解决问题,而不是解决问题。

【讨论】:

    【解决方案6】:

    在表单中,您可以:

    $builder
    ->add('optionalField', TextType::class, ['required'=>false, 'empty_data'=>''])
    

    【讨论】:

      【解决方案7】:

      我也遇到过这个问题。向我建议的一个优雅的解决方案是:定义@ORM\Column(type="string", length=50, nullable=true)

      【讨论】:

      • 永远不要在数据库中使用可空值!它对性能非常不利,更喜欢 "" 或 0。
      • @ThomasDecaux 你有这方面的参考吗?这是针对特定的 RDBMS 的吗?
      • 这里有一个关于 NULL 值(对于 SQL 类似)的性能问题的很好的讨论:link
      猜你喜欢
      • 2013-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多