Atualização do Laravel Eloquent apenas se mudanças foram feitas

86

Existe alguma maneira de atualizar um registro no Laravel usando modelos eloquent apenas se uma alteração foi feita naquele registro? Não quero nenhum usuário solicitando o banco de dados sem um bom motivo repetidamente, apenas apertando o botão para salvar as alterações. Tenho uma javascriptfunção que habilita e desabilita o botão Salvar de acordo com se algo mudou na página, mas gostaria de saber se é possível ter certeza de fazer esse tipo de recurso no servidor também. Eu sei que posso fazer isso sozinho (ou seja: sem apelar para uma funcionalidade interna do framework) apenas verificando se o registro mudou, mas antes de fazer dessa forma, gostaria de saber se o modelo eloquent do Laravel já cuida isso, então não preciso reinventar a roda.

É assim que eu uso para atualizar um 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();
user2755140
fonte
3
Por que não habilitar o registro do banco de dados e verificar os registros para ver qual consulta o Eloquent realmente executa quando você salva: você pode ficar agradavelmente surpreso
Mark Baker
4
Observe que os modelos do Laravel possuem um dirtysinalizador que é usado para determinar se uma atualização no banco de dados é realmente necessária, e este sinalizador é definido comparando os findvalores da coluna original com os valores no ponto onde você executa o salvamento
Mark Baker
@MarkBaker Essa é uma ótima dica!
user2755140

Respostas:

176

Você já está fazendo isso!

save()irá verificar se algo mudou no modelo. Se não tiver, não executará uma consulta de banco de dados.

Esta é a parte relevante do código em Illuminate\Database\Eloquent\Model@performUpdate:

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

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

    return true;
}

O getDirty()método simplesmente compara os atributos atuais com uma cópia salva originalquando o modelo é criado. Isso é feito no 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;
}

Se quiser verificar se o modelo está sujo é só ligar isDirty():

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

Ou se você deseja verificar um determinado atributo:

if($product->isDirty('price')){
    // price has changed
}
lukasgeiter
fonte
Isso é ótimo. Obrigado. Gostaria de saber agora como posso acessar esse flag sujo para saber se uma tentativa de atualização foi feita sem fazer nenhuma alteração? Quer dizer: o retorno dessa ação?
user2755140
1
@DaseinA Você pode usar isDirty()para isso. Veja a resposta atualizada
lukasgeiter
2
Para aqueles que estão lendo isso, certifique-se de verificar esta resposta antes de usar isDirty().
Alex
1
Existe um getChanges()método. Verifique minha resposta stackoverflow.com/a/54132163/1090395
Mladen Janjetovic
FYI isDirty()será truese você não definir seus atributos corretamente. Eu tinha um float que era exatamente igual ao do banco de dados, no entanto, como eu estava configurando o float com uma string (exemplo "10.50" em vez de 10.50), ele o detectaria como uma alteração e faria uma atualização.
Maarten de Graaf
18

Você pode usar getChanges()no modelo Eloquent mesmo depois de persistir.

Mladen Janjetovic
fonte
11

Eu gosto de adicionar este método, se você estiver usando um formulário de edição, você pode usar este código para salvar as alterações em sua update(Request $request, $id)função:

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

tenha em mente que você deve nomear suas entradas com o mesmo nome de coluna. A fill()função fará todo o trabalho para você :)

Ahmad Yousef
fonte
3
Esta é realmente a resposta que eu estava procurando, esqueci o nome fille como enviar a solicitação em massa para ele. Obrigado! Não precisei passar o ID, pois estou atualizando, então já está lá $request->id.
blamb