Dentro del bucle, hacer que las cosas sean dinámicas no es nada nuevo... ¡lo cual es genial! Por ejemplo, {{ ship.status }}. Cuando actualizamos, ¡se imprime! Aunque, ¡ay! Los estados se están quedando sin espacio. ¡Nuestros datos no coinciden con el diseño!
| // ... 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 %} |
¡Giro argumental! Alguien cambió los requisitos del proyecto... ¡justo en medio! ¡Eso "nunca" ocurre! El nuevo plan es éste: cada nave debe tener un estado dein progress, waiting, o completed. Ensrc/Repository/StarshipRepository.php, nuestras naves sí tienen un status -es este argumento-, pero es una cadena que puede establecerse con cualquier valor.
Crear un Enum
Así que tenemos que hacer algunas refactorizaciones para adaptarnos al nuevo plan. Pensemos: hay exactamente tres estados válidos. Este es un caso de uso perfecto para una enum PHP.
Si no estás familiarizado con los enums, son encantadores y una forma estupenda de organizar un conjunto de estados -como publicado, no publicado y borrador- o tamaños -pequeño, mediano o grande- o cualquier cosa similar.
En el directorio Model/ -aunque esto podría vivir en cualquier sitio... estamos creando el enum para nuestra propia organización- crea una nueva clase y llámala StarshipStatusEnum. En cuanto escribí la palabra enum, PhpStorm cambió la plantilla de class a unaenum. Así que no estamos creando una clase, como puedes ver, creamos una enum
| // ... lines 1 - 2 | |
| namespace App\Model; | |
| enum StarshipStatusEnum: string | |
| { | |
| // ... lines 7 - 9 | |
| } |
Añade un : string al enum para hacer lo que se llama un "enum respaldado por cadena". No profundizaremos demasiado, pero esto nos permite definir cada estado -como WAITING y asignarlo a una cadena, lo que será útil en un minuto. Añade un estado para IN_PROGRESSy finalmente uno para COMPLETED.
| // ... lines 1 - 2 | |
| namespace App\Model; | |
| enum StarshipStatusEnum: string | |
| { | |
| case WAITING = 'waiting'; | |
| case IN_PROGRESS = 'in progress'; | |
| case COMPLETED = 'completed'; | |
| } |
Y ya está Eso es todo lo que es un enum: un conjunto de "estados" que se centralizan en un solo lugar.
A continuación: abre la clase Starship. El último argumento es actualmente un estado string. Cámbialo para que sea un StarshipStatusEnum. Y en la parte inferior, el método getStatus devolverá ahora un StarshipStatusEnum.
| // ... lines 1 - 2 | |
| namespace App\Model; | |
| enum StarshipStatusEnum: string | |
| { | |
| case WAITING = 'waiting'; | |
| case IN_PROGRESS = 'in progress'; | |
| case COMPLETED = 'completed'; | |
| } |
Por último, en StarshipRepository donde creamos cada Starship, mi editor está enfadado. Dice:
¡Eh! ¡Este argumento acepta un
StarshipStatusEnum, pero estás pasando una cadena!
Vamos a calmarlo. Cambia esto a StarshipStatusEnum::... ¡y autocompleta las opciones! Hagamos que la primera sea IN_PROGRESS. Y eso añadió la declaración use para el enum al principio de la clase. Para la siguiente, que sea COMPLETED... y para la última, 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 | |
| } |
¡Refactorización realizada! Bueno... tal vez. Cuando actualizamos, ¡arruinado! Dice
el objeto de clase
StarshipStatusEnumno se ha podido convertir a cadena
Y viene de la llamada a Twig de ship.status.
Tiene sentido: ship.status es ahora un enum... que no puede imprimirse directamente como cadena. La solución más fácil, en homepage.html.twig, es añadir .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 %} |
Como hemos hecho que nuestro enum esté respaldado por una cadena, tiene una propiedad value, que será la cadena que asignamos al estado actual. Pruébalo ahora. ¡Tiene una pinta estupenda! En curso, completado, esperando.
A continuación: vamos a aprender cómo podemos hacer este último cambio un poco más elegante creando métodos más inteligentes en nuestra clase Starship. Luego daremos los toques finales a nuestro diseño.
6 Comments
Hi,
thank you for this very interesting video.
There are some places in my Symfony App that needs to be replaced by enums. However how do I have to make sure that my entities are loaded correctly from the database?
Suppose I have my Starships persisted in a database and the attribute status WAS of type string. How do I take care that the string is correctly transformed into an enum?
Thanks
Oliver
Hey Oliver,
That's a fair question if I understand it correctly :) If you're worry about data loss - make sense. That's easy to implement it on an empty DB or when you create a new column that just does not exist on production yet. But if you already have some VARCHAR strings that you want to properly convert to ENUM. First of all, IIRRC the migration should do all the job for you, you just need to create the full ENUM, i.e. ENUM with all the possible values on your production. here's the steps to follow:
SELECT DISTINCT status FROM your_table.You will need to dobule-check the migration, e.g. create some fake data locally, try to migrate and see if that lead to any data loss or no. If everything work correctly and you covered all the possible values - that just should work for prod as well.
I hope this helps!
Cheers!
Hi desanchextorres,
thank you very much für this adivse.
I tried to create a new field first, that is of type enum, but I stopped with the error, that with
symfony console make:entityI received the error
[ERROR] Class "ExerciseCategory" doesn't exist; please enter an existing full class name.However it does exist.
How can I reference enums in make:entity correctly?
For example my enum is located under
src/Entity/ExerciseCategory.php
Oliver
Hey Oliver,
It should work with just class name, i.e. ExerciseCategory should be ok, but please double check that you don't have any misprints in it or in the file name and class name in that file. Also, that entity should be in
src/Entity/ExerciseCategory.phpand hasApp\Entitynamespace there. If still no success, probably try to update the maker bundle, not sure why it does not work for you.If just class name does not work:
You can also try to specify the full class name like:
But it's a weird error, because even if the file does not exist, it would still create it then. So I bet you just made a misprint somewhere :)
Btw,
make:entity --helpsometimes may help.Cheers!
Hi Victor,
I guess I was not precise enough in my description:
I have en existing entity:
and I have a new created enum
Now I want to add a new attribute to entity Exercise that is of type ExerciseCategory. For this I wanted to use
symfony console make:entity->
I am pretty sure, that it will work manually by creating the attribute exerciseCategory like this
private enum ExerciseCategory exerciseCategory;by hand. But I wanted to try it with the console command.
Hey Oliver,
Ah, I see now :) OK, first of all ExerciseCategory is not an entity, right? That's why it should not be inside the src/Entity/ class... Create a different folder for it in the src/ e.g. src/Enums/ or src/Model/ or src/Category/ and move that file there. Don't forget to change the namespace correspondingly!
Next, run that make:entity command again, and when you will choose
enum- write the full namespace to the file, e.g.App\Enums\ExerciseCategory. In some cases, you may need to escape back slashes. So if that will not work - try:App\\Enums\\ExerciseCategory.Cheers!
"Houston: no signs of life"
Start the conversation!