Search code examples
phpdoctrine-ormdatabase-migrationlaravel-5sql-timestamp

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.


Solution

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