I have added #ORM\Entity and #ORM\HasLifecycleCallbacks annotations in my entity class and later in the entity class I have added the callback method with the right annotation (as following):
/**
*
* #ORM\PreUpdate
* #ORM\PrePersist
*/
protected function PreUpdateHandler()
{
echo '*********** PRE UPDATE *************';
var_dump('*********** PRE UPDATE *************');
return $this;
}
But the PreUpdateHandler is not getting called upon any DB manipulation (insert, update or delete). Any idea what am I missing?
BTW: Where can I see the list of all available event annotations (likes of #ORM\PreUpdate and #ORM\PrePersist)?
IMPORTANT!!!
My entity class is inherited from a base Entity class which is located in another directory (and namespace). I have added the HasLifecycleCallbacks annotation in the metadata of the base class as well. Does this matter for the callbacks to trigger?
Your lifecycle callback method PreUpdateHandler is not invoked because the visibility of method is protected and hence not accessible by the ORM. Change the visibility to public and try.
Try this and check your result
$em->persist($object);
$object->PreUpdateHandler()
$em->flush();
Replace your annotation callback
/**
*
* #ORM\PreUpdate
* #ORM\PrePersist
*/
To
/**
*
* #ORM\PreUpdate()
* #ORM\PrePersist()
*/
SO the thing is that I am using the yml file as well as the entity class and it seems as if the annotations doesn't work in parallel to the yml file. I removed the annotations and added the callbacks in the yml file and its working.
Related
I have an entity I wish to have its updatedAt field updated when a change is made to one of its property (even if one of the value changes to an empty string).
Therefore, I have the following code:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* MyEntity
*
* #ORM\Table(name="my_entity")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class MyEntity
{
// ...
/**
* #var datetime
*
* #ORM\Column(name="updated_at", type="datetime", nullable=false)
*/
protected $updatedAt;
// ...
/**
* #ORM\PreUpdate()
*/
public function preUpdate()
{
$this->updatedAt = new \DateTime();
}
}
So when it comes to:
$entity = $repository->findById($id);
$entity->setOneProperty('oneValue');
$entity->flush();
The updatedAt field is updated with the current datetime in MySQL.
However, when I need to set an empty string as a value, like so:
$entity->setOneProperty('');
$entity->flush();
The updatedAt field doesn't get updated when I need it to.
I am pretty sure this has to do with the following part of the documentation:
preUpdate - The preUpdate event occurs before the database update operations to entity data. It is not called for a DQL UPDATE statement nor when the computed changeset is empty.
So my question is how can I override this behavior and have Doctrine update the field even if the computed changeset is empty? I'm also willing to know how I can access to this very changeset in order to investigate by myself.
Thanks.
What is the value of oneProperty before you set it as an empty string?
One of the way (but it's ugly) is to update your property updatedAt in each setter you need (You can try to use __set). It will work, but it is not the good solution.
One easy way to have an updatedAt field update with every change is by using a constructor in your entity. E.g.
public function __construct()
{
$this->lastModified = new \DateTime();
}
I have problem with Doctrine mapping. First of all I'll introduce my two entites:
/**
* #ORM\Entity
* #ORM\Table(name="header_fotos")
*/
class HeaderFoto extends BaseEntity{
....
/**
*
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="\App\Webpage\Webpage", inversedBy="headerFotos")
* #ORM\JoinColumn(name="webpage_id", referencedColumnName="id")
*/
protected $webpage;
...
}
Second entity:
/**
* #ORM\Entity
* #ORM\Table(name="webpages")
*/
class Webpage extends BaseEntity{
...
/**
* #ORM\OneToMany(targetEntity="\App\Webpage\HeaderFoto", mappedBy="webpage")
*/
protected $headerFotos;
public function __construct() {
parent::__construct();
$this->headerFotos = new ArrayCollection();
}
My problem is with mapping. When I load entity Webpage and try to access all entities of type HeaderFoto it cannot find any relation. I was trying to compare with another working project with Doctrine and everything is the same.
I was trying to change association to OneToOne on the both sides, just for sure. But in this case it returned Exception No mapping found for field webpage.
I will appreciate every help and advice. Thanks for help.
EDIT
To be more specific, it has problem in class \Doctrine\ORM\Persisters\BasicEntityPersister.php in method getOneToManyStatement. It tries to load associated objects, but the array associationMappings in this class is empty. This is the last thing what I have found out.
I have an entity with a custom id (i.e. UUID) generated on __construct function.
namespace AppBundle\Entity;
use Rhumsaa\Uuid\Uuid;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Person
{
/**
* #ORM\Id
* #ORM\Column(type="string")
*/
private $id;
/**
* #ORM\Column(type="string")
*/
private $name;
public function __construct()
{
$this->id = Uuid::uuid4()->toString();
}
This entity is used in sonata and also in other part of the project. I need this entity to have id before persisting and flushing it, so I can not use a an auto-increment.
So, the problem is sonata don't let me create entities because it takes the create option as and edit on executing because that entity already has an id, but this entity does not exists at this moment, so it fails.
The problem isn't the library for generating UUID, any value for 'id' fails.
Anyone know how to solve it? Another similar approach to solve the problem?
You shouldn't set your id in the constructor, but rather use the prePersist Doctrine event to alter your entity before persisting it for the first time.
You may use annotations to do so, see the Doctrine Documentation on prePersist.
The issue with setting the id in the constructor is that you may override it when you're retrieving it from the database, in which case it will be incorrect.
Within the same entity I have a PreUpdate and a PrePersist. The PreUpdate fires, but the PrePersist never does. I put a die() after the flush and comments within the lifecycle callbacks. Full entity can be seen at http://pastebin.com/yUk1u4GQ
Entity callbacks
/**
* #PreUpdate
*/
public function fixDates(){
$this->updatedOn = $this->getNow();
$this->closedDate = null;
$this->openDate = null;
print "dates fixed";
}
/**
* #PrePersist
*/
public function prePersist() {
print 'in prePersist';
die();
}
Entity Manager calls
$em->persist($school);
$em->flush();
die();
My screen reads "dates fixed", but not the prePersist message. I do have the #HasLifecycleCallbacks at the top of the entity.
Don't forget to enable Lifecycle Callbacks in your class annotation :
/**
* Report\MainBundle\Entity\Serveur
* #ORM\HasLifecycleCallbacks
*/
class Serveur {
PrePersist is fired only when you are performing INSERT statement, not UPDATE statement.
When testing, don't forget that the UPDATE statement is fired only when the entity attributes really change. If the Entity Manager is being called to persist that entity, it first looks if there are any changes. If not, no sql query is performed and no #PreUpdate method is called.
I know this question is almost 2 years old, but I just had the exact same problem and since this doesn't have an accepted answer I want to share one last thing everyone else forgot to mention.
Although it seems that the triggered method will be used only by the entity class itself, it's scope should be kept public. My method wasn't triggering just because I marked it as a protected one. I hope this will help someone.
I just had the same problem. Hope this helps you:
I forgot to import the annotations with the use statement. If you try this dont forget to add the "ORM" prefix:
use Doctrine\ORM\Mapping as ORM;
// ...
/**
* #ORM\PreUpdate
*/
public function preUpdate()
{
}
Maybe it version dependent but my working annotations have a next view:
Life cycle class annotation:
/**
* #Entity #Table(name="table_name")
* #HasLifecycleCallbacks
**/
Events annotations:
/** #PrePersist **/
/** #PreUpdate **/
That is all that i have in Model.
I have this situation:
Abstract Class:
abstract class AbstractBase
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #var integer
*/
protected $id;
/**
* #ORM\Column(type="datetime", name="updated_at")
* #var \DateTime $updatedAt
*/
protected $updatedAt;
/**
* #ORM\PreUpdate
*/
public function setUpdatedAt()
{
die('THIS POINT IS NEVER REACHED');
$this->updatedAt = new \DateTime();
}
}
Concrete Class:
/**
* #ORM\Entity(repositoryClass="Entity\Repository\UserRepository")
* #ORM\Table(name="users")
* #ORM\HasLifecycleCallbacks
*/
class User extends AbstractBase
{
// some fields, relations and setters/getters defined here, these all work as expected.
}
Then i call it in my controller like this:
$user = $this->em->find('Entity\User', 1);
// i call some setters here like $user->setName('asd');
$this->em->flush();
die('end');
Everything works as expected, so the id field from the abstract class gets created for the User entity, i can access it etc.
The problem is, that the line "die('THIS POINT IS NEVER REACHED')" is never reached. (Note the #ORM\PreUpdate) This means that lifecycleCallbacks are not called on
inherited objects. Is this a bug, or is there a reason for this?
Your abstract base class has to be anotated as Mapped Superclasses and include the HasLifecycleCallbacks-Annotation.
Further Information: Inheritance Mapping in the Doctrine Documentation.
/**
* #ORM\MappedSuperclass
* #ORM\HasLifecycleCallbacks
*/
abstract class AbstractBase
{
[...]
/**
* #ORM\PreUpdate
*/
public function setUpdatedAt()
{
$this->updatedAt = new \DateTime();
}
}
/**
* #ORM\Entity(repositoryClass="Entity\Repository\UserRepository")
* #ORM\Table(name="users")
*/
class User extends AbstractBase
{
// some fields, relations and setters/getters defined here, these all work as expected.
}
You have to annotate the base class with #ORM\HasLifecycleCallbacks, and the function with #ORM\preUpdate
You have a typo (PreUpdate should be preUpdate), also preUpdate isn't called on creation (only on update). So if you want it also be triggered on creation, you should add #ORM\prePersist.
While the accepted reply is correct for the general case, in this particular case (timestamp) you actually want to use the doctrine extension Timestampable as explained for example here Lifecycle Callback Issue When Extending FOSUserBundle User Entity
It is important that the MappedSuperclass with HasLifecycleCallbacks is in the same namespace or directory as their child Entities.
I had problems with life cycle callbacks when the MappedSuperclass was in one directory (Model) while the Entities were in another (Entity). Putting the MappedSuperclass in the same directory as the Entities (Entity) solved the issue.
Maybe i'm wrong but I don't think preUpdate isn't triggered when you persist an entity. You should have a #prePersist.
http://www.doctrine-project.org/docs/orm/2.0/en/reference/events.html
But still then i'm not sure this is going to work but you could try that. Else a workaround would be to overwrite the setUpdatedAt function and just call his parent one but that's a bit ugly.
Hope the #prePersist helps for you.
Perhaps you could this issue report as a reference how to setup your annotations? The testcase seems to be valid and matches your use case.
I think you have to annotate the base class with #ORM\HasLifecycleCallbacks
docs