【问题标题】:How to make casts work in this way?如何使演员以这种方式工作?
【发布时间】:2017-08-08 08:42:48
【问题描述】:

我正在使用存在复选框的表单,但目标模型的字段必须是布尔类型,因为它们在我的迁移中定义为布尔类型。例如:

$table->boolean('is_active')->default(true);

模型的值是这样填充的:

    foreach (static::getFillableFields() as $field) {
        $entry->$field = $request->input($field);
    }

所以我添加了演员表以使该字段为布尔值:

class Entry extends Model
{

    protected $casts = [
        'is_active' => 'boolean',
    ];

但是现在我看到的是:当表单的复选框被选中并且我在请求中有 '1' 字符串时,它运行良好 - 当我访问 $entry->is_active 时,'1' 给出 'true'。但是,当未选中复选框时,它会给出“null”值,并且 - 我不知道为什么 - 当模型的字段设置为 null 时,它会返回 null(不是“false”,正如我所料)。 为什么会这样?在我的情况下,这使得强制转换毫无用处。我可以改变这种行为吗?

我对为每个布尔字段添加这个(访问器/修改器)的想法并没有太大的启发(但实际上这导致了我需要的):

public function setIsActiveAttribute($value)
{
    $this->attributes['is_active'] = (bool)$value;
}


public function getIsActiveAttribute(bool $value): bool
{
    return $value;
}

【问题讨论】:

标签: php laravel eloquent


【解决方案1】:

正如@Devon 提到的,未选中的复选框不包含在发送到控制器的请求数据中。 HTML 规范将未选中的复选框视为不成功,因此不会提交它们。

然而,用于绕过此“限制”的一个技巧是向您的 HTML 添加一个隐藏输入,该输入与您的复选框同名,但包含 false 值。此隐藏输入必须在您的复选框输入之前。

这将允许您继续使用批量分配功能。

所以,您的表单应该如下所示:

<input type="hidden" name="is_active" value="0" />
<input type="checkbox" name="is_active" value="1" />

现在,当表单在未选中复选框的情况下提交时,隐藏的输入将确保输入值存在于请求数据中且值为 false (0)。

当表单被选中的复选框提交时,两个输入将使用相同的名称成功提交,但服务器端只会采用它看到的 last 值。这就是为什么复选框必须位于隐藏输入字段之后的原因,以便服务器看到的最后一个值是复选框上定义的成功值 (1)。

附带说明,这也是 Ruby on Rails 表单助手处理复选框的方式。来自他们的documentation。

HTML 规范说未选中的复选框是不成功的,因此 Web 浏览器不会发送它们。不幸的是,这引入了一个陷阱:如果Invoice 模型具有paid 标志,并且在编辑已付款发票的表单中,用户取消选中其复选框,则不会发送paid 参数。所以,任何像

这样的质量分配习语

@invoice.update(params[:invoice])

不会更新标志。

为防止这种情况,助手会在复选框之前生成一个辅助隐藏字段。隐藏字段具有相同的名称,其属性模仿未选中的复选框。

这样,客户端要么只发送隐藏字段(表示复选框未选中),要么同时发送两个字段。由于 HTML 规范规定键/值对必须按照它们在表单中出现的顺序发送,并且参数提取获取查询字符串中任何重复键的最后一次出现,这适用于普通表单。

【讨论】:

    【解决方案2】:

    这是控制器问题,而不是模型问题。如果未选中 HTML 复选框,它们将没有值,这就是它们的工作方式。

    因此,当从控制器中的 Request 对象中检索值时,应将默认值设置为 false。

    控制器方法中的示例:

    $model->is_active = $request->input('is_active', false);
    

    如果你将 input() 的第二个参数留空,它将默认为 null。

    【讨论】:

    • 好主意,谢谢。但我更喜欢使用 foreach 的简单填充(不指定任何单个字段),因为它使我的存储/更新操作通用。这就是为什么我决定使用 Casts,而不是在我的控制器中进行类型转换(因为它更多地指的是模型逻辑,而不是控制器的)
    【解决方案3】:

    突然:强制转换不适用于 Model::save() 方法。 laravel eloquent model casts on save?

    所以剩下的就是使用访问器/修改器..

    【讨论】:

    • $casts 工作正常。它是一种在您水合(从数据库中读取模型)时转换属性的工具,但不是在您脱水(保存)时。为了达到您想要的结果,您需要使用默认参数输入或自定义保存(可能通过事件/启动)并在必要时设置未设置的内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-07
    • 1970-01-01
    • 2018-01-01
    相关资源
    最近更新 更多