Пустая строка вместо нулевых значений Красноречива
Я пытаюсь создавать сущности, используя красноречивую функцию массового назначения...
$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
, база данных выдаст ошибку, так как внешний ключ "" не может быть нашел.
Работает идеально. Есть какие-нибудь комментарии?
7 answers
Используйте событие "сохранение" модели для поиска пустых моделей и явно установите для них значение null. "Проект" - это название моей модели в этом примере. Включите это в вспомогательную функцию и подключите ее ко всем вашим моделям.
Project::saving(function($model) {
foreach ($model->toArray() as $name => $value) {
if (empty($value)) {
$model->{$name} = null;
}
}
return true;
});
ОБНОВЛЕНИЕ ДЛЯ LARAVEL 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);
}
}
Я сам искал ответ на этот вопрос, и самое близкое, что я могу придумать, - это использовать мутаторы (http://laravel.com/docs/eloquent#accessors-and-mutators).
Та же проблема была решена путем добавления (волшебства!) Метод мутатора для поля внешнего ключа в модели:
public function setHeaderImageIdAttribute($value)
{
$this->attributes['header_image_id'] = $value ?: null;
}
Для таблицы с большим количеством внешних ключей это может показаться громоздким, но это самый "встроенный" метод, который я нашел для обработки этого. Плюс в том, что это волшебство, так что все, что вам нужно сделать это создать метод, и вы готовы идти.
Ларавель 4
При необходимости вы можете удалить любую пустую строку в массиве с помощью фильтрации .
$input = array_filter(Input::all(), 'strlen');
Тогда, если у вас есть что-то вроде array('a' => 'a', 'b' => '')
, вы получите: array('a' => 'a')
.
Насколько я знаю, если поле не указано в массиве для массового назначения, то Laravel Eloquent ORM будет рассматривать его как NULL
.
Ларавель 5
$input = array_filter(Request::all(), 'strlen');
Или
// If you inject the request.
$input = array_filter($request->all(), 'strlen');
Возможно, более общее решение:
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 именами полей, которые должны быть пустыми в случае пустых полей формы:
class SomeModel extends EloquentBaseModel {
protected $forcedNullFields = ['BirthDate','AnotherNullableField'];
}
И это все, вы не должны повторять один и тот же код в мутаторах.
Для ввода формы нормально и более логично иметь пустые значения, а не нулевые значения.
Если вы действительно считаете, что лучший способ сделать это - напрямую ввести входные данные в вашу базу данных, то решение сделать пустые значения нулевыми было бы примерно таким.
$input = Input::all();
foreach ($input as &$value) {
if (empty($value) { $value = null; }
}
$new = new Contact(Input::all());
$new->save();
Лично я не одобряю такого рода решения, но для некоторых людей это действительно работает.
Еще одним простым решением является создание базового класса моделей и расширение моделей на его основе:
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 для каждой модели, если это необходимо.
В вашей красноречивой модели убедитесь, что $guarded
пустое. Вам не нужно устанавливать $fillable
.
Массовое назначение обычно не является хорошей идеей, но в некоторых сценариях это нормально - вы должны определить, когда.