doctrine 2 - removing all elements from arraycollection does not invoke any Event - symfony

I need to invoke event when arraycollection is cleared (has no elements), pre/postUpdate is not invoked then. Only if I change contents of arraycollection and there is at least one element after change pre/postUpdate events are invoked.
Any Idea how to make it work?

public function postUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$em = $args->getEntityManager();
if ($entity instanceof Track) {
// remove all old statuses
$query = $em->createQuery(
'SELECT os
FROM CuculoERPBundle:OrderStatus os
WHERE os.track = :track'
)
->setParameter(':track', $entity->getId());
$orderStatusArray = $query->getResult();
foreach ($orderStatusArray as $orderStatus) {
$em->remove($orderStatus);
}
// add new statuses
foreach ($entity->getOrders() as $order) {
var_dump($order->getId());
$orderStatus = new OrderStatus();
$orderStatus->setOrder($order);
$orderStatus->setTrack($entity);
$orderStatus->setStep($em->getReference('Cuculo\ERPBundle\Entity\OrderStep', 6));
$em->persist($orderStatus);
}
$this->needsFlush = true;
}//end if
}//end postUpdate()
I managed to do this by copying code after // remove all old statuses to UpdateAction and adding conditional to count ArrayCollection and if 0 exec this code.

Related

onFlush listener, getting entity Id for a scheduled entity insertions

I have a listener for onFlush event.
public function onFlush(OnFlushEventArgs $args) {
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityInsertions() as $entity) {
$id = ...
}
}
I would like to get the Id of the $entity. If I call getId() then return null. Is there any way to finish the flushing inside this listener and get the $entity's Id?
Thank you advance
Accessing the ID before the SQL INSERT command is executed is impossible but you can access the MySQL table's auto increment value with some tricky ways like this:
$classMeta = $em->getClassMetadata($className);
if ($classMeta->rootEntityName !== $className) {
// if the Entity is inherited, only root entity table may have auto increment ID
$classMeta = $em->getClassMetadata($classMeta->rootEntityName);
}
$tableName = $classMeta->getTableName();
$conn = $em->getConnection();
$stmt = $conn->prepare("SHOW TABLE STATUS LIKE '".$tableName."'");
$stmt->execute();
$result = $stmt->fetch(\PDO::FETCH_ASSOC);
$id = $result['Auto_increment'];
Be aware using this:
this solution is hacky, auto increment value may differ from inserted ID
your MySQL user must have permission to run this SQL command

Persist multiple entities in symfony2

I cannot persist multiple entities inside the controller. I can only save the last one.
My code:
$product = new Product();
$names = ['yellow', 'blue', 'red']; // save these to the table
foreach ($name as $name) {
$product->setName($name);
$em->persist($product);
// $em->flush(); // doesn't work either
}
$em->flush();
I am using Symfony 2.7
You have to create a new Product inside your loop.
Right now it's only taking 1 product, and it's constantly updating that one.
$names = ['yellow', 'blue', 'red']; // save these to the table
foreach ($names as $name) {
$product = new Product();
$product->setName($name);
$em->persist($product);
}
$em->flush();
I created this solution which looks nice:
array_walk($arrayOfEntities, function ($entity) {
$entityManager->persist($entity);
});
Use clone operator (php 5+)
$product = new Product();
$names = ['yellow', 'blue', 'red'];
foreach ($names as $name) {
$tmpProductObj = clone $product;
$em->persist($tmpProductObj);
}
$em->flush();
More information about cloning an object can be found here
You are only creating one Object Product.
Obviously, only one object will be persisted to the database then.
Also at the top your variable is called $Product(capital P) while in the loop it is called $product.
Try this instead:
$NameList = array("yellow","blue","red"); // save these to the table
foreach($NameList as $name){
$product = new Product();
$product->setName($name);
$em->persist($Product);
//$em->flush(); // doesnot work either
}
$em->flush();
If I want add multiple objects after set its value I use clone in loop:
$application = new Application();
$application->setSomething($someting);
for ($i = 1; $i <= $request->get('number_of_applications'); $i++){
$applicationObj = clone $application;
$em->persist($applicationObj);
}
$em->flush();

Calculate changeSet for object that have proxy entity in property for logging purpose - Doctrine

I have problem with calculating values for change set. I need old and new value for reference entities inside current one. Thing is that those entites are populated with proxy object to avoid loading it from db. Because I already know old value and new value for ID. So code is looking like this :
// in code
$entity->setEntity1($em->getReference(Entity1::class, $id));
...
// event callback for getting diff
protected function postUpdate(LifecycleEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
$entity = $args->getEntity();
$classMetadata = $em->getClassMetadata(get_class($entity));
$uow->computeChangeSet($classMetadata, $entity);
$changeSet = $uow->getEntityChangeSet($entity);
echo json_encode($changeSet);
}
and result is :
{
"entity1": [
{
"__initializer__": {},
"__cloner__": {},
"__isInitialized__": false
},
{
"__initializer__": {},
"__cloner__": {},
"__isInitialized__": false
}
]
}
and desired output would be :
{
"entity1": [ 101, 105]
}
So proxy entity is translated to its property. How can I avoid doing that and have only IDs instead?
Thanks
Solution is really simple one. I was fooled by printed output of json. Object inside that key of changeSet is not simple one. It's whole proxy object so solution is just to call getId of that proxy and replace value inside change set array.
// event callback for getting diff
protected function postUpdate(LifecycleEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
$entity = $args->getEntity();
$classMetadata = $em->getClassMetadata(get_class($entity));
$uow->computeChangeSet($classMetadata, $entity);
$changeSet = $uow->getEntityChangeSet($entity);
if (array_key_exists('entity1', $changeSet) {
if ($changeSet['entity1'][0] != null) {
$changeSet['entity1'][0] = $changeSet['entity1'][0]->getId();
}
if ($changeSet['entity1'][1] != null) {
$changeSet['entity1'][1] = $changeSet['entity1'][1]->getId();
}
}
// do something with it
}
I am hoping that this will help others and waste time as I am.

EntityChangeSet in preUpdate not containing relations?

I am trying to do something when a User joins a Group. I am trying to use the preUpdate event for it and then check if the corresponding relations have changed. Unfortunately in my Group the 'users" relation is never in the changeset, as well as in my User the Usergroup is never in the changeset.
Here the two listeners:
public function preUpdate(PreUpdateEventArgs $args){
if($args->hasChangedField('users')){
$old = $args->getOldValue('users');
$new = $args->getNewValue('users');
}
}
public function preUpdate(PreUpdateEventArgs $args){
if($args->hasChangedField('userGroups')){
$old = $args->getOldValue('userGroups');
$new = $args->getNewValue('userGroups');
}
}
Thats my TestCase:
$group->addUser($user);
$em->beginTransaction();
$em->persist($group);
$em->flush();
$em->rollback();
Both listeners are called, but the relation is never in the changeset.
I want to add the user into a redis table, where I manage some specific data. Maybe the onFlush or some other events are better, since I don't need to modify the saved data. I just want to know if a there is a new Entry in my User-UserGroup Relation. I thought the easiest way to check this would be the changeset within the preUpdate function.
I solved it via the onFlush event:
public function onFlush(OnFlushEventArgs $args){
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledCollectionUpdates() as $col) {
if($this->isUserGroupUserAssociation($col->getMapping())){
$userGroup = $col->getOwner();
foreach($col->getInsertDiff() as $user){
$this->container->get('strego_user.user_manager')->triggerJoined($userGroup,$user);
}
}
}
}
protected function isUserGroupUserAssociation($association){
return($association['fieldName'] == "users" &&
$association['sourceEntity'] == "Strego\UserBundle\Entity\UserGroup"
);
}

Cant flash after preUpdate event

This is what I do in even preUpdate
public function preUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if ($entity instanceof Order) {
if ($args->hasChangedField('status') && $args->getNewValue('status') == 'stock') {
$this->container->get('activity_logger')->writeLog($entity, 'purchase');
}
}
This is where I have error
FatalErrorException: Error: Maximum execution time of 60 seconds
exceeded in /vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php line 2498
public function writeLog ($object, $comment)
{
$entity = new Stock();
$entity->setCategory($object->getIsotope()->getCategory()->getId());
$entity->setComment($comment);
$entity->setDate(new \DateTime('now'));
$entity->setUser($object->getUser()->getId());
$entity->setChange(TRUE);
$this->em->persist($entity);
$this->em->flush();
}
There are store a new entity another way?
find not very nice solution(do it manually) its save and dont touch events
$sql = "INSERT INTO table (field1, field2) VALUES ('foo', 'var')";
$stmt = $em->getConnection()->prepare($sql);
$stmt->bindValue('invoice', $invoiceId);
$result = $stmt->execute();
I think that you can't do this because the output of a managed Entity contains recursive fields.
Take a look at https://github.com/schmittjoh/JMSSerializerBundle :) And log the serialized version ;)
To do that I store the newly created entities in a private array in the EventSuscriber. Then on the onFlush part of the suscriber if that array length is greater than 0 I persist them with a foreach loop. Then, outside the foreach loop, I clear the array (this is very important to prevent infinite loop) and finally call the flush.

Resources