doctrine 2.0 : use SQL timestamp - mysql

I'm looking for a way to make doctrine using TIMESTAMP instead of DATETIME for MySql.
Additionaly I need to set ON UPDATE CURRENT_TIMESTAMP and CURRENT_TIMESTAMP as default values.
I would like to have the possibility to have all this code in PHP annotations to have everything in one central place.
How can I do that?

After hours of searching, I found the answer and I hope this helps anyone else looking for a more satisfactory solution than entity lifecycles and the WRONG column type (because a TIMESTAMP is a particular type with specific behaviours beyond just storing a datetime value)
All you need to do is to add
* #ORM\Column(type="datetime", nullable=false)
* #ORM\Version
to your annotation and Doctrine will both create a TIMESTAMP column with DEFAULT CURRENT_TIMESTAMP and then return the actual table value as a valid \DateTime object.
CTOP (Credits to Original Poster)

There is no such thing like using TIMESTAMP in mysql with Doctrine.
However, I fixed it myself but have to test it:
Create a file: Doctrine\DBAL\Types\TimestampType.php
Copy the code from TimeType.php into TimestampType.php
Rename 'Time' to 'Timestamp' in the code
Create a new constant, and add timestamp to the typesmap in: Doctrine\DBAL\Types\Type.php
Create function called getTimestampTypeDeclarationSQL in Doctrine\DBAL\Platforms\AbstractPlatform.php
And in that function, return TIMESTAMP
I've used yaml to create my Entities and Proxies, so use in yaml:
type: timestamp
I'm going to test this 'fix'/'workaround' now. I'll let you know.

As suggested by Daniel Criconet,
#ORM\Column(type="datetime", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
will make that particular column in the corresponding MySQL table become TIMESTAMP instead of DATETIME.
For YAML version, see the original answer.

/**
* #var \DateTime
* #ORM\Column(type="datetime", columnDefinition="timestamp default current_timestamp")
*/
protected $createdAt;
/**
* #var \DateTime
* #ORM\Column(type="datetime", columnDefinition="timestamp default current_timestamp on update current_timestamp")
*/
protected $updatedAt;

#Polichism provided the right logic, but the patch wasn't accepted. Instead, users were directed to add a custom type. I've implemented that by combining:
The logic in #Polichism's patch
Doctrine's recommended Custom Mapping Types code
Zf2 Application Configuration instructions found here
The final versions can be found on GitHub:
Timestamp Class
See the 'doctrine' part of the Configuration
NOTE: I've linked to specific commits to ensure that the links remain valid if the file is later removed. Use the history to check for newer versions.

/** #ORM\Column(type="datetime", nullable=false ) */
protected $created;
this will create timestamp and return datetime object
public function __construct()
{
$this->created = new \DateTime();
}

/**
* #Column(type="datetime", options={"default": 0})
* #version=true
*/
private $data;
create sql like this:
data TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
tested on version 2.5.4

#ORM\Version works only for one property per entity so it is not an option if you need to have more than one timestamp field in a table.

Related

Saving Datetime Immutable object as Datetime in database with Symfony and Doctrine without default comment

I'm building the Symfony entities from the existing database dump, and decided to switch from Datetime objects to Datatime Immutable, which don't have to change anything in database. But in the migration I see that it's adding the default comment, which I want to get rid of
'ALTER TABLE application_settings CHANGE updated_at updated_at DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)'');
DB is Mysql.
Here's my Datetime Immutable property in the entity:
#[ORM\Column(name: 'updated_at', type: Types::DATETIME_IMMUTABLE, nullable: true)]
private ?\DateTimeImmutable $updatedAt = null;
Didn't find anything about this issue in the internet. Is there any way to get rid of this comment?

Doctrine wants to "ALTER TABLE coo_users CHANGE id id VARCHAR(255) NOT NULL;" but wants to do this each time I update the schema

If I do bin/console doctrine:schema:update --force the result is:
Updating database schema...
Database schema updated successfully! "1" query was executed
And this is done each time I run the update command.
So, with bin/console doctrine:schema:validate I see that:
[Mapping] OK - The mapping files are correct.
[Database] FAIL - The database schema is not in sync with the current mapping file.
The query Doctrine wants to execute is ever the same (bin/console doctrine:schema:update --dump-sql):
ALTER TABLE coo_users CHANGE id id VARCHAR(255) NOT NULL;
What's happening? It seems Doctrine wants to do any modification as it wants to change the field id again to field id, each time, in an infinite modification.
THE MAPPING
/**
* #ORM\Id
* #ORM\Column(type="string", unique=true)
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
Error found!
The problem is that the column type is set to string while it has to be integer.
It was set to string as I used a custom generator that required a string field. When I removed the custom generator, I didn't change the field type back to integer and so Doctrine wanted to edit the field each time.

Timestamp field can only by updated via behavior in Yii

In my model, I have a field named timestamp (MySQL type: timestamp) defined in safe validator and I'm unable to write it manually. Each time I call:
$model->timestmap = time();
$model->save();
model is saved (row created / updated), that is -- it passes validation without errors, but timestamp field is filled with default value of 0000-00-00 00:00:00 (I decided to remove default on update CURRENT_TIMESTAMP attribute, I don't want MySQL to handle this).
However, when I remove above code and attach CTimestampBehavior instead, field is being filled with correct values on both update and create, without any problems.
What can be happening or what am I missing? How can behavior update field without problems, while my manual attempt fails? Is this because column type is timestamp, not datetime or int?
I was always told, that first clue, why some attribute isn't saved, is because it is not listed among safe validator list or because it is listed on unsafe validator list. But this one is listed on safe list.
Your database field is a datetime field (i assume, looking at your default value), but your filling it with a unix timestamp. Try it with an CDbExpression instead:
$model->timestamp = new CDbExpression('NOW()');
$model->save();
I usually use $model->timestamp = date('Y-m-d H:i:s'), it works perfectly
As the other answers already suggested:
The timestamp needs to be converted into a MySQL compatible date format string somehow upon saving and the other way around when loading. Now you already discovered that the CTimestampBehavior does this for you but unfortunately it doesn't care about loading.
IMO the best solution for you is something along the way of:
public function beforeSave()
{
$this->timestamp = date('Y-m-d H:i:s', $this->timestamp);
return parent::beforeSave();
}
public function afterSave()
{
// Turn it back into a unix timestamp in case you want to continue working with the record
$this->timestamp = CDateTimeParser::parse($this->timestamp, 'yyyy-MM-dd hh:mm:ss');
parent::afterSave();
}
public function afterFind()
{
$this->timestamp = CDateTimeParser::parse($this->timestamp, 'yyyy-MM-dd hh:mm:ss');
return parent::afterFind();
}
It's a lot of work for a stupid timestamp and so for myself I have an auto type conversion behaviour that I link to my models. This behaviour uses the table metadata to take care of everything automatically. Might be a good idea to invest in that. I've thought about making mine open source but it's a bit of a mess atm ;)
But the code above will give you unix times to work with during executing and whilst saving it will temporary convert into a mysql datetime string
Hope that helps.

Doctrine "on update CURRENT_TIMESTAMP" annotation (Symfony2)

I need to make annotation for column in my entity - "on update CURRENT_TIMESTAMP" attribute. How can I do that?
You cannot use your own column definition:
/**
* #ORM\Column(type="datetime", columnDefinition="DATETIME on update CURRENT_TIMESTAMP")
*/
private $updatedAt;
The example is for MySQL. Take into account, that columnDefinition is not portable among different databases or even their versions.
The easiest way is to use DoctrineExtensions with Timestampable as described here: https://github.com/l3pp4rd/DoctrineExtensions/blob/master/doc/timestampable.md

Tell Hibernate's hbm2ddl to add MySQL enum columns for #Enumerated annotated fields

I'm creating a DB table using hbm2ddl with Java code similar to the following:
#Entity
public class Filter {
public enum Type {
TypeA, TypeB;
}
#Enumerated(EnumType.STRING)
private Type type;
}
It works fine, but for "type" a VARCHAR column is created, i.e. the DDL code looks like this:
CREATE TABLE IF NOT EXISTS `filter` (`type` varchar(255) DEFAULT NULL)
But what I want to have is this:
CREATE TABLE IF NOT EXISTS `filter` (`type` enum('TypeA','TypeB') NOT NULL)
Is this possible to declare in Hibernate, preferred with annotations?
Or is there a way to extend SchemaUpdate and overwrite the method that renders the alter script part for enumerated field the way I like it?
Background: The same database is used in a PHP part of the project and I want to prevent that invalid values are inserted.
Although it seems there is no way to handle MySQL enums 100% automatically, as pointed out by Lucas on his answer, there is actually a simple way to contour it. You may use columnDefinition attribute on #Column annotation, which seems to be specifically designed to generate custom DDL code.
See the documentation excerpt describing the attribute:
(Optional) The SQL fragment that is used when generating the DDL for the column.
Defaults to the generated SQL to create a column of the inferred type.
The NOT NULL restriction is quite standard, and is supported by another attribute nullable.
Thus, your property definition would look like this:
#Enumerated(EnumType.STRING)
#Column(columnDefinition = "enum ('TypeA', 'TypeB')", nullable = false)
private Type type;
I believe that's going to be complicated, since the java.sql.Types, which define the sql types treated by java, does not have enum type (since it's not a standardized type according to SQL-92).
If that was the case you could create a hibernate custom UserType extending the EnumType and setting the sqlType accordingly, but since java.sql.Types doesn't handle it I don't see how to use native sql enum.
best regards!