This course is still being released! Check back later for more chapters.
Ship Upgrades: 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.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeOur 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 }}
:
// ... 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
:
// ... 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()
:
// ... 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:
// ... 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...":
// ... 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)
:
// ... 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()
:
// ... 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":
// ... 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.