Tworzenie i aktualizacja Laravel Eloquent

165

Jaki jest skrót dotyczący wstawiania nowego rekordu lub aktualizacji, jeśli istnieje?

<?php

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

if ($shopOwner == null) {
    // Insert new record into database
} else {
    // Update the existing record
}
1myb
źródło
Domyślam się, że shopIdnie jest to twój klucz podstawowy, prawda?
Sergiu Paraschiv
@SergiuParaschiv, tak. to nie jest
miliony
Sprawdź odpowiedź od @ErikTheDeveloper. Pokazuje ładnie osadzoną, elokwentną metodę, która powinna wykonać zadanie.
cw24
Dokładną odpowiedź na to samo można znaleźć w poniższym linku stackoverflow.com/questions/18839941/ ...
Bashirpour

Odpowiedzi:

232

Oto pełny przykład tego, o czym mówił „lu cip”:

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

Poniżej znajduje się zaktualizowany link do dokumentów, które są w najnowszej wersji Laravel

Dokumenty tutaj: Zaktualizowany link

weotch
źródło
1
dokładnie! „firstOrNew” istnieje również w wersji 4.0 (nie wspomniano w dokumentacji)
younes0
2
Możemy również sprawdzić, czy $ user jest nowy / pobrany, używając if ($ user-> istnieje).
Ryu_hayabusa
1
@Ryu_hayabusa To prawdopodobnie spowodowałoby warunki wyścigu
Chris Harrison
nowa składnia wydaje się być updateOrInsert (atrybuty $ tablicy, wartości $ tablicy = []) w 5.5: github.com/laravel/framework/blob/5.5/src/Illuminate/Database/…
user1204214
86

Zaktualizowano: 27 sierpnia 2014 r. - [ updateOrCreateWbudowany w rdzeń ...]

Na wypadek, gdyby ludzie wciąż się z tym spotykali ... dowiedziałem się kilka tygodni po napisaniu tego, że jest to w rzeczywistości część rdzenia Laravel's Eloquent ...

Zagłębianie się w równoważne metody Eloquent. Możesz zobaczyć tutaj:

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

dnia: 570 i: 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;
    }

Stara odpowiedź poniżej


Zastanawiam się, czy jest jakaś wbudowana funkcjonalność L4 do zrobienia tego w jakiś sposób, na przykład:

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

Stworzyłem tę metodę kilka tygodni temu ...

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

Mam nadzieję że to pomogło. Chciałbym zobaczyć alternatywę dla tego, jeśli ktoś ma coś do udostępnienia. @erikthedev_

Erik Aybar
źródło
Jest i nazywa się firstOrNew / firstsOrCreate
malhal
@malcolmhall Zaktualizowałem powyższą odpowiedź. Okazuje się, że Eloquent ma wiele funkcji, które odbudowałem;) Zawsze dobrze jest spędzić trochę czasu na przeglądaniu dokumentacji :)
Erik Aybar
Packagist's 4.2.0 (stabilny 2014/6/1) nie zawiera updateOrCreate. Ale można to zrealizować patrząc na źródło. ModelName::firstOrNew(['param' => 'condition'])->fill(Input::get())->save();powinien to zrobić.
bibstha
3
Uważaj tylko, że Laravel nie uruchamia tego jako transakcji, więc jeśli masz unikalne klucze i inny użytkownik utworzy je z tym samym kluczem jednocześnie, możesz otrzymać wyjątek. Uważam, że jedną z zalet RedBeanPHP jest to, że tego typu transakcje są dokonywane za Ciebie.
malhal
Dziękuję za zwrócenie uwagi na użycie funkcji fill (). To bardzo mi pomogło!
Relaks na Cyprze
70

Aktualizacja 2020

Podobnie jak w Laravel> = 5.3 , jeśli ktoś nadal jest ciekawy, jak to zrobić w łatwy sposób. Jest to możliwe za pomocą: updateOrCreate().

Na przykład do zadanego pytania możesz użyć czegoś takiego:

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

Powyższy kod sprawdzi tabelę reprezentowaną przez ShopMeta, co najprawdopodobniej będzie miało miejsce, shop_metaschyba że w samym modelu nie określono inaczej

i spróbuje znaleźć wpis z

kolumna shopId = $theID

i

kolumna metadateKey = 2001

a jeśli znajdzie, zaktualizuje kolumnę shopOwnerznalezionego wiersza do New One.

Jeśli znajdzie więcej niż jeden pasujący wiersz, zaktualizuje pierwszy wiersz, co oznacza, że ​​ma najniższy podstawowy id.

Jeśli w ogóle nie zostanie znaleziony, wstawi nowy wiersz z:

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

Uwaga Sprawdź swój model $fillablei upewnij się, że masz zdefiniowaną nazwę każdej kolumny, którą chcesz wstawić lub zaktualizować, a pozostałe kolumny mają wartość domyślną lub idautomatycznie zwiększaną.

W przeciwnym razie przy wykonywaniu powyższego przykładu pojawi się błąd:

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))'

Ponieważ byłoby pole, które będzie wymagało podania wartości podczas wstawiania nowego wiersza i nie będzie to możliwe, ponieważ albo nie jest zdefiniowane w, $fillablealbo nie ma wartości domyślnej.

Więcej informacji można znaleźć w dokumentacji Laravel pod adresem : https://laravel.com/docs/5.3/eloquent

Oto jeden przykład:

// 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]
);

co prawie wszystko wyjaśnia.

Aktualizacja narzędzia do tworzenia zapytań

Ktoś zapytał, czy jest to możliwe za pomocą Query Builder w Laravel. Tutaj jest odniesienie do Query Builder z Laravel Docs.

Query Builder działa dokładnie tak samo jak Eloquent, więc wszystko, co jest prawdziwe dla Eloquent, jest prawdziwe również dla Query Builder. W tym konkretnym przypadku po prostu użyj tej samej funkcji w swoim konstruktorze zapytań:

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

Oczywiście nie zapomnij dodać fasady DB:

use Illuminate\Support\Facades\DB;

LUB

use DB;

Mam nadzieję, że to pomoże

Uczeń
źródło
A co z narzędziem do tworzenia zapytań?
Sky
Co z tym ? :)
Learner
Chcę zrobić to samo z Konstruktorem zapytań. Nie jest wymowny. Czy to możliwe?
Sky
Zaktualizowałem moją odpowiedź, poszukaj sekcji „Aktualizacja narzędzia do tworzenia zapytań” w powyższej odpowiedzi.
Uczeń
Próbowałem DB :: table ('shop_metas') :: updateOrCreate, ale to daje mi następujący błąd BadMethodCallException w linii 59 Macroable.php: Metoda updateOrInsert nie istnieje. Mimo że używam DB;
Swapnil Shende
17

Funkcja zapisywania:

$shopOwner->save()

już rób co chcesz ...

Kod 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
źródło
6
To nie wygląda na atomową operację w górę. Jeśli tak nie jest, może to spowodować warunki wyścigu.
Emil Vikström
Ten kod służy do sprawdzenia, czy model jest ładowany z bazy danych lub jest modelem opartym na pamięci. Aktualizacja lub tworzenie wymaga jawnej definicji kolumn kluczy do sprawdzenia i nie można jej wykonać niejawnie.
AMIB
17

firstOrNewutworzy rekord, jeśli nie istnieje, i zaktualizuje wiersz, jeśli już istnieje. Możesz również użyć updateOrCreatetutaj pełnego przykładu

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

Jeśli jest lot z Oakland do San Diego, ustaw cenę na 99 USD. jeśli nie istnieje, utwórz nowy wiersz

Dokument referencyjny tutaj: ( https://laravel.com/docs/5.5/eloquent )

Saeed Ansari
źródło
7

Jeśli potrzebujesz tej samej funkcjonalności korzystając z DB, w Laravel >= 5.5możesz użyć:

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

lub wersja skrócona, gdy $attributesi $valuessą takie same:

DB::table('table_name')->updateOrInsert($values);
Sasa Blagojevic
źródło
6
$shopOwner = ShopMeta::firstOrNew(array('shopId' => $theID,'metadataKey' => 2001));

Następnie wprowadź zmiany i zapisz. Zauważ, że firstOrNew nie wykonuje wstawiania, jeśli nie został znaleziony, jeśli tego potrzebujesz, to jego firstOrCreate.

malhal
źródło
2

Jeszcze jedna opcja, jeśli Twój identyfikator nie jest automatycznie zwiększany i wiesz, który z nich wstawić / zaktualizować:

$object = MyModel::findOrNew($id);
//assign attributes to update...
$object->save();
vivoconunxino
źródło
2

Podobnie jak metoda firstOrCreate, updateOrCreate utrwala model, więc nie ma potrzeby wywoływania metody 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]
);

I dla twojego problemu

$shopOwner = ShopMeta::updateOrCreate(
   ['shopId' => $theID, 'metadataKey' => '2001'],
   ['other field' => 'val' ,'other field' => 'val', ....]
);
Bashirpour
źródło
1

W rzeczywistości firstOrCreate nie aktualizowałoby się w przypadku, gdy rejestr już istnieje w bazie danych. Poprawiłem nieco rozwiązanie Erika, ponieważ faktycznie potrzebowałem zaktualizować tabelę, która ma unikalne wartości nie tylko dla kolumny "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);
    }
}

Wtedy użyłbyś tego w ten sposób:

Model::createOrUpdate(
        array(
    'id_a' => 1,
    'foo' => 'bar'
        ), array(
    'id_a' => 1
        )
);
Juancho Ramone
źródło
co było dobrego w tym, że tego nie robiłem: 1. Usuń na podstawie klucza i 2. utwórz z nowymi wartościami. To były nadal 2 operacje. czy ma to zaoszczędzić czas na indeksowanie w przypadku tworzenia i usuwania?
Hafiz
1

jak @JuanchoRamone zamieszczone powyżej (dzięki @Juancho) jest to dla mnie bardzo przydatne, ale jeśli twoje dane są tablicami, powinieneś zmodyfikować trochę w ten sposób:

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
źródło
Tylko krótka uwaga, że ​​powinno to być updateOrCreate zamiast createOrUpdate
John Shipp
Ok, ale jeśli jest 1000 wierszy, będzie to 1000 uruchomionych zapytań?
Marcelo Agimóvel
0

Czy to nie to samo, co updateOrCreate ()?

To jest podobne, ale nie to samo. UpdateOrCreate () będzie działać tylko dla jednego wiersza naraz, co nie pozwala na zbiorcze wstawianie. InsertOnDuplicateKey będzie działać na wielu wierszach.

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

Pax Exterminatus
źródło
-2

sprawdź, czy użytkownik istnieje, czy nie. Jeśli nie, włóż

$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
źródło
Witamy w SO. Zapoznaj się z tym, jak odpowiedzieć, aby udzielić dobrej odpowiedzi. ---
thewaywewewere
Proszę również oznaczyć framework, którego używasz, wersję php, bazę danych.
Jason Joslin
1
używam Laravel 5.4, php7 i mysql
Sabrina Abid
Sabrina Nie jest to idealne rozwiązanie, ponieważ w Laravel istnieją już funkcje do tego. Ale twoje jest rozwiązanie ogólne
djangodude
Jej oldschoolowa metoda laravel ma już w tym celu funkcję. Zobacz wybraną odpowiedź
Saeed Ansari