Подтвердить что ты не робот

Пустая строка вместо нулевых значений Eloquent

Я пытаюсь создать объекты, используя функцию Mass-assign 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' => ...
    ...
));

Какие-нибудь советы о том, как это сделать или исправить?

Обновление. Теперь я решил это сделать array_filter в App::before().

Обновление В фильтре было немного беспорядка. Я в конечном итоге делаю:

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, база данных будет вызывать ошибку, поскольку внешний ключ "" не может быть найден.

Работает отлично. Любой комментарий?

4b9b3361

Ответ 1

Используйте событие 'save' для поиска пустых моделей и явно установите для них значение null. "Проект" - это название моей модели в этом примере. Поместите это в вспомогательную функцию и подключите ее ко всем вашим моделям.

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

    return true;
});

ОБНОВЛЕНИЕ ЗА ЛАВРОВ 5 (11 апреля 2016 года)

Я также закончил создание промежуточного ПО Http для очистки входных данных из 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);
    }
}

Ответ 2

Я сам искал ответ на этот вопрос, и самое близкое, что я могу придумать, - это использовать Mutators (http://laravel.com/docs/eloquent#accessors-and-mutators).

Та же проблема была решена путем добавления (волшебного!) Метода 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 прежде чем я смогу назначить его для моей модели.

Ответ 3

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');

Ответ 4

Возможно, более общее решение:

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'];
}

И все, вы не должны повторять один и тот же код в мутаторе.

Ответ 5

Для ввода формы это нормально и логичнее иметь пустые значения, а не нулевые значения.

Если вы действительно считаете, что лучший способ сделать это, это прямое внесение ввода в вашу базу данных, чем решение сделать пустые значения null будет примерно таким.

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

Лично я не согласен с такими решениями, но это работает для некоторых людей.

Ответ 6

Другое простое решение - создать класс базовой модели и расширить из него модели:

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);
    }
}

Затем просто заполните обязательные поля в $ forceNullFields для каждой модели, если это необходимо.

Ответ 7

В модели Eloquent убедитесь, что $guarded пуст. Вам не нужно устанавливать $fillable.

Массовое присвоение обычно не является хорошей идеей, но в некоторых сценариях это нормально, вы должны определить, когда.

Смотрите: http://laravel.com/docs/eloquent#mass-assignment