Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Añadir nuevas propiedades

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

En nuestra entidad VinylMix, olvidé añadir antes una propiedad: votes. Vamos a llevar la cuenta del número de votos a favor o en contra que tiene una determinada mezcla.

Modificación con make:entity

Bien... ¿cómo podemos añadir una nueva propiedad a una entidad? Bueno, podemos hacerlo a mano: todo lo que tenemos que hacer es crear la propiedad y los métodos getter y setter. Pero, una forma mucho más fácil es volver a nuestro comando favorito make:entity:

php bin/console make:entity

Éste se utiliza para crear entidades, pero también podemos utilizarlo para actualizarlas. Escribe VinylMix como nombre de la clase y... ¡ve que existe! Añade una nueva propiedad: votes... conviértela en integer, di "no" a anulable... y pulsa "intro" para terminar.

¿El resultado final? Nuestra clase tiene una nueva propiedad... y métodos getter y setter a continuación.

... lines 1 - 9
class VinylMix
{
... lines 12 - 31
#[ORM\Column]
private ?int $votes = null;
... lines 34 - 99
public function getVotes(): ?int
{
return $this->votes;
}
public function setVotes(int $votes): self
{
$this->votes = $votes;
return $this;
}
}

Generar una segunda migración

Bien, pensemos. Tenemos una tabla vinyl_mix en la base de datos... pero aún no tiene la nueva columna votes. Tenemos que modificar la tabla para añadirla. ¿Cómo podemos hacerlo? Exactamente igual que antes: ¡con una migración! En tu terminal, ejecuta:

symfony console make:migration

Luego ve a ver la nueva clase.

... lines 1 - 12
final class Version20220718170741 extends AbstractMigration
{
... lines 15 - 19
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE vinyl_mix ADD votes INT NOT NULL');
}
... lines 25 - 31
}

¡Esto es increíble! Dentro del método up(), dice

ALTER TABLE vinyl_mix ADD votes INT NOT NULL

Así que vio nuestra entidad VinylMix, comprobó la tabla vinyl_mix en la base de datos y generó una diferencia entre ellas. Se dio cuenta de que, para que la base de datos se pareciera a nuestra entidad, tenía que alterar la tabla y añadir esa columna votes. Eso es simplemente increíble.

De vuelta al terminal, si ejecutas

symfony console doctrine:migrations:list

verás que reconoce ambas migraciones y sabe que no ha ejecutado la segunda. Para ello, ejecuta:

symfony console doctrine:migrations:migrate

Doctrine es lo suficientemente inteligente como para saltarse la primera y ejecutar la segunda. ¡Qué bien!

Cuando despliegues a producción, todo lo que tienes que hacer es ejecutar doctrine:migrations:migratecada vez. Se encargará de ejecutar todas y cada una de las migraciones que la base de datos de producción aún no haya ejecutado.

Dar valores por defecto a las propiedades

Bien, una cosa más rápida mientras estamos aquí. Dentro de VinylMix, la nueva propiedad votestiene como valor por defecto null. Pero cuando creamos un nuevo VinylMix, tendría mucho sentido poner los votos por defecto a cero. Así que cambiemos esto a = 0.

¡Genial! Y si hacemos eso, la propiedad en PHP ya no necesita permitir null... así que elimina el ?. Como estamos inicializando a un entero, esta propiedad siempre será un int: nunca será nulo.

... lines 1 - 9
class VinylMix
{
... lines 12 - 32
private int $votes = 0;
... lines 34 - 110
}

Pero... Me pregunto... porque he hecho este cambio, ¿tengo que modificar algo en mi base de datos? La respuesta es no. Puedo probarlo ejecutando un comando muy útil:

symfony console doctrine:schema:update --dump-sql

Es muy parecido al comando make:migration... pero en lugar de generar un archivo con el SQL, sólo imprime el SQL necesario para actualizar tu base de datos. En este caso, muestra que nuestra base de datos ya está sincronizada con nuestra entidad.

La cuestión es: si inicializamos el valor de una propiedad en PHP... eso es sólo un cambio en PHP. No cambia la columna en la base de datos ni le da un valor por defecto, lo cual está totalmente bien.

Autoconfiguración de createdAt

Vamos a inicializar otro campo: $createdAt. Sería increíble que algo estableciera automáticamente esta propiedad cada vez que creamos un nuevo objeto VinylMix... en lugar de tener que establecerla nosotros manualmente.

Podemos hacerlo creando un método PHP __construct() a la vieja usanza. Dentro, digamos $this->createdAt = new \DateTimeImmutable(), que por defecto será ahora mismo.

... lines 1 - 9
class VinylMix
{
... lines 12 - 34
public function __construct()
{
$this->createdAt = new \DateTimeImmutable();
}
... lines 39 - 115
}

Y ya está Y... ya no necesitamos el = null ya que se inicializará aquí abajo... y tampoco necesitamos el ?, porque siempre será un objetoDateTimeImmutable.

... lines 1 - 9
class VinylMix
{
... lines 12 - 29
private \DateTimeImmutable $createdAt;
... lines 31 - 115
}

¡Muy bien! Gracias a esto, la propiedad $createdAt se establecerá automáticamente cada vez que instanciemos nuestro objeto. Y eso es sólo un cambio de PHP: no cambia la columna en la base de datos.

Muy bien, tenemos una entidad VinylMix y la tabla correspondiente. A continuación, vamos a instanciar un objeto VinylMix y guardarlo en la base de datos.

Leave a comment!

5
Login or Register to join the conversation
Dima Avatar
Dima Avatar Dima | posted hace 3 meses | edited

We changed $votes property never to be null. So can we remove "?" in getVotes method (or this will provide errors during for example form saving)? And the same question about getCreatedAt method

Reply

Hey @Dima

Yes, you're right. Since those fields are not nullable anymore, you can do that refactoring, but as you said, if you are using Symfony Forms to create new VinylMix records, then you can't remove it from the $votes property

Cheers!

1 Reply
wh Avatar

concerning default values...
Am I right to understand that the way you've shown here (i.e. creating the entity > altering the code to insert defaults > not migrating the code changes), the table would still accept null values, but no value would ever be null because I changed the code?
If so, how could I make the table no longer accept null values? Should I run the symfony console make:migration command again? Or isn't there a way to do this once it's been set as nullable?

Reply
wh Avatar

...? Can anybody help me with the above question?

Reply

Hey!

Sorry we missed your original question somehow! Not cool!

the table would still accept null values, but no value would ever be null because I changed the code?

Not quite :). There are two totally independent systems going on:

A) By adding the defaults in PHP, it means that the properties on the PHP object will never been null. So, you've got that correct.
B) But, whether or not the database will accept null values is determined by something else: the nullable: option on ORM\Column. For example, you could have:

#[ORM\Column(nullable: true)
private $name = 'ryan';

In this case, in PHP, the name property could never be null. But, technically, you COULD insert a row into the database with a null value (and yes, if you changed nullable: false, you would need to run a database migration for that).

So, to answer this question:

how could I make the table no longer accept null values? Should I run the symfony console make:migration command again? Or isn't there a way to do this once it's been set as nullable?

The answer is: if you want a column in your table to NO accept null values (i.e.to explode with an error if null is sent), use nullable: false on that property (or omit the nullable option entirely, because false is the default value for nullable).

Let me know if that clears things up... or if I'm still being confusing :).

Cheers!

2 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
    }
}
userVoice