Home » Php » php – Empty string instead of null values Eloquent

php – Empty string instead of null values Eloquent

Posted by: admin April 23, 2020 Leave a comment

Questions:

I’m trying to create entities using mass-assignment Eloquent feature…

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

The problem’s that this way, every field’s filled out with an empty string instead of null values as I expected.

I’m currently developing the system and still some table columns’re not defined, that’s why using this method, to avoid adding every new field to $fillable array and to a new Contact(array(...));

Also I’ve around 20 fields in this table, so It’d be a bit ugly to have an array such as

$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' => ...
    ...
));

Any tips of how to do this or fix?

Update By now I’ve sorted out this doing the array_filter in the App::before() filter.

Update In filter was a bit mess. I end up doing:

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

And in my 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)));
    }
}

By now only working with fields that ends with “_id”. This is my biggest problem as if a relationship is not NULL, the database will throw an error as the foreign key “” cannot be found.

Works perfect. Any comment?

How to&Answers:

Use the model ‘saving’ event to look for empty models and explicitly set them to null. ‘Project’ is the name of my model in this example. Put this in a helper function and wire it up to all your models.

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

    return true;
});

UPDATE FOR LARAVEL 5 (April 11 2016)

I also ended up creating an Http Middleware to cleanup the input data from the http request, in addition to cleaning up the data before it is sent to the database.

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

Answer:

I’ve looked for the answer to this myself, and the closest I can come up with is using Mutators (http://laravel.com/docs/eloquent#accessors-and-mutators).

The same problem was solved by adding a (magic!) Mutator method for the foreign key field in the model:

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

For a table with a lot of foreign keys, this can get kind of bulky, but it’s the most “built-in” method I’ve found for handling this. The upside is that it’s magic, so all you have to do is create the method and you’re good to go.

UPDATE — Laravel 5.4 and above

As of Laravel 5.4, the \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class middleware handles this when the request is received. In my example above, if the request contains an empty string value for ‘header_image_id’, this middleware automatically converts it to null before I can assign it to my model.

Answer:

Laravel 4

If it is necessary you could remove any empty string in an array by filtering.

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

Then if you have something like array('a' => 'a', 'b' => '') you will get: array('a' => 'a').

As far as I know, if a field is not specified in the array for mass-assignment, then Laravel Eloquent ORM will treat it like NULL.


Laravel 5

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

or

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

Answer:

Probably more generic solution:

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

    }

}

The model where you needs to sanitize empty form fields should extends this class, then you should fill $forcedNullFields array with field names that required to be NULL in case of empty form fields:

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

And thats all, you should not repeat same code in mutators.

Answer:

For a form input, it’s normal, and more logical to have empty values, rather then null values.

If you really think the best way to do this, is to directly put the input into your database, than the solution to make empty values null would be something like this.

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

Personally I don’t approve with these kind of solutions, but it does work for some people.

Answer:

Another simple solution is to create base model class and extend models from it:

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

Then just fill required fields in $forcedNullFields for each model if needed.

Answer:

In your Eloquent model, make sure $guarded is empty. You don’t need to set $fillable.

Mass assignment isn’t usually a good idea, but it’s OK to do in some scenarios – you must determine when.

See: http://laravel.com/docs/eloquent#mass-assignment