Criando e atualizando o Laravel Eloquent


165

Qual é a forma abreviada de inserir um novo registro ou atualizar, se existir?

<?php

$shopOwner = ShopMeta::where('shopId', '=', $theID)
    ->where('metadataKey', '=', 2001)->first();

if ($shopOwner == null) {
    // Insert new record into database
} else {
    // Update the existing record
}

Acho que shopIdnão é sua chave primária, certo?
Sergiu Paraschiv

@SergiuParaschiv, sim. não é
1myb

Confira a resposta de @ErikTheDeveloper. Ele mostra um bom método eloqüente incorporado que deve fazer o trabalho.
Cw24 07/07/2015

A mesma coisa é respondida exatamente no link abaixo stackoverflow.com/questions/18839941/…
Bashirpour

Respostas:


232

Aqui está um exemplo completo do que "lu cip" estava falando:

$user = User::firstOrNew(array('name' => Input::get('name')));
$user->foo = Input::get('foo');
$user->save();

Abaixo está o link atualizado dos documentos que estão na versão mais recente do Laravel

Documentos aqui: link atualizado


1
exatamente! 'firstOrNew' também existe na versão 4.0 (não mencionada na documentação)
younes0

2
Também podemos verificar que $ user é novo / recuperado usando if ($ user-> existe).
Ryu_hayabusa

1
@Ryu_hayabusa Isso provavelmente causaria condições de corrida
Chris Harrison

nova sintaxe parece ser updateOrInsert (array $ atributos, array $ valores = []) em 5.5: github.com/laravel/framework/blob/5.5/src/Illuminate/Database/...
user1204214

86

Atualizado: 27 de agosto de 2014 - [ updateOrCreateCriada no núcleo ...]

Apenas para o caso de as pessoas ainda se depararem com isso ... descobri algumas semanas depois de escrever isso, que isso faz parte do núcleo do Eloquent do Laravel ...

Indo para o (s) método (s) equivalente (s) da Eloquent. Você pode ver aqui:

https://github.com/laravel/framework/blob/4.2/src/Illuminate/Database/Eloquent/Model.php#L553

em: 570 e: 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;
    }

Resposta antiga abaixo


Gostaria de saber se existe alguma funcionalidade L4 integrada para fazer isso de alguma forma, como:

$row = DB::table('table')->where('id', '=', $id)->first();
// Fancy field => data assignments here
$row->save();

Eu criei esse método algumas semanas atrás ...

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

Espero que ajude. Eu adoraria ver uma alternativa para isso, se alguém tiver um para compartilhar. @erikthedev_


Existe e é chamado firstOrNew / firstsOrCreate
malhal

@malcolmhall Atualizei a resposta acima. Acontece Eloquent tem muitas características que eu me encontrei reconstrução;) É sempre bom passar algum tempo a visitar os docs :)
Erik Aybar

4.2.0 do packagist (estável em 2014/6/1) não contém updateOrCreate. Mas pode-se implementá-lo olhando a fonte. ModelName::firstOrNew(['param' => 'condition'])->fill(Input::get())->save();deve fazê-lo.
bibstha

3
Apenas observe que o Laravel não o executa como uma transação; portanto, se você tiver chaves únicas e outro usuário criá-lo com a mesma chave simultaneamente, poderá receber uma exceção. Acredito que uma das vantagens do RedBeanPHP é esse tipo de coisa que é feita em uma transação para você.
malhal

Obrigado por apontar o uso de fill () Isso me ajudou muito!
Relaxando em Chipre

70

Atualização 2020

Como no Laravel> = 5.3 , se alguém ainda estiver curioso para fazê-lo de maneira fácil. Sua possível usando: updateOrCreate().

Por exemplo, para perguntas feitas, você pode usar algo como:

$matchThese = ['shopId'=>$theID,'metadataKey'=>2001];
ShopMeta::updateOrCreate($matchThese,['shopOwner'=>'New One']);

O código acima verificará a tabela representada pelo ShopMeta, que será mais provável, a shop_metasmenos que não seja definido de outra forma no próprio modelo

e tentará encontrar entrada com

coluna shopId = $theID

e

coluna metadateKey = 2001

e se encontrar, atualizará a coluna shopOwnerda linha encontrada para New One.

Se encontrar mais de uma linha correspondente, atualizará a primeira linha, o que significa qual é a menor primária id.

Se não for encontrado, ele inserirá uma nova linha com:

shopId = $theID, metadateKey = 2001eshopOwner = New One

Aviso Verifique seu modelo $fillablee suponha que você tenha todos os nomes de colunas definidos lá, nos quais deseja inserir ou atualizar e que as colunas restantes tenham valor padrão ou sua idcoluna seja incrementada automaticamente.

Caso contrário, ele lançará um erro ao executar o exemplo acima:

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

Como haveria algum campo que precisará de valor ao inserir uma nova linha e isso não será possível, pois não está definido $fillableou não possui valor padrão.

Para obter mais referências, consulte a documentação do Laravel em: https://laravel.com/docs/5.3/eloquent

Um exemplo de lá é:

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

o que praticamente limpa tudo.

Atualização do Query Builder

Alguém perguntou se é possível usar o Query Builder no Laravel. Aqui está uma referência para o Query Builder dos documentos do Laravel.

O Query Builder funciona exatamente da mesma forma que o Eloquent, portanto, qualquer coisa verdadeira para o Eloquent também é verdadeira para o Query Builder. Portanto, neste caso específico, use a mesma função com o construtor de consultas da seguinte maneira:

$matchThese = array('shopId'=>$theID,'metadataKey'=>2001);
DB::table('shop_metas')::updateOrCreate($matchThese,['shopOwner'=>'New One']);

Obviamente, não esqueça de adicionar a fachada do DB:

use Illuminate\Support\Facades\DB;

OU

use DB;

Espero que ajude


E o construtor de consultas?
Sky

Que tal isso? :)
Aluno

Quero fazer o mesmo com o Query Builder. Não é eloquente. É possível?
Sky

Atualizei minha resposta, procure a seção "Atualização do Criador de consultas" na resposta acima.
Learner

Tentei o método DB :: table ('shop_metas') :: updateOrCreate, mas isso me deu o seguinte erro BadMethodCallException na linha Macroable.php 59: O método updateOrInsert não existe. Mesmo que eu use DB;
Swapnil Shende

17

Salvar função:

$shopOwner->save()

já faz o que você quer ...

Código 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
Isso não parece uma operação de upsert atômica. Caso contrário, isso pode causar condições de corrida.
Emil Vikström

Este código é para verificar se o modelo é carregado do DB ou se é um modelo baseado em memória. Atualizar ou Criar precisa de uma definição explícita de colunas-chave a serem verificadas e não pode ser executada implicitamente.
AMIB

17

firstOrNewcriará um registro se não existir e atualizará uma linha se já existir. Você também pode usar updateOrCreateaqui é o exemplo completo

$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99]
); 

Se houver um voo de Oakland para San Diego, defina o preço para US $ 99. se não existir, crie uma nova linha

Documento de referência aqui: ( https://laravel.com/docs/5.5/eloquent )


7

Se você precisar da mesma funcionalidade usando o DB, no Laravel, >= 5.5poderá usar:

DB::table('table_name')->updateOrInsert($attributes, $values);

ou a versão abreviada quando $attributese $valuessão os mesmos:

DB::table('table_name')->updateOrInsert($values);

6
$shopOwner = ShopMeta::firstOrNew(array('shopId' => $theID,'metadataKey' => 2001));

Em seguida, faça as alterações e salve. Observe que o firstOrNew não faz a inserção se não for encontrado; se você precisar, o firstOrCreate.


2

Mais uma opção se o seu ID não for incremento automático e você souber qual inserir / atualizar:

$object = MyModel::findOrNew($id);
//assign attributes to update...
$object->save();

2

Como o método firstOrCreate, updateOrCreate persiste no modelo, portanto não há necessidade de chamar 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]
);

E para o seu problema

$shopOwner = ShopMeta::updateOrCreate(
   ['shopId' => $theID, 'metadataKey' => '2001'],
   ['other field' => 'val' ,'other field' => 'val', ....]
);

1

Na verdade, firstOrCreate não seria atualizado caso o registro já exista no banco de dados. Melhorei um pouco a solução de Erik, pois precisava atualizar uma tabela que tivesse valores exclusivos, não apenas para a coluna "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);
    }
}

Então você usaria assim:

Model::createOrUpdate(
        array(
    'id_a' => 1,
    'foo' => 'bar'
        ), array(
    'id_a' => 1
        )
);

o que foi bom em não fazer isso: 1. Exclua com base na chave e 2. crie com novos valores. Estas ainda eram 2 operações. é para economizar tempo para indexar em caso de criação e exclusão?
Hafiz

1

como @JuanchoRamone postado acima (obrigado @Juancho), é muito útil para mim, mas se seus dados forem array, você deve modificar um pouco assim:

public static function createOrUpdate($data, $keys) {
    $record = self::where($keys)->first();
    if (is_null($record)) {
        return self::create($data);
    } else {
        return $record->update($data);
    }
}

Apenas uma nota rápida que esta deve ser updateOrCreate vez de createOrUpdate
John Shipp

Ok, mas se houver 1000 linhas, serão 1000 consultas em execução?
Marcelo Agimóvel 07/02


-2

verifique se um usuário existe ou não. Caso contrário, insira

$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           

Bem-vindo ao SO.Take uma olhada neste how-to-resposta para a prestação de resposta de qualidade. ---
thewaywewere

Por favor, marque também o framework que você está usando, versão php, banco de dados.
Jason joslin

1
Eu estou usando Laravel 5.4, php7 e mysql
Sabrina Abid

Sabrina Não é uma solução ideal, pois já existem funções no laravel para isso. Mas o seu é uma solução geral
djangodude

Seu método da velha escola laravel já tem uma função para isso. Ver resposta selecionada
Saeed Ansari
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.