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
}
1myb
fonte
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

weotch
fonte
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_

Erik Aybar
fonte
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

Aprendiz
fonte
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);
    }
lu cip
fonte
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 )

Saeed Ansari
fonte
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);
Sasa Blagojevic
fonte
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.

malhal
fonte
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();
vivoconunxino
fonte
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', ....]
);
Bashirpour
fonte
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
        )
);
Juancho Ramone
fonte
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);
    }
}
Duy Ngo
fonte
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
0

Não é o mesmo que updateOrCreate ()?

É semelhante, mas não é o mesmo. O updateOrCreate () funcionará apenas uma linha por vez, o que não permite a inserção em massa. InsertOnDuplicateKey funcionará em várias linhas.

https://github.com/yadakhov/insert-on-duplicate-key

Pax Exterminatus
fonte
-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           
Sabrina Abid
fonte
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