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