Login to bookmark this video
Buy Access to Course
14.

Ship Upgrades: Updating an Entity

|

Share this awesome video!

|

Lucky you! You found an early release chapter - it will be fully polished and published shortly!

This Chapter isn't quite ready...

Get Notified About this Course!

We will send you messages regarding this course only
and nothing else, we promise.
You can unsubscribe anytime by emailing us at:
privacy@symfonycasts.com

Our starship repair scheme, ahem, business is doing well! We now have some repeat customers who want some after market upgrades. We need a way to check in an existing starship whose status is completed.

Finding a Completed Ship

This list on our homepage only lists incomplete starships, so we need to find a completed one. In your terminal, run:

symfony console doctrine:query:sql 'SELECT slug, status FROM starship'

lunar-marauder-1 is a completed ship. Copy the slug, and back in the app, visit /starships/lunar-marauder-1. Got it. To better see the update, let's show the arrivedAt date on the show page.

In templates/starship/show.html.twig, copy these h4 and p tags. Paste them below. Update the h4 content to Arrived At and the p to {{ ship.arrivedAt|ago }}:

43 lines | templates/starship/show.html.twig
// ... lines 1 - 4
{% block body %}
// ... lines 6 - 11
<div class="md:flex justify-center space-x-3 mt-5 px-4 lg:px-8">
// ... lines 13 - 15
<div class="space-y-5">
<div class="mt-8 max-w-xl mx-auto">
<div class="px-8 pt-8">
// ... lines 19 - 35
<h4 class="text-xs text-slate-300 font-semibold mt-2 uppercase">Arrived At</h4>
<p class="text-[22px] font-semibold">{{ ship.arrivedAt|ago }}</p>
</div>
</div>
</div>
</div>
{% endblock %}

Back to the app, refresh, and there we go! This ship is completed and arrived 1 month ago.

To check in a ship when it arrives, let's create another command.

app:ship:check-in Command

At your terminal, run:

symfony console make:command

For the name, use app:ship:check-in.

Updating Command Boilerplate

Open the new command class: src/Command/ShipCheckInCommand.php. Update the description - Check-in ship:

43 lines | templates/starship/show.html.twig
// ... lines 1 - 4
{% block body %}
// ... lines 6 - 11
<div class="md:flex justify-center space-x-3 mt-5 px-4 lg:px-8">
// ... lines 13 - 15
<div class="space-y-5">
<div class="mt-8 max-w-xl mx-auto">
<div class="px-8 pt-8">
// ... lines 19 - 35
<h4 class="text-xs text-slate-300 font-semibold mt-2 uppercase">Arrived At</h4>
<p class="text-[22px] font-semibold">{{ ship.arrivedAt|ago }}</p>
</div>
</div>
</div>
</div>
{% endblock %}

and for the constructor, we need the same things as the remove command. Open that, copy the constructor, and paste it over ShipCheckInCommand::__construct():

43 lines | templates/starship/show.html.twig
// ... lines 1 - 4
{% block body %}
// ... lines 6 - 11
<div class="md:flex justify-center space-x-3 mt-5 px-4 lg:px-8">
// ... lines 13 - 15
<div class="space-y-5">
<div class="mt-8 max-w-xl mx-auto">
<div class="px-8 pt-8">
// ... lines 19 - 35
<h4 class="text-xs text-slate-300 font-semibold mt-2 uppercase">Arrived At</h4>
<p class="text-[22px] font-semibold">{{ ship.arrivedAt|ago }}</p>
</div>
</div>
</div>
</div>
{% endblock %}

We'll also find the ship by slug, so copy the configure() method from ShipRemoveCommand and paste it too:

59 lines | src/Command/ShipCheckInCommand.php
// ... lines 1 - 18
class ShipCheckInCommand extends Command
{
// ... lines 21 - 27
protected function configure(): void
{
$this
->addArgument('slug', InputArgument::REQUIRED, 'The slug of the starship')
;
}
// ... lines 34 - 57
}

Command Logic

The first part of execute(), finding the ship by slug, is also the same. Copy that and paste. Update the IO comment to "Checking in starship...":

59 lines | src/Command/ShipCheckInCommand.php
// ... lines 1 - 18
class ShipCheckInCommand extends Command
{
// ... lines 21 - 34
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$slug = $input->getArgument('slug');
$ship = $this->shipRepo->findOneBy(['slug' => $slug]);
if (!$ship) {
$io->error('Starship not found.');
return Command::FAILURE;
}
$io->comment(sprintf('Checking-in starship: %s', $ship->getName()));
// ... lines 48 - 56
}
}

Time for the actual "check-in" logic. First, update the arrived at date to the current time with $ship->setArrivedAt(new \DateTimeImmutable('now')). Then set the status to "waiting" with $ship->setStatus(StarshipStatusEnum::WAITING):

59 lines | src/Command/ShipCheckInCommand.php
// ... lines 1 - 18
class ShipCheckInCommand extends Command
{
// ... lines 21 - 34
protected function execute(InputInterface $input, OutputInterface $output): int
{
// ... lines 37 - 48
$ship->setArrivedAt(new \DateTimeImmutable('now'));
$ship->setStatus(StarshipStatusEnum::WAITING);
// ... lines 51 - 56
}
}

These fields have been updated on the object, but not yet in the database. To execute the UPDATE query, below, call, you guessed it, $this->em->flush():

59 lines | src/Command/ShipCheckInCommand.php
// ... lines 1 - 18
class ShipCheckInCommand extends Command
{
// ... lines 21 - 34
protected function execute(InputInterface $input, OutputInterface $output): int
{
// ... lines 37 - 51
$this->em->flush();
// ... lines 53 - 56
}
}

Just flush()?

Wait, wait, wait! When we persist or remove an entity, we had to call a method - like persist or remove on the entity manager to let Doctrine know our intention. Here, we don't? Nope! Doctrine is super smart. Above, when we found the entity, Doctrine started tracking it. When we call flush(), it sees that it's been modified and determines the best SQL to update the database. So awesome!

Finally, add a success message: "Starship checked-in":

59 lines | src/Command/ShipCheckInCommand.php
// ... lines 1 - 18
class ShipCheckInCommand extends Command
{
// ... lines 21 - 34
protected function execute(InputInterface $input, OutputInterface $output): int
{
// ... lines 37 - 53
$io->success('Starship checked-in.');
return Command::SUCCESS;
}
}

Back in our app, this is the ship we want to check in. Copy the slug from the url.

Running the Command

In your terminal, run the new command with:

symfony console app:ship:check-in

paste the slug and execute! Success! Back in the app, refresh. The ship is now marked as "waiting" and arrived 9 seconds ago. It worked!

Jump back to the check-in logic inside ShipCheckInCommand. We're calling setters to update two fields. Next, let's encapsulate this logic into a method on the Starship entity.