Inside the loop, making things dynamic is nothing new... which is great! For in progress, say {{ ship.status }}
. When we refresh, it prints! Though, yikes! The statuses are running way out of their space. Our data doesn't match the design!
// ... lines 1 - 4 | |
{% block body %} | |
<main class="flex flex-col lg:flex-row"> | |
// ... lines 7 - 8 | |
<div class="px-12 pt-10 w-full"> | |
// ... lines 10 - 13 | |
<div class="space-y-5"> | |
{% for ship in ships %} | |
// ... lines 16 - 43 | |
{% endfor %} | |
</div> | |
// ... lines 46 - 50 | |
</div> | |
</main> | |
{% endblock %} |
Plot twist! Someone changed the project's requirements... right in the middle! That "never" happens! The new plan is this: each ship should have a status of in progress
, waiting
, or completed
. Over in src/Repository/StarshipRepository.php
, our ships do have a status
- it's this argument - but it's a string that can be set to anything.
Creating an Enum
So we need to do some refactoring to fit the new plan. Let's think: there are exactly three valid statuses. This a perfect use case for a PHP enum.
If you're not familiar with enums, they're lovely and a great way to organize a set of statuses - like published, unpublished & draft - or sizes - small, medium or large - or anything similar.
In the Model/
directory - though this could live anywhere... we're creating the enum for our own organization - create a new class and call it StarshipStatusEnum
. As soon as I typed the word enum, PhpStorm changed the template from class
to an enum
. So we're not creating a class, as you can see, we created an enum
// ... lines 1 - 2 | |
namespace App\Model; | |
enum StarshipStatusEnum: string | |
{ | |
// ... lines 7 - 9 | |
} |
Add a : string
to the enum to make what's called a "string-backed enum". We won't go too deep, but this allows us to define each status - like WAITING
and assign that to a string, which will be handy in a minute. Add a status for IN_PROGRESS
and finally one for COMPLETED
.
// ... lines 1 - 2 | |
namespace App\Model; | |
enum StarshipStatusEnum: string | |
{ | |
case WAITING = 'waiting'; | |
case IN_PROGRESS = 'in progress'; | |
case COMPLETED = 'completed'; | |
} |
That's it! That's all an enum is: a set of "states" that get centralized in one place.
Next: open up the Starship
class. The last argument is currently a string
status. Change it to be a StarshipStatusEnum
. And at the bottom, the getStatus
method will now return a StarshipStatusEnum
.
// ... lines 1 - 2 | |
namespace App\Model; | |
enum StarshipStatusEnum: string | |
{ | |
case WAITING = 'waiting'; | |
case IN_PROGRESS = 'in progress'; | |
case COMPLETED = 'completed'; | |
} |
Finally, in StarshipRepository
where we create each Starship
, my editor is angry. It says:
Hey! This argument accepts a
StarshipStatusEnum
, but you're passing a string!
Let's calm it down. Change this to StarshipStatusEnum::
... and it autocomplete the choices! Let's make the first one IN_PROGRESS
. And that did add the use
statement for the enum
to the top of the class. For the next one, make it COMPLETED
... and for the last, WAITING
.
// ... lines 1 - 5 | |
use App\Model\StarshipStatusEnum; | |
// ... lines 7 - 8 | |
class StarshipRepository | |
{ | |
// ... lines 11 - 14 | |
public function findAll(): array | |
{ | |
// ... lines 17 - 18 | |
return [ | |
new Starship( | |
// ... lines 21 - 24 | |
StarshipStatusEnum::IN_PROGRESS | |
), | |
new Starship( | |
// ... lines 28 - 31 | |
StarshipStatusEnum::COMPLETED | |
), | |
new Starship( | |
// ... lines 35 - 38 | |
StarshipStatusEnum::WAITING | |
), | |
]; | |
} | |
// ... lines 43 - 53 | |
} |
Refactoring done! Well... maybe. When we refresh, busted! It says:
object of class
StarshipStatusEnum
could not be converted to string
And it's coming from the ship.status
Twig call.
That makes sense: ship.status
is now an enum... which can't be directly printed as a string. The easiest fix, in homepage.html.twig
, is to add .value
.
// ... lines 1 - 4 | |
{% block body %} | |
<main class="flex flex-col lg:flex-row"> | |
// ... lines 7 - 8 | |
<div class="px-12 pt-10 w-full"> | |
// ... lines 10 - 13 | |
<div class="space-y-5"> | |
{% for ship in ships %} | |
<div class="bg-[#16202A] rounded-2xl pl-5 py-5 pr-11 flex flex-col min-[1174px]:flex-row min-[1174px]:justify-between"> | |
<div class="flex justify-center min-[1174px]:justify-start"> | |
// ... line 18 | |
<div class="ml-5"> | |
<div class="rounded-2xl py-1 px-3 flex justify-center w-32 items-center bg-amber-400/10"> | |
// ... line 21 | |
<p class="uppercase text-xs text-nowrap">{{ ship.status.value }}</p> | |
</div> | |
// ... lines 24 - 29 | |
</div> | |
</div> | |
// ... lines 32 - 42 | |
</div> | |
{% endfor %} | |
</div> | |
// ... lines 46 - 50 | |
</div> | |
</main> | |
{% endblock %} |
Because we made our enum string-backed, it has a value
property, which will be the string that we assigned to the current status. Try it now. It looks great! In progress, completed, waiting.
Next: let's learn how we can make this last change a bit more elegant by creating smarter methods on our Starship
class. Then we'll put the finishing touches on our design.