17

Following the Doctrine guidelines I understand how to set a default value for an Entity, but what if I wanted a date/time stamp?

My problem is my database has a default of NOW() on a field but when I use Doctrine to insert a record the values are null or blank but the rest of the insert happened.

Also since Doctrine says to declare the default as a const, this also creates a problem.

Suggestions?

Mike Brant
  • 70,514
  • 10
  • 99
  • 103
Phill Pafford
  • 83,471
  • 91
  • 263
  • 383

7 Answers7

27

Ok I found the solution:

The prePersist option is what I'm doing.

Make sure you define in the annotations

<?php

/** @Entity 
 *  @HasLifecycleCallbacks 
 */
class User

and here is the function example they offer

/** 
 *  @PrePersist 
 */
public function doStuffOnPrePersist()
{
    $this->createdAt = date('Y-m-d H:i:s');
}

And if you're using ORM like I am

<?php

/** @ORM\Entity 
 *  @ORM\HasLifecycleCallbacks 
 */
class User

and here is the function example they offer

/** 
 *  @ORM\PrePersist 
 */
public function doStuffOnPrePersist()
{
    $this->createdAt = date('Y-m-d H:i:s');
}
Thomas Landauer
  • 7,857
  • 10
  • 47
  • 99
Phill Pafford
  • 83,471
  • 91
  • 263
  • 383
  • 1
    Is there a way to achieve this without using the abomination that is php annotations? – Sejanus Nov 26 '14 at 12:26
  • @Sejanus, yes, Doctrine has XML, YAML, and PHP annotations. See "Drivers" under http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/ – Dennis Nov 16 '15 at 16:03
  • 1
    From the docs: "It should be noted that this event is only triggered on *initial* persist of an entity (i.e. it does not trigger on future updates)" – Dennis Nov 16 '15 at 16:06
  • I think it is worth noting that both this solution and mine below are good solutions. There is an important distinction in how that work. In my solution the timestamp is created when the entity is first instantiated. In this solution the timestamp is set at the time it is persisted to the database. – David Baucum Mar 25 '16 at 21:25
  • This will work but the update will be done by doctrine in symfony layer. If you think that update should be done by db layer, than you need to find how to set `on update CURRENT_TIMESTAMP`. If you find also inform me as well :) – Erce May 31 '19 at 12:52
9

In my experience it is best to put everything in your Entities and not try to force your database to bypass the ORM.

<?php
namespace Phill\PaffordBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
 * Stack
 * @ORM\Table()
 */
class Stack
{
  /**
   * @var integer
   * @ORM\Column(type="integer")
   * @ORM\Id
   * @ORM\GeneratedValue(strategy="AUTO")
   */
  private $id;

  /**
   * @var \DateTime
   * @ORM\Column(type="datetime")
   */
  private $startDate;

  public function __construct()
  {
    $this->startDate = new \DateTime();
  }

}
David Baucum
  • 2,162
  • 24
  • 25
  • 2
    I disagree, here is why: ORM can change, what if I do raw SQL, there are other reasons as well but this should be handled on the database side for at least data integrity – Phill Pafford Aug 07 '14 at 03:04
  • Then it just depends on your needs. Often when using Doctrine it is the developer's intention to program to the ORM and just use the DB as the backend. If that is not the case then stuff will need to be pusehd to the DB, like you are suggesting. I think that for those who are looking to only interface with the ORM then this is the better solution. – David Baucum Aug 08 '14 at 16:27
  • 1
    I think it is worth noting that both this solution and @PhillPafford's above are good solutions. There is an important distinction in how that work. In my solution the timestamp is created when the entity is first instantiated. In this solution the timestamp is set at the time it is persisted to the database. – David Baucum Mar 25 '16 at 21:25
7

In order to have exactly NOW() you could extend Doctrine\DBAL\Types\DateTimeType.

Other method:

class DateTimeNow
{
    public function format() 
    {
        return 'NOW()';
    }
}

Then you can use $entity->setFieldDatetime(DateTimeNow()) instead of $entity->setFieldDatetime(new Datetime()).
Note: The method format() is automatically called by Doctrine.

Thomas Landauer
  • 7,857
  • 10
  • 47
  • 99
Alexandre
  • 3,088
  • 3
  • 34
  • 53
  • Where (i.e. in which file) is this code to be placed (in Symfony)? Thanks! – Thomas Landauer Feb 16 '16 at 13:56
  • @ThomasLandauer I did it durty and had it directely in the file where I need to use it... But would be better to create a bundle in the vendor folder or something like this. – Alexandre Feb 17 '16 at 07:43
  • This answer is the only solution so far which uses the database server's time (rather than PHP's time). Here's why this is a good thing: http://stackoverflow.com/a/3705090/1668200 – Thomas Landauer Feb 24 '16 at 12:39
3

Try this:

/**
 * @var \DateTime
 *
 * @Column(name="created", type="datetime", nullable=false)
 */
private $created;

function __construct()
{
    $this->created = new \DateTime();
}

The whatever $value you assign to created field, has to be able to handle this call:

$value->format('Y-m-d H:i:s');

See relevant Doctrine code line

Tested to work with Doctrine 2.5.4

Note: above works at creation time but not on update by default -- you have to manually set created property to new \DateTime()when you do an update or look into doStuffOnPrePersist

/** @PrePersist */
public function doStuffOnPrePersist()
{
    $this->created = date('Y-m-d H:i:s');
}
Dennis
  • 7,907
  • 11
  • 65
  • 115
3
#[ORM\Column(type: 'datetime_immutable', options: ['default' => 'CURRENT_TIMESTAMP'])]
    private $createdAt;

    #[ORM\Column(type: 'datetime_immutable', options: ['default' => 'CURRENT_TIMESTAMP'], columnDefinition: "DATETIME on update CURRENT_TIMESTAMP")]
    private $modifiedAt;

this is what I found out and I was able to get the "ON UPDATE CURRENT_TIMESTAMP()" however the column becomes nullable which is a better tradeoff in my opinion.

Okmarq
  • 47
  • 6
1

You can use TimestampableEntity Trait for automatically create created_at and updated_at fields in you Entity;

First install doctrine-extensions;

composer require gedmo/doctrine-extensions

Secondly add trait into your Entity Class;

use Gedmo\Timestampable\Traits\TimestampableEntity;

    /**
     * @ORM\Entity(repositoryClass="App\Repository\ProjectRepository")
     */
    class Project
    {
        use TimestampableEntity;
Tuncay Elvanagac
  • 1,048
  • 11
  • 13
0

For PostgreSQL, you should just be able to pass string value of now for the field to get the timestamp to function.

Mike Brant
  • 70,514
  • 10
  • 99
  • 103
  • Thanks, Using PostgreSQL 9.x and when changing the DEFAULT value from now() to CURRENT_TIMESTAMP but it reverts back to now(). Also created a test table and set the datetime column to CURRENT_TIMESTAMP but also reverted to now(). Any other thoughts? – Phill Pafford Dec 13 '12 at 00:05
  • Sorry, didn't realize it was Postgres. I was giving commentary for MySQL which is more typically encountered by PHP users. Have added tag for PostgreSQL. I have updated my answer as well. – Mike Brant Dec 13 '12 at 00:11
  • sorry that didn;'t work either, DateTime issue when passing string NOW – Phill Pafford Dec 13 '12 at 00:20
  • So you are getting a PHP-level error from Doctrine when trying to use that value? If you are only using it for data insert purposes and aren't going to be reading from it, you might trick Doctrine be saying the field is a string. I haven't used Doctrine with Postgres, so I don't really know best approach in this case. – Mike Brant Dec 13 '12 at 00:30