web-dev-qa-db-fra.com

Chaîne vide au lieu de valeurs nulles Eloquent

J'essaie de créer des entités à l'aide de la fonction Eloquent à assignation en masse ...

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

Le problème est que de cette façon, chaque champ est rempli avec une chaîne vide au lieu de null valeurs que je m'attendais.

Je suis en train de développer le système et certaines colonnes de la table ne sont pas encore définies. C’est pourquoi, avec cette méthode, nous évitons d’ajouter chaque nouveau champ au tableau $fillable et à un new Contact(array(...));...

En outre, j'ai environ 20 champs dans cette table, donc il serait un peu moche d'avoir un tableau tel que

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

Des conseils sur la façon de faire ou de réparer?

Mise à jour Maintenant, j'ai résolu ce problème en utilisant array_filter dans le filtre App::before().

Mise à jour Le filtre était un peu foutu. Je finis par faire:

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

Et dans mon 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)));
    }
}

A présent, je ne travaille qu'avec des champs qui se terminent par "_id". C’est mon plus gros problème car si une relation n’est pas NULL, la base de données émettra une erreur car la clé étrangère "" est introuvable.

Fonctionne parfaitement. Avez-vous des commentaires?

29
Israel Ortuño

Utilisez l'événement 'save' du modèle pour rechercher des modèles vides et les définir explicitement sur null. "Projet" est le nom de mon modèle dans cet exemple. Mettez ceci dans une fonction d'assistance et connectez-le à tous vos modèles.

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

    return true;
});

MISE À JOUR POUR LARAVEL 5 (11 avril 2016)

J'ai également fini par créer un middleware HTTP pour nettoyer les données d'entrée de la requête http, en plus de nettoyer les données avant qu'elles ne soient envoyées à la base de données.

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

J'ai moi-même cherché la réponse à cette question, et le mieux que je puisse trouver consiste à utiliser Mutators ( http://laravel.com/docs/eloquent#accessors-and-mutators ).

Le même problème a été résolu en ajoutant une méthode (magique!) Mutator pour le champ de clé étrangère dans le modèle:

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

Pour une table avec beaucoup de clés étrangères, cela peut devenir volumineux, mais c'est la méthode la plus "intégrée" que j'ai trouvée pour gérer cela. L'avantage, c'est que c'est magique, il ne vous reste plus qu'à créer la méthode et vous êtes prêt à partir.

60
Trip

Laravel 4

Si nécessaire, vous pouvez supprimer toute chaîne vide dans un tableau en filtering .

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

Ensuite, si vous avez quelque chose comme array('a' => 'a', 'b' => '') vous obtiendrez: array('a' => 'a').

Autant que je sache, si un champ n'est pas spécifié dans le tableau pour l'affectation en masse, Laravel Eloquent ORM le traitera alors comme NULL.


Laravel 5

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

ou

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

Solution probablement plus générique:

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

    }

}

Le modèle dans lequel vous devez assainir les champs de formulaire vides doit étendre cette classe, puis vous devez remplir le tableau $ forcéNullFields avec les noms de champ devant être NULL s'il s'agit d'un champ de formulaire vide:

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

Et c'est tout, vous ne devez pas répéter le même code dans mutators.

11

Pour une entrée de formulaire, il est normal et plus logique d'avoir des valeurs vides, plutôt que des valeurs nulles.

Si vous pensez vraiment que le meilleur moyen de le faire est de mettre directement l'entrée dans votre base de données, alors la solution pour rendre les valeurs vides nulles serait quelque chose comme ceci.

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

Personnellement, je n'approuve pas ce type de solutions, mais cela fonctionne pour certaines personnes.

1
Nico Kaag

Une autre solution simple consiste à créer une classe de modèle de base et à en étendre les modèles:

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

Ensuite, remplissez simplement les champs obligatoires dans $ forcéNullFields pour chaque modèle si nécessaire.

1
Vedmant