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
}

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


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_


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


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

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 )


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

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.


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

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', ....]
);

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

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

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


-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           

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.