x-yuri
x-yuri

Reputation: 18893

How do I make doctrine support timestamp columns?

I'm trying to apply the following migration:

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

But artisan says:

  [Doctrine\DBAL\DBALException]
  Unknown column type "timestamp" requested. Any Doctrine type that you use has to be registered with \Doctrine\DBAL
  \Types\Type::addType(). You can get a list of all the known types with \Doctrine\DBAL\Types\Type::getTypesMap(). I
  f this error occurs during database introspection then you might have forgot to register all database types for a
  Doctrine Type. Use AbstractPlatform#registerDoctrineTypeMapping() or have your custom types implement Type#getMapp
  edDatabaseTypes(). If the type name is empty you might have a problem with the cache or forgot some mapping inform
  ation.

When I try to install mmerian/doctrine-timestamp (composer install mmerian/doctrine-timestamp), composer says:

  [InvalidArgumentException]
  Could not find package mmerian/doctrine-timestamp at any version for your minimum-stability (stable). Check the pa
  ckage spelling or your minimum-stability

What do I do?

UPD With composer require mmerian/doctrine-timestamp=dev-master, I was able to install the package, then added Type::addType('timestamp', 'DoctrineTimestamp\DBAL\Types\Timestamp'); before Schema::table statement, but now I've got the other error:

  [Illuminate\Database\QueryException]
  SQLSTATE[42000]: Syntax error or access violation: 1067 Invalid default value for 'created_at' (SQL: ALTER TABLE u
  sers CHANGE created_at created_at INT DEFAULT 'CURRENT_TIMESTAMP' NOT NULL)

UPD I checked again if it works with mmerian/doctrine-timestamp, since I added only first of the lines from the docs back then (or the doc was updated):

Type::addType('timestamp', 'DoctrineTimestamp\DBAL\Types\Timestamp');                                          
DB::getDoctrineConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('Timestamp', 'timestamp');

But it doesn't help as well. The migration succeeds, but the column definition doesn't change.

Upvotes: 14

Views: 20100

Answers (7)

Kwaadpepper
Kwaadpepper

Reputation: 536

To prevent adding Dbal using on app config I am using this in my migration class.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class UpdateSlidePropsonSlidesTable extends Migration
{

    public function __construct()
    {
        // ! This is to allow changing timestamps without forcing require dbal on non dev composer.
        \Doctrine\DBAL\Types\Type::addType(
            'timestamp',
            \Illuminate\Database\DBAL\TimestampType::class
        );
    }

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('slides', function (Blueprint $table) {
            $table->renameColumn('date_start', 'publish_at');
            $table->renameColumn('date_end', 'unpublish_at');
        });
        Schema::table('slides', function (Blueprint $table) {
            $table->timestamp('publish_at')->nullable()
                ->change()
                ->comment('The date where the console published state should be changed to published.');
            $table->timestamp('unpublish_at')->nullable()
                ->change()
                ->comment('The date where the console published state should be changed to archived.');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('slides', function (Blueprint $table) {
            $table->renameColumn('publish_at', 'date_start');
            $table->renameColumn('unpublish_at', 'date_end');
        });
    }
}

Upvotes: 0

rsanchez
rsanchez

Reputation: 14657

With the current Laravel version you just need to add the following to your config/database.php and changing timestamp columns in migrations will work.

use Illuminate\Database\DBAL\TimestampType;
 
'dbal' => [
    'types' => [
        'timestamp' => TimestampType::class,
    ],
],

Source

Upvotes: 13

Anjani Barnwal
Anjani Barnwal

Reputation: 1522

if you want to make migration for current timestamp and get the error "Unknown column type "timestamp" requested. Any Doctrine type that you use has to be registered with \Doctrine\DBAL\Types\Type::addType()" then use like this

\DB::statement("ALTER TABLE `order_status_logs` CHANGE `created_at` `created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP");

Upvotes: 1

徐正洋
徐正洋

Reputation: 103

hi~ you can use "datetime" type:

 Schema::table('orders', function ($table) {

        $table->datetime('pay_time')->nullable()->change();

    });

Upvotes: 7

Mark Topper
Mark Topper

Reputation: 664

I builded this for it, since Doctrine does not want to support it cause it's a MySQL-specific column type.

Upvotes: 0

x-yuri
x-yuri

Reputation: 18893

As one can see, mmerian/doctrine-timestamp doesn't solve the issue. First, after this line $table->getColumns()['created_at'] is

class Doctrine\DBAL\Schema\Column#520 (16) {
  protected $_type => class Doctrine\DBAL\Types\DateTimeType#504 (0) { }
  protected $_length => NULL
  protected $_precision => int(10)
  protected $_scale => int(0)
  protected $_unsigned => bool(false)
  protected $_fixed => bool(false)
  protected $_notnull => bool(true)
  protected $_default => string(17) "CURRENT_TIMESTAMP"
  protected $_autoincrement => bool(false)
  protected $_platformOptions => array(0) { }
  protected $_columnDefinition => NULL
  protected $_comment => NULL
  protected $_customSchemaOptions => array(0) { }
  protected $_name => string(10) "created_at"
  protected $_namespace => NULL
  protected $_quoted => bool(false)
}

and $this->getTableWithColumnChanges($blueprint, $table)->getColumns()['created_at'] is

class Doctrine\DBAL\Schema\Column#533 (16) {
  protected $_type => class DoctrineTimestamp\DBAL\Types\Timestamp#513 (0) { }
  protected $_length => NULL
  protected $_precision => int(10)
  protected $_scale => int(0)
  protected $_unsigned => bool(false)
  protected $_fixed => bool(false)
  protected $_notnull => bool(true)
  protected $_default => string(17) "CURRENT_TIMESTAMP"
  protected $_autoincrement => bool(false)
  protected $_platformOptions => array(0) { }
  protected $_columnDefinition => NULL
  protected $_comment => NULL
  protected $_customSchemaOptions => array(0) { }
  protected $_name => string(10) "created_at"
  protected $_namespace => NULL
  protected $_quoted => bool(false)
}

So, first I can't see information about ON UPDATE part here. Second, the onle difference is $_type value. What I can confirm after this line, $tableDiff->changedColumns['created_at']->changedProperties is

array(1) {
  [0] => string(4) "type"
}

Then, when generating ALTER TABLE statement, it all comes down to this

public function getDefaultValueDeclarationSQL($field)
{
    $default = empty($field['notnull']) ? ' DEFAULT NULL' : '';
    if (isset($field['default'])) {
        $default = " DEFAULT '".$field['default']."'";
        if (isset($field['type'])) {
            if (in_array((string) $field['type'], array("Integer", "BigInt", "SmallInt"))) {
                $default = " DEFAULT ".$field['default'];
            } elseif (in_array((string) $field['type'], array('DateTime', 'DateTimeTz')) && $field['default'] == $this->getCurrentTimestampSQL()) {
                $default = " DEFAULT ".$this->getCurrentTimestampSQL();
            } elseif ((string) $field['type'] == 'Time' && $field['default'] == $this->getCurrentTimeSQL()) {
                $default = " DEFAULT ".$this->getCurrentTimeSQL();
            } elseif ((string) $field['type'] == 'Date' && $field['default'] == $this->getCurrentDateSQL()) {
                $default = " DEFAULT ".$this->getCurrentDateSQL();
            } elseif ((string) $field['type'] == 'Boolean') {
                $default = " DEFAULT '" . $this->convertBooleans($field['default']) . "'";
            }
        }
    }
    return $default;
}

Somewhere around this line there supposed to be a check for Timestamp type to turn 'CURRENT_TIMESTAMP' into CURRENT_TIMESTAMP. Is this possible within mmerian/doctrine-timestamp? That question is left open for now. This check would most likely solve my particular issue. But for now I'm going to get away with this:

DB::statement('ALTER TABLE users MODIFY COLUMN created_at
    TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP');

Upvotes: 7

maximkou
maximkou

Reputation: 5332

Set minimum-stability setting to dev in your composer.json, because mmerian/doctrine-timestamp has only dev-master version, to example:

{
    "minimum-stability": "dev",
    "require": {
        ...
     }
}

Then, when bootstraping your doctrine connection:

Type::addType('timestamp', 'DoctrineTimestamp\DBAL\Types\Timestamp');
$conn->getDatabasePlatform()->registerDoctrineTypeMapping('Timestamp', 'timestamp');

Upvotes: 0

Related Questions