空字符串而不是空值Eloquent

时间:2013-07-03 16:07:56

标签: php laravel laravel-4

我正在尝试使用质量分配Eloquent功能创建实体...

$new = new Contact(Input::all());
$new->save();

问题是这样,每个字段都填充了一个空字符串,而不是我预期的null值。

我目前正在开发系统,但仍未定义一些表格列,这就是使用此方法的原因,以避免将每个新字段添加到$fillable数组和new Contact(array(...)); ...

此表中我还有大约20个字段,所以拥有像

这样的数组会有点难看
$new = new Contact(array(
    'salutation' => Input::get('salutation'),
    'first_name' => Input::get('first_name'),
    'last_name'  => Input::get('last_name'),
    'company_id' => Input::get('company_id'),
    'city' => ...
    ...
));

有关如何执行此操作或修复的任何提示?

更新到目前为止,我已经在App::before()过滤器中对array_filter进行了整理。

更新在过滤器中有点乱。我最终做了:

public static function allEmptyIdsToNull()
{
    $input = Input::all();

    $result = preg_grep_keys ( '/_id$/' , $input );

    $nulledResults = array_map(function($item) {
        if (empty($item))
            return null;

        return $item;
    }, $result);

    return array_merge($input, $nulledResults);
}

在我的functions.php。

if ( ! function_exists('preg_grep_keys'))
{
    /**
    * This function gets does the same as preg_grep but applies the regex
    * to the array keys instead to the array values as this last does.
    * Returns an array containing only the keys that match the exp.
    * 
    * @author Daniel Klein
    * 
    * @param  string  $pattern
    * @param  array  $input
    * @param  integer $flags
    * @return array
    */
    function preg_grep_keys($pattern, array $input, $flags = 0) {
        return array_intersect_key($input, array_flip(preg_grep($pattern, array_keys($input), $flags)));
    }
}

现在只处理以“_id”结尾的字段。这是我最大的问题,如果关系不是NULL,数据库将抛出错误,因为无法找到外键“”。

完美的作品。有评论吗?

7 个答案:

答案 0 :(得分:61)

我自己已经找到了答案,而我最接近的就是使用Mutators(http://laravel.com/docs/eloquent#accessors-and-mutators)。

通过为模型中的外键字段添加(magic!)Mutator方法解决了同样的问题:

public function setHeaderImageIdAttribute($value)
{
    $this->attributes['header_image_id'] = $value ?: null;
}

对于包含大量外键的表格,这可能会变得笨重,但它是我发现处理此问题的最“内置”方法。好处是它很神奇,所以你所要做的就是创造方法并且你很高兴。

更新 - Laravel 5.4及以上

从Laravel 5.4开始,\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class中间件在收到请求时处理此问题。在上面的示例中,如果请求包含“header_image_id”的空字符串值,则此中间件会自动将其转换为null,然后才能将其分配给我的模型。

答案 1 :(得分:19)

Laravel 4

如果有必要,可以通过过滤删除数组中的任何空字符串。

$input = array_filter(Input::all(), 'strlen');

然后,如果你有类似array('a' => 'a', 'b' => '')的内容,你会得到:array('a' => 'a')

据我所知,如果数组中没有为质量分配指定一个字段,那么Laravel Eloquent ORM会将其视为NULL


Laravel 5

$input = array_filter(Request::all(), 'strlen');

// If you inject the request.
$input = array_filter($request->all(), 'strlen');

答案 2 :(得分:11)

可能更通用的解决方案:

class EloquentBaseModel extends Eloquent {

    public static function boot()
    {
        parent::boot();

        static::saving(function($model)
        {
            if (count($model->forcedNullFields) > 0) {
                foreach ($model->toArray() as $fieldName => $fieldValue) {
                    if ( empty($fieldValue) && in_array($fieldName,$model->forcedNullFields)) {
                        $model->attributes[$fieldName] = null;
                    }
                }
            }

            return true;
        });

    }

}

您需要清理空表单字段的模型应该扩展此类,然后您应该填充$ forcedNullFields数组,其中字段名称在空表单字段的情况下必须为NULL:

class SomeModel extends EloquentBaseModel {
    protected $forcedNullFields = ['BirthDate','AnotherNullableField'];
}

就是这样,你不应该在mutators中重复相同的代码。

答案 3 :(得分:10)

使用模型'saving'事件查找空模型并将其显式设置为null。 “Project”是此示例中我的模型的名称。将它放在辅助功能中并将其连接到所有模型。

Project::saving(function($model) {
    foreach ($model->toArray() as $name => $value) {
        if (empty($value)) {
            $model->{$name} = null;
        }
    }

    return true;
});

LARAVEL 5更新(2016年4月11日)

除了在将数据发送到数据库之前清理数据之外,我还最终创建了一个Http Middleware来清除http请求中的输入数据。

class InputCleanup
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $input = $request->input();

        array_walk_recursive($input, function(&$value) {

            if (is_string($value)) {
                $value = StringHelper::trimNull($value);
            }
        });

        $request->replace($input);

        return $next($request);
    }
}

答案 4 :(得分:1)

对于表单输入,它是正常的,更合乎逻辑的是具有空值,而不是空值。

如果你真的认为最好的方法就是直接把输入放到你的数据库中,那么使空值为null的解决方案就是这样的。

$input = Input::all();
foreach ($input as &$value) {
    if (empty($value) { $value = null; }
}
$new = new Contact(Input::all());
$new->save();

就我个人而言,我不同意这些解决方案,但它确实适用于某些人。

答案 5 :(得分:1)

另一个简单的解决方案是创建基础模型类并从中扩展模型:

class Model extends \Illuminate\Database\Eloquent\Model
{
    /**
     * Set field to null if empty string
     *
     * @var array
     */
    protected $forcedNullFields = [];

    /**
     * Set a given attribute on the model.
     *
     * @param  string  $key
     * @param  mixed  $value
     * @return \Illuminate\Database\Eloquent\Model
     */
    public function setAttribute($key, $value)
    {
        if (in_array($key, $this->forcedNullFields) && $value === '') {
            $value = null;
        }

        return parent::setAttribute($key, $value);
    }
}

然后根据需要在每个模型的$ forcedNullFields中填写必填字段。

答案 6 :(得分:-2)

在您的Eloquent模型中,确保$guarded为空。您无需设置$fillable

批量作业通常不是一个好主意,但在某些情况下可以做 - 你必须确定何时。

请参阅:http://laravel.com/docs/eloquent#mass-assignment