Création et mise à jour de Laravel Eloquent

165

Quel est le raccourci pour insérer un nouvel enregistrement ou mettre à jour s'il existe?

<?php

$shopOwner = ShopMeta::where('shopId', '=', $theID)
    ->where('metadataKey', '=', 2001)->first();

if ($shopOwner == null) {
    // Insert new record into database
} else {
    // Update the existing record
}
1myb
la source
Je suppose que ce shopIdn'est pas votre clé primaire, non?
Sergiu Paraschiv
@SergiuParaschiv, oui. ce n'est pas
1myb
Découvrez la réponse de @ErikTheDeveloper. Il montre une belle méthode éloquente intégrée qui devrait faire le travail.
cw24
La même chose est entièrement répondue dans le lien ci-dessous stackoverflow.com/questions/18839941/…
Bashirpour

Réponses:

232

Voici un exemple complet de ce dont parlait "lu cip":

$user = User::firstOrNew(array('name' => Input::get('name')));
$user->foo = Input::get('foo');
$user->save();

Vous trouverez ci-dessous le lien mis à jour de la documentation sur la dernière version de Laravel

Documents ici: lien mis à jour

weotch
la source
1
exactement! 'firstOrNew' existe aussi en 4.0 (non mentionné dans la documentation)
younes0
2
Nous pouvons également vérifier que $ user est nouveau / récupéré en utilisant if ($ user-> exists).
Ryu_hayabusa
1
@Ryu_hayabusa Cela causerait probablement des conditions de course
Chris Harrison
la nouvelle syntaxe semble être updateOrInsert (array $ attributes, array $ values ​​= []) en 5.5: github.com/laravel/framework/blob/5.5/src/Illuminate/Database/…
user1204214
86

Mise à jour: 27 août 2014 - [ updateOrCreateBuilt into core ...]

Juste au cas où les gens rencontreraient encore ceci ... J'ai découvert quelques semaines après avoir écrit ceci, que cela fait en fait partie du noyau d'Eloquent de Laravel ...

Explorer la (les) méthode (s) équivalente (s) d'Eloquent. Vous pouvez voir ici:

https://github.com/laravel/framework/blob/4.2/src/Illuminate/Database/Eloquent/Model.php#L553

sur: 570 et: 553

    /**
     * Create or update a record matching the attributes, and fill it with values.
     *
     * @param  array  $attributes
     * @param  array  $values
     * @return static
     */
    public static function updateOrCreate(array $attributes, array $values = array())
    {
        $instance = static::firstOrNew($attributes);

        $instance->fill($values)->save();

        return $instance;
    }

Ancienne réponse ci-dessous


Je me demande s'il existe une fonctionnalité L4 intégrée pour faire cela d'une manière quelconque, telle que:

$row = DB::table('table')->where('id', '=', $id)->first();
// Fancy field => data assignments here
$row->save();

J'ai créé cette méthode il y a quelques semaines ...

// Within a Model extends Eloquent
public static function createOrUpdate($formatted_array) {
    $row = Model::find($formatted_array['id']);
    if ($row === null) {
        Model::create($formatted_array);
        Session::flash('footer_message', "CREATED");
    } else {
        $row->update($formatted_array);
        Session::flash('footer_message', "EXISITING");
    }
    $affected_row = Model::find($formatted_array['id']);
    return $affected_row;
}

J'espère que cela aide. J'adorerais voir une alternative à cela si quelqu'un en a une à partager. @erikthedev_

Erik Aybar
la source
Il y a et ça s'appelle firstOrNew / firstsOrCreate
malhal
@malcolmhall J'ai mis à jour la réponse ci-dessus. Il s'avère qu'Eloquent a de nombreuses fonctionnalités que je me suis retrouvé à reconstruire;) Toujours bon de passer du temps à parcourir la documentation :)
Erik Aybar
La version 4.2.0 de packagist (stable 2014/6/1) ne contient pas updateOrCreate. Mais on peut l'implémenter en regardant la source. ModelName::firstOrNew(['param' => 'condition'])->fill(Input::get())->save();devrait le faire.
bibstha le
3
Faites juste attention que Laravel ne l'exécute pas comme une transaction, donc si vous avez des clés uniques et qu'un autre utilisateur la crée simultanément avec la même clé, vous pouvez obtenir une exception. Je crois que l'un des avantages de RedBeanPHP est que ce type de chose est fait dans une transaction pour vous.
malhal
Merci d'avoir signalé l'utilisation de fill () Cela m'a beaucoup aidé!
Détente à Chypre le
70

Mise à jour 2020

Comme dans Laravel> = 5.3 , si quelqu'un est toujours curieux de savoir comment le faire de manière simple. C'est possible en utilisant:updateOrCreate() .

Par exemple, pour une question posée, vous pouvez utiliser quelque chose comme:

$matchThese = ['shopId'=>$theID,'metadataKey'=>2001];
ShopMeta::updateOrCreate($matchThese,['shopOwner'=>'New One']);

Le code ci-dessus vérifiera la table représentée par ShopMeta, ce qui sera le plus probable, shop_metassauf indication contraire dans le modèle lui-même

et il essaiera de trouver l'entrée avec

colonne shopId = $theID

et

colonne metadateKey = 2001

et s'il trouve, il mettra à jour la colonne shopOwnerde la ligne trouvée pourNew One .

S'il trouve plus d'une ligne correspondante, il mettra à jour la toute première ligne qui signifie qui a le plus bas primaire id .

S'il n'est pas du tout trouvé, il insérera une nouvelle ligne avec:

shopId = $theID, metadateKey = 2001etshopOwner = New One

Remarque Vérifiez votre modèle $fillableet assurez-vous que vous avez défini chaque nom de colonne que vous souhaitez insérer ou mettre à jour et que les colonnes de repos ont soit la valeur par défaut, soit said colonne incrémentée automatiquement.

Sinon, cela générera une erreur lors de l'exécution de l'exemple ci-dessus:

Illuminate\Database\QueryException with message 'SQLSTATE[HY000]: General error: 1364 Field '...' doesn't have a default value (SQL: insert into `...` (`...`,.., `updated_at`, `created_at`) values (...,.., xxxx-xx-xx xx:xx:xx, xxxx-xx-xx xx:xx:xx))'

Comme il y aurait un champ qui aura besoin de valeur lors de l'insertion d'une nouvelle ligne et cela ne sera pas possible car $fillableil n'est pas défini dans ou il n'a pas de valeur par défaut.

Pour plus de références, veuillez consulter la documentation Laravel sur: https://laravel.com/docs/5.3/eloquent

Un exemple à partir de là est:

// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.
$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99]
);

ce qui efface à peu près tout.

Mise à jour du générateur de requêtes

Quelqu'un a demandé s'il était possible d'utiliser Query Builder dans Laravel. Voici la référence pour Query Builder de la documentation Laravel.

Query Builder fonctionne exactement de la même manière que Eloquent, donc tout ce qui est vrai pour Eloquent l'est également pour Query Builder. Donc, pour ce cas spécifique, utilisez simplement la même fonction avec votre générateur de requêtes comme ceci:

$matchThese = array('shopId'=>$theID,'metadataKey'=>2001);
DB::table('shop_metas')::updateOrCreate($matchThese,['shopOwner'=>'New One']);

Bien sûr, n'oubliez pas d'ajouter une façade DB:

use Illuminate\Support\Facades\DB;

OU

use DB;

J'espère que ça aide

Apprenant
la source
Qu'en est-il du générateur de requêtes?
Sky
Qu'en est-il? :)
Apprenant
Je veux faire la même chose avec Query Builder. Pas éloquent. C'est possible?
Sky
J'ai mis à jour ma réponse, recherchez la section "Mise à jour du générateur de requêtes" dans la réponse ci-dessus.
Apprenant
J'ai essayé la méthode DB :: table ('shop_metas') :: updateOrCreate mais cela me donne l'erreur suivante BadMethodCallException dans Macroable.php ligne 59: La méthode updateOrInsert n'existe pas. Même si j'utilise DB;
Swapnil Shende
17

Fonction de sauvegarde:

$shopOwner->save()

fais déjà ce que tu veux ...

Code Laravel:

    // If the model already exists in the database we can just update our record
    // that is already in this database using the current IDs in this "where"
    // clause to only update this model. Otherwise, we'll just insert them.
    if ($this->exists)
    {
        $saved = $this->performUpdate($query);
    }

    // If the model is brand new, we'll insert it into our database and set the
    // ID attribute on the model to the value of the newly inserted row's ID
    // which is typically an auto-increment value managed by the database.
    else
    {
        $saved = $this->performInsert($query);
    }
lu cip
la source
6
Cela ne ressemble pas à une opération d'upsert atomique. Si ce n'est pas le cas, cela peut entraîner des conditions de concurrence.
Emil Vikström le
Ce code sert à vérifier si le modèle est chargé à partir de la base de données ou s'il s'agit d'un modèle basé sur la mémoire. Update ou Create nécessite une définition explicite des colonnes clés à vérifier et ne peut pas être effectuée implicitement.
AMIB
17

firstOrNewcréera un enregistrement s'il n'existe pas et met à jour une ligne si elle existe déjà. Vous pouvez également utiliser updateOrCreateici est l'exemple complet

$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99]
); 

S'il y a un vol d'Oakland à San Diego, fixez le prix à 99 $. s'il n'existe pas créer une nouvelle ligne

Doc de référence ici: ( https://laravel.com/docs/5.5/eloquent )

Saeed Ansari
la source
7

Si vous avez besoin des mêmes fonctionnalités en utilisant DB, dans Laravel, >= 5.5vous pouvez utiliser:

DB::table('table_name')->updateOrInsert($attributes, $values);

ou la version abrégée quand $attributeset $valuessont les mêmes:

DB::table('table_name')->updateOrInsert($values);
Sasa Blagojevic
la source
6
$shopOwner = ShopMeta::firstOrNew(array('shopId' => $theID,'metadataKey' => 2001));

Effectuez ensuite vos modifications et enregistrez-les. Notez que firstOrNew ne fait pas l'insertion s'il n'est pas trouvé, si vous en avez besoin, alors son firstOrCreate.

malhal
la source
2

Une option de plus si votre identifiant n'est pas auto-incrémenté et que vous savez lequel insérer / mettre à jour:

$object = MyModel::findOrNew($id);
//assign attributes to update...
$object->save();
vivoconunxino
la source
2

Comme la méthode firstOrCreate, updateOrCreate persiste le modèle, il n'est donc pas nécessaire d'appeler save ()

// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.

$flight = App\Flight::updateOrCreate(
   ['departure' => 'Oakland', 'destination' => 'San Diego'],
   ['price' => 99]
);

Et pour votre problème

$shopOwner = ShopMeta::updateOrCreate(
   ['shopId' => $theID, 'metadataKey' => '2001'],
   ['other field' => 'val' ,'other field' => 'val', ....]
);
Bashirpour
la source
1

En fait, firstOrCreate ne serait pas mis à jour au cas où le registre existe déjà dans la base de données. J'ai amélioré un peu la solution d'Erik car j'avais en fait besoin de mettre à jour une table qui a des valeurs uniques non seulement pour la colonne "id"

/**
 * If the register exists in the table, it updates it. 
 * Otherwise it creates it
 * @param array $data Data to Insert/Update
 * @param array $keys Keys to check for in the table
 * @return Object
 */
static function createOrUpdate($data, $keys) {
    $record = self::where($keys)->first();
    if (is_null($record)) {
        return self::create($data);
    } else {
        return self::where($keys)->update($data);
    }
}

Ensuite, vous l'utiliseriez comme ceci:

Model::createOrUpdate(
        array(
    'id_a' => 1,
    'foo' => 'bar'
        ), array(
    'id_a' => 1
        )
);
Juancho Ramone
la source
ce qui était bien de ne pas faire cela: 1. Supprimer en fonction de la clé, et 2. créer avec de nouvelles valeurs. C'étaient encore 2 opérations. est-ce pour gagner du temps pour l'indexation en cas de création et de suppression?
Hafiz
1

comme @JuanchoRamone posté ci-dessus (merci @Juancho) c'est très utile pour moi, mais si vos données sont un tableau, vous devriez modifier un peu comme ceci:

public static function createOrUpdate($data, $keys) {
    $record = self::where($keys)->first();
    if (is_null($record)) {
        return self::create($data);
    } else {
        return $record->update($data);
    }
}
Duy Ngo
la source
Juste une note rapide que cela devrait être updateOrCreate au lieu de createOrUpdate
John Shipp
Ok mais s'il y a 1000 lignes, ce seront 1000 requêtes en cours?
Marcelo Agimóvel
0

N'est-ce pas la même chose que updateOrCreate ()?

C'est similaire mais pas pareil. UpdateOrCreate () ne fonctionnera que pour une ligne à la fois, ce qui n'autorise pas l'insertion en bloc. InsertOnDuplicateKey fonctionnera sur de nombreuses lignes.

https://github.com/yadakhov/insert-on-duplicate-key

Pax Exterminatus
la source
-2

vérifier si un utilisateur existe ou non. Sinon insérer

$exist = DB::table('User')->where(['username'=>$username,'password'=>$password])->get();
if(count($exist)  >0) {
    echo "User already exist";;
}
else  {
    $data=array('username'=>$username,'password'=>$password);
    DB::table('User')->insert($data);
}
Laravel 5.4           
Sabrina Abid
la source
Bienvenue à SO. Jetez un œil à cette solution pour fournir une réponse de qualité. ---
thewaywewere
Veuillez également baliser le framework que vous utilisez, la version php, la base de données.
Jason Joslin
1
J'utilise Laravel 5.4, php7 et mysql
Sabrina Abid
Sabrina Ce n'est pas une solution idéale car une fonction existe déjà dans laravel pour le faire. Mais la vôtre est une solution générale
djangodude
Sa méthode old school laravel a déjà une fonction pour cela. Voir la réponse sélectionnée
Saeed Ansari