Crear y actualizar Laravel Eloquent

165

¿Cuál es la abreviatura para insertar un nuevo registro o actualizar si existe?

<?php

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

if ($shopOwner == null) {
    // Insert new record into database
} else {
    // Update the existing record
}
1myb
fuente
Supongo que shopIdno es tu clave principal, ¿verdad?
Sergiu Paraschiv
@SergiuParaschiv, sí. no es
1myb
Mira la respuesta de @ErikTheDeveloper. Muestra un buen método elocuente incrustado que debería hacer el trabajo.
cw24
Exactamente

Respuestas:

232

Aquí hay un ejemplo completo de lo que estaba hablando "lu cip":

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

A continuación se muestra el enlace actualizado de los documentos que se encuentra en la última versión de Laravel

Documentos aquí: enlace actualizado

weotch
fuente
1
¡exactamente! 'firstOrNew' también existe en 4.0 (no mencionado en los documentos)
younes0
2
También podemos verificar que $ user es nuevo / recuperado usando if ($ user-> exist).
Ryu_hayabusa
1
@Ryu_hayabusa Eso probablemente causaría condiciones de carrera
Chris Harrison
parece que la nueva sintaxis es updateOrInsert (matriz $ atributos, matriz $ valores = []) en 5.5: github.com/laravel/framework/blob/5.5/src/Illuminate/Database/…
user1204214
86

Actualizado: 27 de agosto de 2014 - [ updateOrCreateIncorporado en el núcleo ...]

En caso de que la gente todavía se encuentre con esto ... Descubrí algunas semanas después de escribir esto, que esto es en realidad parte del núcleo de Eloquent de Laravel ...

Excavando en los métodos equivalentes de Eloquent. Puedes ver aquí:

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

en: 570 y: 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;
    }

Respuesta anterior a continuación


Me pregunto si hay alguna funcionalidad L4 integrada para hacer esto de alguna manera, como por ejemplo:

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

Creé este método hace unas semanas ...

// 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 eso ayude. Me encantaría ver una alternativa a esto si alguien tiene uno para compartir. @erikthedev_

Erik Aybar
fuente
Hay y se llama firstOrNew / firstsOrCreate
malhal
@malcolmhall He actualizado la respuesta anterior. Resulta que Eloquent tiene muchas características que me he encontrado reconstruyendo;) Siempre es bueno pasar algún tiempo navegando por los documentos :)
Erik Aybar
4.2.0 de Packagist (estable 2014/6/1) no contiene updateOrCreate. Pero uno puede implementarlo mirando la fuente. ModelName::firstOrNew(['param' => 'condition'])->fill(Input::get())->save();Deberías hacerlo.
bibstha
3
Solo tenga en cuenta que Laravel no lo ejecuta como una transacción, por lo que si tiene claves únicas y otro usuario lo crea con la misma clave simultáneamente, puede obtener una excepción. Creo que una de las ventajas de RedBeanPHP es que este tipo de cosas se realizan en una transacción para usted.
malhal
Gracias por señalar el uso de fill () ¡Eso me ha ayudado mucho!
Relajarse en Chipre
70

Actualización 2020

Como en Laravel> = 5.3 , si alguien todavía tiene curiosidad por cómo hacerlo de manera fácil. Su posible mediante el uso de: updateOrCreate().

Por ejemplo, para la pregunta formulada, puede usar algo como:

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

El código anterior verificará la tabla representada por ShopMeta, que probablemente shop_metasno se definirá lo contrario en el modelo mismo.

e intentará encontrar la entrada con

columna shopId = $theID

y

columna metadateKey = 2001

y si encuentra, actualizará la columna shopOwnerde la fila encontrada a New One.

Si encuentra más de una fila coincidente, actualizará la primera fila, lo que significa cuál tiene la primaria más baja id.

Si no se encuentra, insertará una nueva fila con:

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

Aviso Verifique su modelo $fillabley solicite que tenga todos los nombres de columna definidos allí que desea insertar o actualizar y que las columnas restantes tengan el valor predeterminado o su idcolumna se incremente automáticamente.

De lo contrario, arrojará un error al ejecutar el ejemplo anterior:

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 habría algún campo que necesitará valor al insertar una nueva fila y no será posible ya que no está definido $fillableo no tiene un valor predeterminado.

Para obtener más información, consulte la documentación de Laravel en: https://laravel.com/docs/5.3/eloquent

Un ejemplo a partir de ahí es:

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

que prácticamente borra todo.

Actualización del generador de consultas

Alguien ha preguntado si es posible usar Query Builder en Laravel. Aquí hay una referencia para el generador de consultas de los documentos de Laravel.

Query Builder funciona exactamente igual que Eloquent, por lo que cualquier cosa que sea verdadera para Eloquent también lo es para Query Builder. Entonces, para este caso específico, simplemente use la misma función con su generador de consultas de la siguiente manera:

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

Por supuesto, no olvides agregar la fachada DB:

use Illuminate\Support\Facades\DB;

O

use DB;

Espero que ayude

Aprendiz
fuente
¿Qué tal el generador de consultas?
Cielo
¿Qué hay de eso? :)
Estudiante
Quiero hacer lo mismo con Query Builder. No es elocuente. ¿Es posible?
Cielo
Actualicé mi respuesta, busque la sección "Actualización del generador de consultas" en la respuesta anterior.
Estudiante
Intenté el método DB :: table ('shop_metas') :: updateOrCreate pero esto me da el siguiente error BadMethodCallException en Macroable.php línea 59: El método updateOrInsert no existe. Aunque yo uso DB;
Swapnil Shende
17

Guardar función:

$shopOwner->save()

ya haz lo que quieras ...

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
fuente
66
Eso no parece una operación atómica de inserción. Si no es así, esto podría causar condiciones de carrera.
Emil Vikström
Este código es para verificar si el modelo se carga desde la base de datos o si es un modelo basado en memoria. Actualizar o Crear necesita una definición explícita de las columnas clave que se deben verificar y no se puede realizar implícitamente.
AMIB
17

firstOrNewcreará un registro si no existe y actualizará una fila si ya existe. También puedes usar updateOrCreateaquí el ejemplo completo

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

Si hay un vuelo desde Oakland a San Diego, establezca el precio en $ 99. si no existe, cree una nueva fila

Documento de referencia aquí: ( https://laravel.com/docs/5.5/eloquent )

Saeed Ansari
fuente
7

Si necesita la misma funcionalidad usando DB, en Laravel >= 5.5puede usar:

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

o la versión abreviada cuando $attributesy $valuesson iguales:

DB::table('table_name')->updateOrInsert($values);
Sasa Blagojevic
fuente
6
$shopOwner = ShopMeta::firstOrNew(array('shopId' => $theID,'metadataKey' => 2001));

Luego haga sus cambios y guarde. Tenga en cuenta que firstOrNew no realiza la inserción si no se encuentra, si lo necesita, entonces es firstOrCreate.

malhal
fuente
2

Una opción más si su identificación no es aumento automático y sabe cuál insertar / actualizar:

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

Al igual que el método firstOrCreate, updateOrCreate persiste en el modelo, por lo que no es necesario llamar a 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]
);

Y para tu problema

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

En realidad, firstOrCreate no se actualizaría en caso de que el registro ya exista en la base de datos. Mejoré un poco la solución de Erik ya que en realidad necesitaba actualizar una tabla que tiene valores únicos no solo para la columna "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);
    }
}

Entonces lo usarías así:

Model::createOrUpdate(
        array(
    'id_a' => 1,
    'foo' => 'bar'
        ), array(
    'id_a' => 1
        )
);
Juancho Ramone
fuente
Lo bueno de no hacer esto: 1. Eliminar en función de la clave, y 2. Crear con nuevos valores. Estas todavía eran 2 operaciones. ¿Es para ahorrar tiempo para indexar en caso de creación y eliminación?
Hafiz
1

como @JuanchoRamone publicado anteriormente (gracias @Juancho) es muy útil para mí, pero si sus datos son una matriz, debería modificar un poco así:

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
fuente
Solo una nota rápida de que esto debería ser updateOrCreate en lugar de createOrUpdate
John Shipp
Ok, pero si hay 1000 filas, ¿se ejecutarán 1000 consultas?
Marcelo Agimóvel
0

¿No es esto lo mismo que updateOrCreate ()?

Es similar pero no igual. UpdateOrCreate () solo funcionará para una fila a la vez que no permite la inserción masiva. InsertOnDuplicateKey funcionará en muchas filas.

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

Pax Exterminatus
fuente
-2

comprobar si existe un usuario o no. Si no, inserte

$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
fuente
Bienvenido a SO.Take un vistazo a este cómo-a-respuesta para proporcionar respuesta de calidad. ---
thewaywewere
También etiquete el marco que está utilizando, la versión php, la base de datos.
Jason Joslin el
1
Estoy usando Laravel 5.4, php7 y mysql
Sabrina Abid
Sabrina No es una solución ideal ya que las funciones ya existen en laravel para hacerlo. Pero la
suya
Su método de la vieja escuela laravel ya tiene una función para esto. Ver respuesta seleccionada
Saeed Ansari