Search code examples
symfonyentity

get autogenerated id for additional field, using post-persist


i have a product with an autogenarete id and also have a productcode field, which grabs values based on user choices combined with the autogenated key to make the productcode. However i cannot grab the autogenate id when inserting a new product.

I used first prepersist & preupdate but that doesn't grab the id when inserting a new product. only when updating it grabs the id

/**
 * @ORM\PrePersist
 * @ORM\PreUpdate
 */
public function setProductcode() 
{


  $option1 = $this->option1;   
  $option2 = $this->option2; 
  $id = $this->id;

  $whole = $option1.''.$option2.''.$id;    

  $this->productcode = $whole;


}

i try to use postpersist, and changed my field to be nullablae true but it saves the productcode as null.

/**
 * @var string
 *
 * @ORM\Column(type="string", length=191, unique=true, nullable=true)
 */
private $productcode;

I used postload and postpersist together and it does show the productcode as output.. but it isn't save it the db.

 * @ORM\PostLoad
 * @ORM\PostPersist

How can i grab the id in the entity to put it in additional field? Thanks in advance!


edit

I made an easyadminsubcriber and it works when i use the pre_persist return. However the code below is updated to post_persist. but i have trouble implementing the flush function together with lifecycleeventargs.

i got the following error back

Argument 2 passed to App\EventSubscriber\EasyAdminSubscriber::setProductcode() must be an instance of Doctrine\Common\Persistence\Event\LifecycleEventArgs, string given, called in 

below is my post_persist code

<?php 
# src/EventSubscriber/EasyAdminSubscriber.php
namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
use App\Entity\Product;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
class EasyAdminSubscriber implements EventSubscriberInterface
{


    public static function getSubscribedEvents()
    {
        return array(
            'easy_admin.post_persist' => array('setProductcode'),
        );
    }


    /**
     * @param LifecycleEventArgs $args
     */
    public function setProductcode(GenericEvent $event, LifecycleEventArgs $args)
    {

      $entityManager = $args->getEntityManager();


        $entity = $event->getSubject();



        if (!($entity instanceof Product)) {
            return;
        }


        $whole = 'yooo'; 


        $entityManager->flush();

        $entity->setProductcode($whole);

        $event['entity'] = $entity;
    }
}

Solution

  • by default, the id is only set, when the entity is flushed to the database. this means, you have to generate your product code after you have flushed the entity and then flush again. doctrine can't use some fancy magic to determine the id before it actually hears back from the database, so there's not really another way. (if you want to do all of this in-entity, I can't imagine another practical and clean way to do this)

    update

    you should use PostPersist (while keeping PreUpdate).

    The postPersist event occurs for an entity after the entity has been made persistent. It will be invoked after the database insert operations. Generated primary key values are available in the postPersist event. (source)

    so, the generated primary key is available there. However, this is only after you flushed the entity. So, you'd have to flush again to write the productcode to the database as well.

    create proper event handlers (because "setProductcode" is a setter, not an event handler, at least name-wise)

    /**
     * PostPersist triggers after the _creation_ of entities in db
     * @ORM\PostPersist
     */
    public function postPersist(LifecycleEventArgs $args) {
        $this->setProductcode();
        // need to flush, so that changes are written to database
        $args->getObjectManager()->flush();
    }   
    
    /**
     * PreUpdate triggers before changes are written to db
     * @ORM\PreUpdate
     */
    public function preUpdate() {
        $this->setProductcode(); 
        // don't need to flush, this happens before the database calls
    }
    

    (see https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#lifecycle-callbacks-event-argument for further information)

    (disclaimer: this answer was heavily edited since it first was created, leaving the connected comments partly without relevant references)