Laravel Eloquent Update nur, wenn Änderungen vorgenommen wurden

85

Gibt es eine Möglichkeit, einen Datensatz in Laravel mithilfe beredter Modelle zu aktualisieren, wenn eine Änderung an diesem Datensatz vorgenommen wurde? Ich möchte nicht, dass ein Benutzer die Datenbank ohne guten Grund immer wieder anfordert und nur auf die Schaltfläche drückt, um Änderungen zu speichern. Ich habe eine javascriptFunktion, die die Schaltfläche zum Speichern aktiviert und deaktiviert, je nachdem, ob sich etwas auf der Seite geändert hat, aber ich möchte wissen, ob es möglich ist, diese Art von Funktion auch auf der Serverseite auszuführen. Ich weiß, dass ich es selbst schaffen kann (dh ohne auf eine interne Funktionalität des Frameworks einzugehen), indem ich nur überprüfe, ob sich der Datensatz geändert hat, aber bevor ich es auf diese Weise mache, möchte ich wissen, ob sich das beredte Laravel-Modell bereits darum kümmert das, also muss ich das Rad nicht neu erfinden.

So aktualisiere ich einen Datensatz:

$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
quelle
3
Aktivieren Sie die Datenbankprotokollierung und überprüfen Sie die Protokolle, um festzustellen, welche Abfrage Eloquent beim Speichern tatsächlich ausführt: Sie könnten angenehm überrascht sein
Mark Baker
4
Beachten Sie, dass Laravel-Modelle ein dirtyFlag enthalten, mit dem bestimmt wird, ob tatsächlich eine Aktualisierung der Datenbank erforderlich ist. Dieses Flag wird gesetzt, indem die ursprünglichen findSpaltenwerte mit den Werten an dem Punkt verglichen werden, an dem Sie das Speichern ausführen
Mark Baker
@ MarkBaker Das ist ein toller Tipp!
user2755140

Antworten:

175

Du machst es schon!

save()prüft, ob sich etwas im Modell geändert hat. Wenn dies nicht der Fall ist, wird keine Datenbankabfrage ausgeführt.

Hier ist der relevante Teil des Codes in Illuminate\Database\Eloquent\Model@performUpdate:

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

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

    return true;
}

Die getDirty()Methode vergleicht einfach die aktuellen Attribute mit einer Kopie, die beim originalErstellen des Modells gespeichert wurde . Dies geschieht in der syncOriginal()Methode:

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

    $this->syncOriginal();

    $this->fill($attributes);
}

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

    return $this;
}

Wenn Sie überprüfen möchten, ob das Modell verschmutzt ist, rufen Sie einfach an isDirty():

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

Oder wenn Sie ein bestimmtes Attribut überprüfen möchten:

if($product->isDirty('price')){
    // price has changed
}
lukasgeiter
quelle
Das ist großartig. Vielen Dank. Ich frage mich jetzt, wie ich auf diese schmutzige Flagge zugreifen kann, um zu erfahren, ob ein Aktualisierungsversuch durchgeführt wurde, ohne Änderungen vorzunehmen. Ich meine: die Rückkehr für diese Aktion?
user2755140
1
@DaseinA Dafür kannst du verwenden isDirty(). Siehe die aktualisierte Antwort
lukasgeiter
2
Lesen Sie diese Antwort unbedingt durch, bevor Sie sie verwenden isDirty().
Alex
1
Es gibt eine getChanges()Methode. Überprüfen Sie meine Antwort stackoverflow.com/a/54132163/1090395
Mladen Janjetovic
Zu Ihrer Information isDirty()wird es sein, truewenn Sie Ihre Attribute nicht richtig umsetzen . Ich hatte einen Float, der genau der gleiche war wie in der Datenbank. Da ich den Float jedoch mit einer Zeichenfolge (Beispiel "10.50" anstelle von 10.50) festlegte, wurde er als Änderung erkannt und eine Aktualisierung durchgeführt.
Maarten de Graaf
18

Sie können das getChanges()Eloquent-Modell auch nach dem Fortbestehen verwenden.

Mladen Janjetovic
quelle
11

Ich möchte diese Methode hinzufügen. Wenn Sie ein Bearbeitungsformular verwenden, können Sie diesen Code verwenden, um die Änderungen in Ihrer update(Request $request, $id)Funktion zu speichern :

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

Beachten Sie, dass Sie Ihre Eingaben mit demselben Spaltennamen benennen müssen. Die fill()Funktion erledigt die ganze Arbeit für Sie :)

Ahmad Yousef
quelle
3
Dies ist eigentlich die Antwort, nach der ich gesucht habe. Ich habe den Namen vergessen fillund erfahren, wie ich eine Massenanforderung an ihn weiterleiten kann. Vielen Dank! Ich musste die ID allerdings nicht weitergeben, da ich sie aktualisiere, also ist sie bereits da $request->id.
Blamb