Search code examples
symfonyasynchronousdoctrinemessage-handlers

(Symfony project) Doctrine error: A new entity was found through the relationship. Ecountered when running messageBus async (sync works fine)


I know many people asked this type of question but I believe mine is different because I only encouter the error when trying to run the message async.

This is just a project for personal use to try to implement the strava premium functions my self (I made it work but I just want to handle everything better). Im new to symfony and so I hope I made the question and the problem clear for someone to give me some tips.

In my controller I make a call to function to start loading activities from the strava api and then making some calculations to persist the the new activities and flush them. I encounter this error when running the message: Handling "App\Message\StartLoadingMessage" failed: A new entity was found through the relationship 'App\Entity\Activities#user' that was not configured to cascade persist operations for entity: App\Entity\User@42792

I only encounter this error when running the message async.If im running it synchronously it works perfectly fine. I want to be able to run it async as that makes the user experience just way better if I can do other things while the function is running in the background.

In my controller I make the call to start loading like so:

$user = $this->getUser();
$messageBus->dispatch(new StartLoadingMessage($user));

The StartLoadingMessage class is just the standard message class

The StartLoadingMessageHandler class is like so:

class StartLoadingMessageHandler implements MessageHandlerInterface
{
    public function __construct(private StravaRequests $startLoading)
    {
        $this->startLoading = $startLoading;
    }

    public function __invoke(StartLoadingMessage $message)
    {
        // Run the background task here
        $user = $message->getUser();
        $this->startLoading->startLoading($user);
    }
}

And then in the actual startloading function I make some api calls (without errors) and some calculations and then I try to add the newly created activities to the database like this:

            $newActivity = new Activity();
            $newActivity->setUser($user);
           
            $newActivity->setStravaId($activity['id']);
            $newActivity->setType($activity['type']);
            $newActivity->setRecordData($peaks);
            $this->em->persist($newActivity);
            $this->em->flush();

EDIT after this im also updating the user as so:

 if (count($activities) > 0) {
            $latestEpochBeforeDate = 
          strtotime($activities[count($activities) - 1]['start_date']);
            $user->setLatestLoadedEpochDate($latestEpochBeforeDate);
            
            $this->em->flush();
        }

I dont know if this is the right way im you need to create one new Entity (Activity in my case) and update an other (User)

In the Activity entity I made sure to add the cascade persist line:

 #[ORM\ManyToOne(inversedBy: 'activities', cascade:['persist'])]
    #[ORM\JoinColumn(nullable: false)]
    private ?User $user = null;

And I tried to explicitly call EntityManager#persist() on the user entity in my startloading function but that does not work either.


Solution

  • I guess the problem comes from your User entity that is currently detached from Doctrine scope when running in async. The way to correct it is to use below code if your version of Doctrine accept it:

    $newActivity = new Activity();
    $newActivity->setUser($user);
               
    $newActivity->setStravaId($activity['id']);
    $newActivity->setType($activity['type']);
    $newActivity->setRecordData($peaks);
    
    $this->em->merge($user);
    $this->em->persist($newActivity);
    $this->em->flush();
    

    Or simply query the User using your UserRepository with the User ID you have and replace your actual $user variable.