Actualización de Laravel Eloquent solo si se han realizado cambios

86

¿Hay alguna forma de actualizar un registro en Laravel usando modelos elocuentes solo si se ha realizado un cambio en ese registro? No quiero que ningún usuario solicite la base de datos sin una buena razón una y otra vez, simplemente presionando el botón para guardar los cambios. Tengo una javascriptfunción que habilita y deshabilita el botón de guardar según si algo ha cambiado en la página, pero me gustaría saber si es posible asegurarme de hacer este tipo de función también en el lado del servidor. Sé que puedo lograrlo por mí mismo (es decir: sin apelar a una funcionalidad interna del marco) simplemente verificando si el registro ha cambiado, pero antes de hacerlo de esa manera, me gustaría saber si el modelo elocuente de Laravel ya se encarga de eso, así que no necesito reinventar la rueda.

Esta es la forma en que uso para actualizar un registro:

$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();
usuario2755140
fuente
3
¿Por qué no habilitar el registro de la base de datos y luego verificar los registros para ver qué consulta Eloquent realmente ejecuta cuando guarda? Puede que se sorprenda gratamente
Mark Baker
4
Tenga en cuenta que los modelos de Laravel tienen una dirtybandera que se usa para determinar si realmente se requiere una actualización en la base de datos, y esta bandera se establece comparando los findvalores de columna originales con los valores en el punto donde ejecuta el guardado
Mark Baker
@MarkBaker ¡Ese es un gran consejo!
user2755140

Respuestas:

176

¡Ya lo estás haciendo!

save()Verificará si algo en el modelo ha cambiado. Si no es así, no ejecutará una consulta db.

Aquí está la parte relevante del código en Illuminate\Database\Eloquent\Model@performUpdate:

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

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

    return true;
}

El getDirty()método simplemente compara los atributos actuales con una copia guardada originalcuando se crea el modelo. Esto se hace en el syncOriginal()método:

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

    $this->syncOriginal();

    $this->fill($attributes);
}

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

    return $this;
}

Si desea verificar si el modelo está sucio, simplemente llame a isDirty():

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

O si desea verificar un determinado atributo:

if($product->isDirty('price')){
    // price has changed
}
lukasgeiter
fuente
Eso es genial. Gracias. Me pregunto ahora cómo puedo acceder a esa bandera sucia para saber si se realizó un intento de actualización sin realizar ningún cambio. Quiero decir: ¿el regreso de esa acción?
user2755140
1
@DaseinA Puede usar isDirty()para eso. Vea la respuesta actualizada
lukasgeiter
2
Para aquellos que lean esto, asegúrese de revisar esta respuesta antes de usar isDirty().
Alex
1
Hay un getChanges()método. Verifique mi respuesta stackoverflow.com/a/54132163/1090395
Mladen Janjetovic
Para tu información isDirty()lo será truesi no lanzas bien tus atributos. Tenía un flotador que era exactamente el mismo que en la base de datos, sin embargo, debido a que estaba configurando el flotador con una cadena (ejemplo "10.50" en lugar de 10.50), lo detectaría como un cambio y realizaría una actualización.
Maarten de Graaf
18

Puede usar getChanges()en el modelo Eloquent incluso después de persistir.

Mladen Janjetovic
fuente
11

Me gusta agregar este método, si está usando un formulario de edición, puede usar este código para guardar los cambios en su update(Request $request, $id)función:

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

tenga en cuenta que debe nombrar sus entradas con el mismo nombre de columna. La fill()función hará todo el trabajo por ti :)

Ahmad Yousef
fuente
3
Esta es en realidad la respuesta que estaba buscando, olvidé el nombre filly cómo enviarle una solicitud en masa. ¡Gracias! Sin embargo, no necesitaba pasar la identificación, ya que estoy actualizando, por lo que ya está allí $request->id.
culpa