¿Cómo puedo establecer el valor predeterminado de una columna de marca de tiempo en la marca de tiempo actual con migraciones de Laravel?

167

Me gustaría hacer una columna de marca de tiempo con un valor predeterminado de CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMPuso de Laravel Schema Builder / Migrations. He revisado la documentación de Laravel varias veces, y no veo cómo puedo hacer que ese sea el valor predeterminado para una columna de marca de tiempo.

La timestamps()función establece los valores predeterminados 0000-00-00 00:00para ambas columnas que crea.

JoeyD473
fuente

Respuestas:

310

Dado que es una expresión en bruto, debe usar DB::raw()para establecer CURRENT_TIMESTAMPcomo valor predeterminado para una columna:

$table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'));

Esto funciona perfectamente en cada controlador de base de datos.

Nuevo atajo

A partir de Laravel 5.1.25 (ver PR 10962 y commit 15c487fe ) puede usar el nuevo useCurrent()método de modificación de columna para establecer el CURRENT_TIMESTAMPvalor predeterminado para una columna:

$table->timestamp('created_at')->useCurrent();

Volviendo a la pregunta, en MySQL también puede usar la ON UPDATEcláusula a través de DB::raw():

$table->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));

Gotchas

  • MySQL

    Comenzando con MySQL 5.7, 0000-00-00 00:00:00ya no se considera una fecha válida. Como se documenta en la guía de actualización de Laravel 5.2 , todas las columnas de marca de tiempo deben recibir un valor predeterminado válido cuando inserte registros en su base de datos. Puede usar el useCurrent()modificador de columna (de Laravel 5.1.25 y superior) en sus migraciones para predeterminar las columnas de marca de tiempo a las marcas de tiempo actuales, o puede hacer que las marcas nullable()de tiempo permitan valores nulos.

  • PostgreSQL y Laravel 4.x

    En las versiones de Laravel 4.x, el controlador PostgreSQL estaba utilizando la precisión predeterminada de la base de datos para almacenar valores de marca de tiempo. Cuando se usa la CURRENT_TIMESTAMPfunción en una columna con una precisión predeterminada, PostgreSQL genera una marca de tiempo con la mayor precisión disponible, generando así una marca de tiempo con una segunda parte fraccionaria - vea este violín de SQL .

    Esto llevará a Carbon a fallar al analizar una marca de tiempo ya que no esperará que se almacenen microsegundos. Para evitar que este comportamiento inesperado rompa su aplicación, debe dar explícitamente una precisión cero a la CURRENT_TIMESTAMPfunción de la siguiente manera:

    $table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP(0)'));

    Desde Laravel 5.0, las timestamp()columnas se han cambiado para usar una precisión predeterminada de cero, lo que evita esto.

    Gracias a @andrewhl por señalar este problema en los comentarios.

Paulo Freitas
fuente
Sugerencia mucho mejor que la mía. Use esto en lugar de mi DB::statementejemplo, esto es mucho más simple.
Marwelln
¿Esto también se puede usar para las declaraciones PARTITION BY en sus pruebas?
Glenn Plas
2
No sin fallas. Para PostgreSQL, 'CURRENT_TIMESTAMP' devuelve algo en el formato de: 2014-08-11 15: 06: 29.692439. Esto hace que el método Carbon :: createFromFormat ('Ymd H: i: s', $ timestamp) falle (no puede analizar los milisegundos finales). Esto lo usa Laravel cuando accede a las marcas de tiempo. Para arreglar PostgreSQL, use: DB :: raw ('now () :: timestamp (0)') (referencia: postgresql.org/docs/8.1/static/… )
andrewhl
@andrewhl En realidad, respondí solo para MySQL, ya que es el tema de la pregunta. Pero gracias por compartir esto con nosotros, ¡actualizaré mi respuesta para cubrir eso! :)
Paulo Freitas
55

Para crear ambas columnas created_aty updated_at:

$t->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'));
$t->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP'));

Necesitará la versión de MySQL> = 5.6.5 para tener varias columnas con CURRENT_TIMESTAMP

Brian Adams
fuente
3
¿Por qué no solo usar $table->timestamps()->default(DB::raw('CURRENT_TIMESTAMP'));?
Dave
1
@dave Porque entonces updated_atno cambiaría cuando el registro fuera modificado después de su creación inicial
Erik Berkun-Drevnig
Sí, agregar a eso las marcas de tiempo () no permite valores predeterminados de todos modos, por lo que eso no funcionaría en absoluto. Había comprometido el código para permitirlo, pero los gerentes de Laravel realmente no quieren que las personas usen el valor predeterminado de la forma en que lo estamos usando (suponiendo que las versiones de MySQL anteriores a 5.6.5 no permiten múltiples columnas que tengan marcas de tiempo predeterminadas).
Dave
En realidad, updated_at es administrado por Eloquent, por lo que no es necesario el bit "en actualización" ya que se establecerá cuando un modelo se actualice automáticamente.
dmyers
@dmyers Si estás usando elocuente, entonces puedes hacerlo $t->timestamps();pero eso no responde la pregunta.
Brian Adams
44

A partir de Laravel 5.1.26, etiquetado el 02/12/2015, useCurrent()se ha agregado un modificador:

Schema::table('users', function ($table) {
    $table->timestamp('created')->useCurrent();
});

PR 10962 (seguido de commit 15c487fe ) condujo a esta adición.

También puede leer los números 3602 y 11518 que son de interés.

Básicamente, MySQL 5.7 (con la configuración predeterminada) requiere que defina un valor predeterminado o que se pueda anular para los campos de tiempo.

Gras Double
fuente
10

Esto no funciona por un hecho:

$table->timestamp('created_at')->default('CURRENT_TIMESTAMP');

No elimina el '0 predeterminado' que parece venir con la selección de la marca de tiempo y solo agrega el predeterminado personalizado. Pero lo necesitamos sin las comillas. No todo lo que manipula un DB proviene de Laravel4. Ese es su punto. Quiere valores predeterminados personalizados en ciertas columnas como:

$table->timestamps()->default('CURRENT_TIMESTAMP');

No creo que sea posible con Laravel. He estado buscando durante una hora para ver si es posible.


Actualización: la respuesta de Paulos Freita muestra que es posible, pero la sintaxis no es sencilla.

Glenn Plas
fuente
Excelente. Cosas perfectas Pulgares arriba, esto también me ayudó.
Glenn Plas
pequeño comentario, antes de gritar: esto no funciona para mí en laravel, eche un vistazo a la fecha en que se escribió esta respuesta: 2013. Era válido en ese entonces. Le agradecería antes de tocar la flecha hacia abajo.
Glenn Plas
9

Como posibilidad adicional para futuros googlers

Me resulta más útil tener nulo en la columna updated_at cuando se crea el registro pero nunca se ha modificado . Reduce el tamaño de la base de datos (ok, solo un poco) y es posible ver a primera vista que los datos nunca se han modificado.

A partir de esto uso:

$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->default(DB::raw('NULL ON UPDATE CURRENT_TIMESTAMP'))->nullable();

(En Laravel 7 con mysql 8).

ndberg
fuente
7

Utilice la sugerencia de Paulo Freitas en su lugar.


Hasta que Laravel corrija esto, puede ejecutar una consulta de base de datos estándar después de Schema::createhaberla ejecutado.

    Schema::create("users", function($table){
        $table->increments('id');
        $table->string('email', 255);
        $table->string('given_name', 100);
        $table->string('family_name', 100);
        $table->timestamp('joined');
        $table->enum('gender', ['male', 'female', 'unisex'])->default('unisex');
        $table->string('timezone', 30)->default('UTC');
        $table->text('about');
    });
    DB::statement("ALTER TABLE ".DB::getTablePrefix()."users CHANGE joined joined TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL");

Funcionó de maravilla para mí.

Marwelln
fuente
Ese es un buen truco. Deseo que el generador de esquemas soporte tablas particionadas ya que las uso por todas partes. Traté de profundizar en el código, pero no es tan obvio para mí dónde modificar esto.
Glenn Plas
-2

Así es como lo haces, lo he comprobado y funciona en mi Laravel 4.2.

$table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'));

Espero que esto ayude.

Jawad
fuente
-4

En Laravel 5 simplemente:

$table->timestamps(); //Adds created_at and updated_at columns.

Documentación: http://laravel.com/docs/5.1/migrations#creating-columns

JoenMarz
fuente
12
Pero esto no establece el valor predeterminado como CURRENT_TIMESTAMP como se hizo en la pregunta.
Josh
intento esto, pero dado nulo en 5.4 idk por qué, pero cuando intento -> useCurrent (); funciona bien
Anthony Kal
no responde la pregunta de CURRENT_TIMESTAMPen created_atcolumna y on UPDATE CURRENT_TIMESTAMPen updated_atcolumna. Hasta que la gente de Laravel lo arregle, use esto: $table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP')); $table->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP'));
Hamza Rashid