Doctrine DateTime format() errors

A pesky and difficult to debug doctrine problem I’ve been encountering is the following occurring after a flush() call:

PHP Fatal error:  Call to a member function format() on a non-object in /var/www/myapp/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php on line 53

Obviously this gives no info on the actual cause of the error, which it turns out is because of a mismatch between doctrine expecting a DateTime object and instead encountering a string object, which does not have a format() function to call.

The solution is to remove the default string value that doctrine’s orm tools sometimes wrongly attaches to objects and instead use the correct DateTime() object, e.g.:

/**
 * MyObject
 *
 * @ORM\Table(name="my_object_table")
 * @ORM\Entity
 */
class MyObject
{
...
    protected $timestamp = '0000-00-00 00:00:00';
...
}

The first problem if you have a lot of model files is actually finding where the problem is. Whilst there are a host of debug tools which can help, a quick and easy hack is to simply add a stacktrace to the offending doctrine vendor file.

First go to your project’s main folder and vim the relevant file, if you are not a vim file, the substitute it for another one of the many text editors out there:

vim vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php

Then go to line 53 (or whatever line number appears in the error message) by typing :53 (i.e. a semi-colon followed by the line number) where you should see a line reading:

        return ($value !== null)
            ? $value->format($platform->getDateTimeFormatString()) : null;

Go up one line above this and press the a key to enter insert mode and enter:

        if(is_string($value)) {
                debug_print_backtrace();
        }

A good tip is to save the file using :w, but don’t actually close the file. Running your application again you will then print out the usual – hideously ugly – stacktrace. This can be filtered down but even just loooking at the raw ouput should reveal the reflection classes inspecting the model file a couple of steps down which has the incorrectly set line exampled above. This is where you then remove the changes from the above file, by pressing u to undo the changes. Followed by :q to quit the file.

This string needs to be changed to a DateTime() object and you will need to set a default value in the constructor, e.g.:

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

Leaving out the parameter will be set it to the current system time. This will lead to doctrine correctly interpreting and saving timestamps to the database upon flush.

A more complete strategy to automating timestamp & date generation for data using Doctrine is present in my article:

Automating timestamps with Doctrine ORM

Advertisements

Leave a Reply