framework: Timestamp is not a valid Doctrine column type
- Laravel Version: 8.17.0
- PHP Version: 7.3.25
- Database Driver & Version: mysql / 10.2.36-MariaDB
Preface
I created the below issue from my own research before finding this duplicate issue:
This bug report is a duplicate of: https://github.com/laravel/framework/issues/16526
and I’d like to address the resolution of that issue here. As a member of the Doctrine team I can assure you this is not an issue in Doctrine. It is a design decision just as not supporting enum
is a design decision. But Laravel uses timestamp column types throughout and timestamp is a standard in Laravel. So just because Doctrine chooses not to support this directly is not reason enough for Laravel not to take the small steps necessary to support Laraval’s own design decisions and implement the column in the DBAL library Laravel is integrating with. I’m open to writing the PR for this.
Description:
Attempting to change a column from a datetime to a timestamp results in the following exception:
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(). If this error occurs during database introspection then you might have forgotten to register all database types for a Doctrine Type. Use AbstractPlatform#registerDoctrineTypeMapping() or have your custom types implement Type#getMappedDatabaseTypes(). If the type name is empty you might have a problem with the cache or forgot some mapping information.
Steps To Reproduce:
Run this migration against an empty database:
public function up()
{
Schema::create('user_to_role', function (Blueprint $table) {
$table->bigInteger('role_id')->unsigned();
$table->bigInteger('user_id')->unsigned();
$table->primary(['role_id','user_id']);
});
Schema::table('user_to_role', function (Blueprint $table) {
$table->dateTime('created_at')->after('user_id');
});
Schema::table('user_to_role', function (Blueprint $table) {
$table->timestamp('created_at')->after('user_id')->change();
});
}
Discussion
Laravel uses the mysql timestamp
field type throughout the framework. To modify columns Laravel uses the Doctrine DBAL library. Doctrine has a strict set of data types and uses the more generic datetime
instead of the MySQL specific timestamp
.
The problem reported is the same class of problem where Doctrine does not support the enum
datatype: https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/cookbook/mysql-enums.html
This library creates a proper timestamp column type for DBAL: https://github.com/marktopper/doctrine-dbal-timestamp-type and it includes a Laravel 5 service provider which suggests to me that this problem has been around for some time.
I do not believe it should be necessary to install a 3rd party package in order to change a column to the widely used and supported timestamp
data type.
See also:
Similar issue with enum: https://github.com/laravel/framework/issues/35096
Documentation where changing to enum is not supported says nothing about timestamp https://github.com/laravel/docs/blame/8.x/migrations.md#L403
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 2
- Comments: 16 (16 by maintainers)
I have a couple ideas.
Here is what I’m leaning towards because it involves the least amount of change for a user when they have alter migrations. This approach continues the current practice of only including doctrine/dbal when the user needs alter migrations. But it configures the application with existing data and functions therefore allowing the user to just include dbal-and-go instead of adding a new ServiceProvider too (see idea #2)
Idea #1:
In https://github.com/laravel/laravel/blob/8.x/config/database.php add a configuration option
Use this configuration in https://github.com/laravel/framework/blob/8.x/src/Illuminate/Database/DatabaseServiceProvider.php#L39 and add to the boot() function:
and in the class add
Finally create a proper Illuminate\Database\DBAL\TimestampType class
So I don’t come across as crazy, I want to point out that I’m not using MSSQL. It is included because Laravel claims support for it and I’m a completist.
Now, as I said above, I work on the Doctrine team, and in Doctrine metadata for the ORM is managed in a very different way than Laraval’s ORM (and not just because it is a Data Mapper pattern). But for both ORMs metadata can be extracted into an ORM abstraction pattern. And in Laravel, the available metadata for date fields includes datetime and timestamp regardless of the database driver which means that this bit of code is valid for every database Laravel supports including MSSQL
It would be crazy to write documentation that says “if you’re using MSSQL you have to modify your migrations” because portable migrations seem to be the very goal of Laravel’s migration code. If a user advertises their product works on MySQL, PostgreSQL, SQLite, and MSSQL then Laravel should support what Laravel says it supports and not force such a user to put in database driver-specific migrations.
I may be short-sighted concerning the availability of unit testing in
laravel/framework
because I thought PostgreSQL was in the same boat as MSSQL; that being there are no unit tests for PostgreSQL because thedocker-composer.yml
file forlaravel/framework
does not include PostgreSQL and https://github.com/laravel/framework/blob/8.x/.github/workflows/tests.yml does not include PostgreSQL.And to close my own sanity argument I did start this issue because I’m using PostgreSQL.
Going to close this as this is technically an enhancement. Let’s focus on the PR.
Great! I’ll put a PR together soon and reference this issue.