4

I use two concepts of Doctrine in my model :

  • class-table inheritance
  • Lifecycle callbacks

When I load a inherited instance of my parent class, the parent property which is updated with the lifecycle callback is wrong, well, not the value I have in the parent class table of my database.

Parent {abstract} class: User

use DateTime;

/**
 * User
 *
 * @ORM\Table(name="users")
 * @ORM\Entity(repositoryClass="MyProject\CoreBundle\Repository\UserRepository")
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="entity_name", type="string")
 * @ORM\DiscriminatorMap({"member" = "Member", "administrator" = "Administrator"})
 * @ORM\HasLifecycleCallbacks()
 */
abstract class User implements AdvancedUserInterface, Serializable
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var DateTime
     *
     * @ORM\Column(name="created_at", type="datetime")
     */
    protected $createdAt;

    /**
     * @var DateTime
     *
     * @ORM\Column(name="updated_at", type="datetime")
     */
    protected $updatedAt;

    // ...

    public function __construct()
    {
        $this->createdAt = new DateTime();
        $this->updatedAt = new DateTime();
        // ...

        return $this;
    }

    /**
     * @ORM\PreUpdate
     */
    public function onPreUpdate()
    {
        $this->updatedAt = new DateTime();
    }
}

Child class: Member

/**
 * Member
 *
 * @ORM\Table(name="members")
 * @ORM\Entity(repositoryClass="MyProject\CoreBundle\Repository\MemberRepository")
 * @ORM\HasLifecycleCallbacks()
 */
class Member extends User
{
    // ...

    public function __construct()
    {
        parent::__construct();

        // ...

        return $this;
    }
}

Then in the controller

use MyProject\CoreBundle\Entity\Member;

public function someAction(Member $member)
{
    $em = $this->getDoctrine()->getManager();
    $member = $em->getRepository(Member::class)->find(1);

    exit(var_dump($member->getUpdatedAt()));
    // --> date of today, why?
}

Here is an extract of my database content

// users
|----|---------------------|---------------------|-------------|
| id |     created_at      |      updated_at     | entity_name |
|----|---------------------|---------------------|-------------|
| 1  | 2016-01-01 00:00:00 | 2016-06-01 12:00:00 |    member   |
|----|---------------------|---------------------|-------------|

// members
|----|
| id |
|----|
| 1  |
|----|

When I debug the updatedAt property of my member, I expect the value to be a DateTime object corresponding to 2016-06-01 12:00:00, and not the date of day...

Is it even related to inheritance loading? Can't figure out where do that come from.

Flo Schild
  • 5,104
  • 4
  • 40
  • 55

1 Answers1

-1

Your updatedAt could come from User class. In __construct() of your User class, you populate the data using $this->createdAt = new DateTime() and $this->updatedAt = new DateTime(). I think it's a bad practice, because createdAt is supposed to be filled on User creation, and updatedAt on User creation or update. Try to update your User class to this (removing the __constructor):

use DateTime;

/**
 * User
 *
 * @ORM\Table(name="users")
 * @ORM\Entity(repositoryClass="MyProject\CoreBundle\Repository\UserRepository")
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="entity_name", type="string")
 * @ORM\DiscriminatorMap({"member" = "Member", "administrator" = "Administrator"})
 * @ORM\HasLifecycleCallbacks()
 */
abstract class User implements AdvancedUserInterface, Serializable
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var DateTime
     *
     * @ORM\Column(name="created_at", type="datetime")
     */
    protected $createdAt;

    /**
     * @var DateTime
     *
     * @ORM\Column(name="updated_at", type="datetime")
     */
    protected $updatedAt;

    // ...

    /**
     * @ORM\PrePersist
     */
    public function onPrePersist()
    {
        $this->createdAt = new DateTime();
        $this->updatedAt = new DateTime();
    }

    /**
     * @ORM\PreUpdate
     */
    public function onPreUpdate()
    {
        $this->updatedAt = new DateTime();
    }
}
Rendy Eko Prastiyo
  • 1,068
  • 7
  • 16
  • Thanks Rendy but it's not the cause. 1. Doctrine does not call the entity constructor when it loads entities, they are unserialized from the database values. It is only called by the application when I do something like `$member = new Member();`. 2. PrePersist would be called at every `$em->persist($member);` call, so this would definitely update the createdAt timestamp at every update aswell. – Flo Schild Feb 08 '17 at 13:02
  • Wait, how come you have a table of mapped superclass `User`? A mapped superclass is just an abstract class and cannot be an entity (which also cannot have a table). See http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html#mapped-superclasses – Rendy Eko Prastiyo Feb 09 '17 at 03:05
  • I am not using using mapped superclass, but class-table inheritance. Making the User class abstract is just for preventing the application to create an `User` instance, which would not make sense. I need either `Member` instances, either the other `User` child classes of my model :) – Flo Schild Feb 10 '17 at 10:02
  • Yes, it also apply to class-table inheritance, you must declare the parent class-table as abstract. See [Table Inheritance with Doctrine](https://blog.liip.ch/archive/2012/03/27/table-inheritance-with-doctrine.html) for brief explanation and example. – Rendy Eko Prastiyo Feb 11 '17 at 23:05
  • I do not think I must. Doctrine uses annotations to define the SQL scheme. Class-table inheritance is working perfectly fine, and database model is correct, wether I set `User` class as abstract or not. I've read all the doc ; my only trouble is this `updatedAt` timestamp which is uncorrectly populated from database. – Flo Schild Feb 12 '17 at 23:54