Phill Pafford
Phill Pafford

Reputation: 85328

Doctrine - Add default time stamp to entity like NOW()

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?

Upvotes: 17

Views: 38865

Answers (8)

iloo
iloo

Reputation: 986

My approach was to let the database set that datetime field configured as CURRENT_TIMESTAMP(). The reason to not use Doctrine's PrePersist or calling the corresponding setter manually in PHP was that our database (and I mean data) was shared with other microservices. These microservices didn't even use Doctrine, actually these were not even PHP projects, so we wanted to move the responsibility of setting datetimes to the database, centralized, in one place.

But as the author pointed out, yes, there was a problem of Doctrine passing a null value as datetime upon flushing a persisted entity, to the DB, which gave the following error:

Integrity constraint violation: 1048 Column 'createdAt' cannot be null

And the fix for that was to set insertable: false (which by default is true), thus preventing Doctrine from passing that null value inside queries,

#[ORM\Column(
    name: "createdAt",
    type: "datetime",
    nullable: false,
    insertable: false,
    options: ["default" => "CURRENT_TIMESTAMP"]
)]
private \DateTime $createdAt;

Upvotes: 0

user7621228
user7621228

Reputation:

#[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.

Upvotes: 3

Tuncay Elvanagac
Tuncay Elvanagac

Reputation: 1342

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;

Upvotes: 1

Dennis
Dennis

Reputation: 8111

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

Upvotes: 3

Alexandre
Alexandre

Reputation: 3170

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.

Upvotes: 7

Phill Pafford
Phill Pafford

Reputation: 85328

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

Upvotes: 28

David Baucum
David Baucum

Reputation: 2250

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();
  }

}

Upvotes: 9

Mike Brant
Mike Brant

Reputation: 71384

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

Upvotes: 0

Related Questions