Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Updating an Entity

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

We are successfully changing the value of the votes property. Now we need to make an update query to save that to the database.

To insert a VinylMix, we used the EntityManagerInterface service, and then called persist() and flush(). To update, we'll use that exact same service.

Updating an Entity with the Entity Manager

Add a new argument to the vote() method type-hinted with EntityManagerInterface. I'll call it $entityManager. Then, very simply, after we've set the votes property to the new value, call $entityManager->flush().

... lines 1 - 12
class MixController extends AbstractController
{
... lines 15 - 44
public function vote(VinylMix $mix, Request $request, EntityManagerInterface $entityManager): Response
{
... lines 47 - 53
$entityManager->flush();
... line 55
}
}

That's it people! Before I explain this, let's make sure it works. Refresh. We have 49 votes right now. I'll hit up. It says 50. But the real proof is that when we refresh... it still shows 50! It did save!

Persisting and Flushing: The Details

Ok, so when we created a new VinylMix earlier, we had to call persist() - passing the VinylMix object - and then flush(). But now, all we need is flush(). Why?

Here's the full story. When you call flush(), Doctrine loops over all of the entity objects that it "knows about" and "saves" them. And that "save" is smart. If Doctrine determines that an entity has not been saved yet, it will execute an INSERT query. But if it's an object that does already exist in the database, Doctrine will figure out what has changed on the object - if anything - and execute an UPDATE query. Yep! We just call flush() and Doctrine figures out what to do. It's... the best thing since Starburst Jellybeans.

But... why don't we need to call persist() when we're updating? Well, you can say $entityManager->persist($mix) if you want to. It's just... totally redundant!

When you call persist(), it tells Doctrine:

Hey! I want you to be aware of this object so that, next time I call flush(), you'll know to save it.

When you create a new entity object, Doctrine doesn't really know about that object until you call persist(). But when you're updating an entity, it means that you've already asked Doctrine to query for that object. So Doctrine is already aware of it... and when we call flush(), Doctrine will - automatically - check that object to see if any changes have been made to it.

Redirecting to Another Page

So... we are successfully saving the new vote count to the database! Now what? Because... I don't think this die statement is going to look good on production.

Well, anytime you submit a form successfully, you always do the same thing: redirect to another page. How do we redirect in Symfony? With return $this->redirect() passing whatever URL you want to redirect to. Though, usually we're redirecting to another page on our site... so we use a similar shortcut called redirectToRoute() and then pass a route name.

Let's redirect back to the show page. Copy the app_mix_show route name, paste... and just like with the Twig path() function, this accepts a second argument: an array of the route wildcards that we need to fill in. In this case, we have an {id} wildcard... so pass id set to $mix->getId().

... lines 1 - 44
public function vote(VinylMix $mix, Request $request, EntityManagerInterface $entityManager): Response
{
... lines 47 - 55
return $this->redirectToRoute('app_mix_show', [
'id' => $mix->getId(),
]);
}
... lines 60 - 61

Now, remember: controllers always return a Response object. And, whelp it turns out that a redirect is a response. It's a response that, instead of containing HTML, basically says:

Please send the user to this other URL

The redirectToRoute() method is a shortcut that returns this special response object, called a RedirectResponse.

Anyways, let's test the whole flow! Refresh, and... got it! After voting, we end up right back on this page. And, thanks to Turbo, this is all happening via Ajax calls... which is a nice bonus.

The only problem is that... it's so smooth that it's not super obvious that my vote was actually saved - other than seeing the vote number change. It might be better if we showed a success message. Let's do that next by learning about flash messages. We're also going to make our VinylMix entity trendier by exploring the concept of smart versus anemic models.

Leave a comment!

2
Login or Register to join the conversation
thephilosoft Avatar
thephilosoft Avatar thephilosoft | posted 2 days ago

In a challenge after this video. Shouldn't 1 be the answer? Or at least there should be a call for setPrice with a different value...

1 Reply

Hey Thephilosoft,

Yes, you're totally correct, the price should be changed to make the 2nd query, otherwise the Doctrine is smart enough to ignore the last flush() call. Thank you for reporting it, I changed the price in the last setPrice() call to avoid editing the explanation message.

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "babdev/pagerfanta-bundle": "^3.7", // v3.7.0
        "doctrine/doctrine-bundle": "^2.7", // 2.7.0
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
        "doctrine/orm": "^2.12", // 2.12.3
        "knplabs/knp-time-bundle": "^1.18", // v1.19.0
        "pagerfanta/doctrine-orm-adapter": "^3.6", // v3.6.1
        "pagerfanta/twig": "^3.6", // v3.6.1
        "sensio/framework-extra-bundle": "^6.2", // v6.2.6
        "stof/doctrine-extensions-bundle": "^1.7", // v1.7.0
        "symfony/asset": "6.1.*", // v6.1.0
        "symfony/console": "6.1.*", // v6.1.2
        "symfony/dotenv": "6.1.*", // v6.1.0
        "symfony/flex": "^2", // v2.2.2
        "symfony/framework-bundle": "6.1.*", // v6.1.2
        "symfony/http-client": "6.1.*", // v6.1.2
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/proxy-manager-bridge": "6.1.*", // v6.1.0
        "symfony/runtime": "6.1.*", // v6.1.1
        "symfony/twig-bundle": "6.1.*", // v6.1.1
        "symfony/ux-turbo": "^2.0", // v2.3.0
        "symfony/webpack-encore-bundle": "^1.13", // v1.15.1
        "symfony/yaml": "6.1.*", // v6.1.2
        "twig/extra-bundle": "^2.12|^3.0", // v3.4.0
        "twig/twig": "^2.12|^3.0" // v3.4.1
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
        "symfony/debug-bundle": "6.1.*", // v6.1.0
        "symfony/maker-bundle": "^1.41", // v1.44.0
        "symfony/stopwatch": "6.1.*", // v6.1.0
        "symfony/web-profiler-bundle": "6.1.*", // v6.1.2
        "zenstruck/foundry": "^1.21" // v1.21.0
    }
}