Aktualizacja Laravel Eloquent, tylko jeśli zostały wprowadzone zmiany

86

Czy istnieje sposób, aby zaktualizować rekord w Laravel przy użyciu elokwentnych modeli, tylko jeśli wprowadzono zmianę w tym rekordzie? Nie chcę, aby jakikolwiek użytkownik żądał bazy danych bez dobrego powodu w kółko, po prostu naciskając przycisk, aby zapisać zmiany. Mam javascriptfunkcję, która włącza i wyłącza przycisk zapisywania w zależności od tego, czy coś się zmieniło na stronie, ale chciałbym wiedzieć, czy jest możliwe, aby zrobić tego rodzaju funkcję również po stronie serwera. Wiem, że mogę to zrobić sam (czyli: bez odwoływania się do wewnętrznej funkcjonalności frameworka) po prostu sprawdzając, czy rekord się zmienił, ale zanim to zrobię, chciałbym wiedzieć, czy elokwentny model Laravela już dba o to to, więc nie muszę ponownie wymyślać koła.

Oto sposób, w jaki aktualizuję rekord:

$product = Product::find($data["id"]);
$product->title = $data["title"];
$product->description = $data["description"];
$product->price = $data["price"];
//etc (string values were previously sanitized for xss attacks)
$product->save();
user2755140
źródło
3
Dlaczego nie włączyć rejestrowania bazy danych, a następnie sprawdzić dzienniki, aby zobaczyć, jakie zapytanie faktycznie wykonuje Eloquent podczas zapisywania: możesz być mile zaskoczony
Mark Baker
4
Zwróć uwagę, że modele Laravel posiadają dirtyflagę, która jest używana do określenia, czy aktualizacja bazy danych jest rzeczywiście wymagana, a ta flaga jest ustawiana przez porównanie oryginalnych findwartości kolumn z wartościami w punkcie, w którym wykonujesz zapis
Mark Baker
@MarkBaker To świetna wskazówka!
user2755140

Odpowiedzi:

177

Już to robisz!

save()sprawdzi czy coś w modelu się zmieniło. Jeśli tak nie jest, nie uruchomi zapytania db.

Oto odpowiednia część kodu w Illuminate\Database\Eloquent\Model@performUpdate:

protected function performUpdate(Builder $query, array $options = [])
{
    $dirty = $this->getDirty();

    if (count($dirty) > 0)
    {
        // runs update query
    }

    return true;
}

getDirty()Metoda po prostu porównuje bieżące atrybuty z kopii zapisanej w originalkiedy model jest tworzony. Odbywa się to syncOriginal()metodą:

public function __construct(array $attributes = array())
{
    $this->bootIfNotBooted();

    $this->syncOriginal();

    $this->fill($attributes);
}

public function syncOriginal()
{
    $this->original = $this->attributes;

    return $this;
}

Jeśli chcesz sprawdzić czy model nie jest zabrudzony to zadzwoń isDirty():

if($product->isDirty()){
    // changes have been made
}

Lub jeśli chcesz sprawdzić określony atrybut:

if($product->isDirty('price')){
    // price has changed
}
lukasgeiter
źródło
To wspaniale. Dziękuję Ci. Zastanawiam się teraz, jak mogę uzyskać dostęp do tej brudnej flagi, aby wiedzieć, czy próba aktualizacji została podjęta bez dokonywania jakichkolwiek zmian? Mam na myśli: zwrot za tę akcję?
user2755140
1
@DaseinA Możesz isDirty()do tego użyć . Zobacz zaktualizowaną odpowiedź
lukasgeiter
2
Dla tych, którzy to czytają, koniecznie sprawdź tę odpowiedź przed użyciem isDirty().
Alex,
1
Jest getChanges()metoda. Sprawdź moją odpowiedź stackoverflow.com/a/54132163/1090395
Mladen Janjetovic
FYI isDirty()będzie, truejeśli nie rzucisz dobrze swoich atrybutów. Miałem float, który był dokładnie taki sam jak w bazie danych, jednak ponieważ ustawiałem float za pomocą łańcucha (na przykład „10,50” zamiast 10,50), wykryłby to jako zmianę i wykonał aktualizację.
Maarten de Graaf
18

Możesz używać getChanges()na modelu Eloquent nawet po wytrwałości.

Mladen Janjetovic
źródło
11

Lubię dodawać tę metodę, jeśli korzystasz z formularza edycji, możesz użyć tego kodu, aby zapisać zmiany w swojej update(Request $request, $id)funkcji:

$post = Post::find($id);    
$post->fill($request->input())->save();

pamiętaj, że musisz nazwać swoje dane wejściowe taką samą nazwą kolumny. fill()Funkcja wykona całą pracę za Ciebie :)

Ahmad Yousef
źródło
3
To jest właściwie odpowiedź, której szukałem, zapomniałem o nazwie filli jak masowo przekazać do niej żądanie. Dzięki! Nie musiałem jednak podawać identyfikatora, ponieważ jestem aktualizowany, więc już tam jest $request->id.
blamb