web-dev-qa-db-fra.com

Mise à jour de masse du modèle Eloquent

S'il vous plaît, corrigez-moi si je me trompe, mais je pense qu'il n'existe pas de mise à jour massive dans un modèle Eloquent.

Existe-t-il un moyen d'effectuer une mise à jour en masse sur la table de base de données sans émettre de requête pour chaque ligne?

Par exemple, y at-il une méthode statique, quelque chose comme 

User::updateWhere(
    array('age', '<', '18'),
    array(
        'under_18' => 1 
        [, ...]
    )
);

(oui, c'est un exemple idiot mais vous obtenez l'image ...)

Pourquoi une telle fonctionnalité n'est-elle pas implémentée? Suis-je le seul qui serait très heureux si quelque chose comme ça se présente?

Je (les développeurs), ne voudrais pas l'implémenter comme:

DB::table('users')->where('age', '<', '18')->update(array('under_18' => 1));

car, au fur et à mesure que le projet se développe, nous pouvons demander aux programmeurs de changer le nom de la table à l'avenir et ils ne peuvent pas rechercher et remplacer le nom de la table!

Existe-t-il une telle méthode statique pour effectuer cette opération? Et s'il n'y en a pas, pouvons-nous étendre la classe Illuminate\Database\Eloquent\Model pour accomplir une telle chose?

29
papas-source

Pour les fonctionnalités de mise à jour/insertion en masse, cela a été demandé, mais Taylor Otwell (auteur de Laravel) suggère aux utilisateurs d’utiliser plutôt le générateur de requêtes. https://github.com/laravel/framework/issues/1295

Vos modèles doivent généralement étendre Illuminate\Database\Eloquent\Model. Ensuite, vous accédez à l'entité iself, par exemple si vous avez ceci:

<?php
Use Illuminate\Database\Eloquent\Model;

class User extends Model {

    // table name defaults to "users" anyway, so this definition is only for
    // demonstration on how you can set a custom one
    protected $table = 'users';
    // ... code omited ...

Mise à jour # 2

Vous devez avoir recours au constructeur de requêtes. Pour couvrir le problème de nommage des tables, vous pouvez l’obtenir de manière dynamique via la méthode getTable (). La seule limite à cela est que vous devez initialiser votre classe d'utilisateurs avant de pouvoir utiliser cette fonction. Votre requête serait la suivante:

$userTable = (new User())->getTable();
DB::table($userTable)->where('age', '<', 18)->update(array('under_18' => 1));

De cette façon, votre nom de table est controller dans le modèle User (comme illustré dans l'exemple ci-dessus).

Mise à jour n ° 1

Une autre façon de le faire (pas efficace dans votre situation) serait:

$users = User::where('age', '<', 18)->get();
foreach ($users as $user) {
    $user->field = value;
    $user->save();
}

De cette façon, le nom de la table est conservé dans la classe des utilisateurs et vos développeurs n'ont pas à s'en soucier.

34
phoops

Cela n’était peut-être pas possible il ya quelques années, mais dans les versions récentes de Laravel, vous pouvez certainement le faire:

User::where('age', '<', 18)->update(['under_18' => 1]);

Il est à noter que vous avez besoin de la méthode where avant d'appeler update.

39
bryceadams

Utilisez les transactions de base de données pour mettre à jour plusieurs entités en bloc. La transaction sera validée lorsque votre fonction de mise à jour sera terminée ou annulée si une exception survient quelque part entre les deux.

https://laravel.com/docs/5.4/database#database-transactions

Par exemple, voici comment je régénère des slugs de chemin matérialisés ( https://communities.bmc.com/docs/DOC-9902 ) pour des articles dans une seule mise à jour en bloc:

public function regenerateDescendantsSlugs(Model $parent, $old_parent_slug)
    {
        $children = $parent->where('full_slug', 'like', "%/$old_parent_slug/%")->get();

        \DB::transaction(function () use ($children, $parent, $old_parent_slug) {
            /** @var Model $child */
            foreach ($children as $child) {
                $new_full_slug  = $this->regenerateSlug($parent, $child);
                $new_full_title = $this->regenerateTitle($parent, $child);

                \DB::table($parent->getTable())
                    ->where('full_slug', '=', $child->full_slug)
                    ->update([
                        'full_slug' => $new_full_slug,
                        'full_title' => $new_full_title,
                    ]);
            }
        });
    }
3
metamaker

Une petite correction à la réponse de @metamaker:

DB::beginTransaction();
     // do all your updates here

        foreach ($users as $user) {

            $new_value = Rand(1,10) // use your own criteria

            DB::table('users')
                ->where('id', '=', $user->id)
                ->update([
                    'status' => $new_value  // update your field(s) here
                ]);
        }
    // when done commit
DB::commit();

Vous pouvez maintenant avoir 1 million de mises à jour différentes dans une transaction de base de données

0
catalin87