This course is still being released! Check back later for more chapters.

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
Login to bookmark this video
Buy Access to Course
04.

Herencia de tablas de clases

|

Share this awesome video!

|

Keep on Learning!

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Es hora de sumergirnos en el último tipo de Herencia Doctrine: la Herencia de Tabla de Clase. Ésta vuelve a utilizar una única tabla por entidad en la jerarquía. Pero aquí está la diferencia: en la tabla sólo se almacenan las propiedades específicas de esa entidad (más el id). Al buscar, Doctrine realiza las uniones necesarias entre bastidores para obtener todos los datos de la entidad que estás buscando.

En Starship, en el atributo InheritanceType, cambia SINGLE_TABLEpor JOINED:

116 lines | src/Entity/Starship.php
// ... lines 1 - 8
#[ORM\InheritanceType('JOINED')]
// ... lines 10 - 14
abstract class Starship
// ... lines 16 - 116

Doctrine utilizaba tradicionalmente JOINED para referirse a este tipo de herencia, pero se conoce más comúnmente como Herencia de tabla de clases. ¡Ya está!

Note

Aunque este cambio de código es superfácil, los cambios en la base de datos no lo son. Si tuvieras una base de datos ya establecida con datos en ella, tendrías que hacer algunas migraciones cuidadosas.

Para mostrar mejor cómo cambia el SQL, eliminemos el esquema y empecemos de cero. En el terminal, ejecuta

symfony console doctrine:schema:drop --force

Ahora, crea el esquema con:

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

Observar los cambios

Veamos lo que tenemos aquí. La tabla del carguero sólo tiene id y cargo_capacity. La tabla starship tiene todas las propiedades comunes, y la tabla scout sólo tiene idy sensor_range.

Carguemos nuestros accesorios para asegurarnos de que todo sigue funcionando.

symfony console foundry:load-fixtures

Se ve bien. Salta a la aplicación y actualiza. ¡Perfecto! ¡Seguimos teniendo seis naves!

Comprobación de la base de datos

Ahora, echemos un vistazo a la base de datos para ver con qué estamos tratando. De nuevo en el terminal, ejecuta:

symfony console doctrine:query:sql 'select * from starship'

Note

Recuerda que este comando ha cambiado a dbal:run-sql en las nuevas versiones de Doctrine.

Como puedes ver, sólo contiene las columnas de la entidad Starship, no las deFreighter y Scout. Sin embargo, sigue incluyendo una ship_type. Y sí, tenemos tres cargueros y tres exploradores. Si queremos ver las propiedades específicas de éstos, están en sus respectivas tablas.

Veamos la tabla de los cargueros. En primer lugar, fíjate en la columna id para los cargueros, 1, 2 y 3. Deberían coincidir con los identificadores de la tabla de cargueros. Ejecuta:

symfony console doctrine:query:sql 'select * from freighter'

Esto sólo tiene los cargo_capacity's de los tres cargueros, y efectivamente, los ids coinciden con los ids de la tabla de naves estelares. Así, cuando Doctrine construye (o hidrata) una nave estelar, sabe que la nave con id 1 es un carguero. Entonces instanciará un objeto Freighter, tomará las propiedades comunes de la tabla de naves estelares, se unirá con la tabla de cargueros en el id y tomará la capacidad de carga. Uf, ¡me alegro de que Doctrine haga todo eso por nosotros!

Inspección del perfilador

Volviendo a la aplicación, dediquemos un momento a comprobar el perfilador y ver esto en acción. Haz clic en la consulta formateada para verla mejor. La consulta es un poco más compleja que antes, debido a las uniones, pero hace exactamente lo que esperábamos.

Añadir una nueva entidad

Vamos a subir la apuesta con un escenario un poco más avanzado. Vamos a crear otra nave: un carguero minero. Será una subclase de Freighter, lo que nos llevará a otro nivel de profundidad en el árbol de herencia.

De vuelta al terminal, crea la entidad con:

symfony console make:entity MiningFreighter

Dale una propiedad llamada laserPower, un número entero, no anulable, y listo. Ahora la fábrica de la Fundición:

symfony console make:factory

Elige all para crear el MiningFreighter que falta.

De vuelta en nuestro IDE, abre nuestra nueva entidad MiningFreighter. Haz que extienda Freighter y elimina la propiedad id y el getter:

26 lines | src/Entity/MiningFreighter.php
// ... lines 1 - 7
#[ORM\Entity(repositoryClass: MiningFreighterRepository::class)]
class MiningFreighter extends Freighter
// ... lines 10 - 26

Actualizar la fábrica

Ahora, abre el MiningFreighterFactory. Tenemos que hacer algunos ajustes en el FreighterFactory antes de poder extenderlo, así que ábrelo.

Elimina el final para que podamos ampliarlo. A continuación, tenemos que añadir una plantilla para que las subfábricas puedan especificar el tipo de entidad que están creando. En el docblock de la clase, añade@template T of Freighter. Luego, en el @extends, amplíalo a T:

27 lines | src/Factory/FreighterFactory.php
// ... lines 1 - 6
/**
* @template T of Freighter
* @extends StarshipFactory<T>
*/
class FreighterFactory extends StarshipFactory
// ... lines 12 - 27

De vuelta en MiningFreighterFactory, haz que la clase extienda a FreighterFactory. Haz lo mismo para @extends en el docblock:

27 lines | src/Factory/MiningFreighterFactory.php
// ... lines 1 - 7
/**
* @extends FreighterFactory<MiningFreighter>
*/
final class MiningFreighterFactory extends FreighterFactory
// ... lines 12 - 27

Abajo en defatuls(), añade nuestro truco array_merge. array_merge(parent::defaults(), el segundo argumento, la matriz, y no olvides cerrar la función:

27 lines | src/Factory/MiningFreighterFactory.php
// ... lines 1 - 10
final class MiningFreighterFactory extends FreighterFactory
{
// ... lines 13 - 18
#[\Override]
protected function defaults(): array
{
return array_merge(parent::defaults(), [
'laserPower' => self::faker()->randomNumber(),
]);
}
}

Aquí tenemos un inicio genial de array_merge. El MiningFreighterFactory se está fusionando con los valores por defecto del FreighterFactory, que se está fusionando con los valores por defecto del StarshipFactory.

Crear algunos datos

A continuación, en AppStory, crea unos cuantos cargueros mineros con MiningFreighterFactory::createMany(2):

21 lines | src/Story/AppStory.php
// ... lines 1 - 11
final class AppStory extends Story
{
public function build(): void
{
// ... lines 16 - 17
MiningFreighterFactory::createMany(2);
}
}

De vuelta en el terminal, vuelve a cargar los accesorios.

symfony console foundry:load-fixtures

Uy, tenemos un error... "La entidad MiningFreighter tiene que formar parte del mapa discriminador de Starship para estar correctamente mapeada en la jerarquía de herencia"

Este es un paso habitual que se olvida al añadir nuevas entidades a una jerarquía de herencia.

De vuelta en Starship, en la matriz DiscriminatorMap, añade nuestra nueva entidad con'mining_freighter' => MiningFreighter::class:

117 lines | src/Entity/Starship.php
// ... lines 1 - 10
#[ORM\DiscriminatorMap([
// ... lines 12 - 13
'mining_freighter' => MiningFreighter::class,
])]
abstract class Starship
// ... lines 17 - 117

Me gusta utilizar snake case para las claves, pero puedes utilizar lo que quieras. Lo importante es que el valor sea el nombre de la clase de la entidad.

Ahora carga de nuevo nuestros accesorios.

symfony console foundry:load-fixtures

Bien, ¡ha funcionado! Actualiza nuestra aplicación... ¡y voilá! ¡Ya tenemos ocho naves!

Conclusión

Vuelve a echar un vistazo a la consulta en el perfilador. Se ha añadido otro join para la tabla mining_freighter.

Este es uno de los contras de la herencia de tablas de clases, las consultas se vuelven más complejas cuantas más entidades tengas en tu jerarquía. Esto puede reducir el rendimiento.

Otro contra en el que quizá no pienses es que, si utilizas herramientas externas para consultar y manipular tu base de datos, es más difícil trabajar con ella. Tienes que duplicar las uniones y la lógica que Doctrine utiliza internamente.

Como en la mayoría de las cosas, hay ventajas y desventajas con cada tipo de herencia.

A continuación, veremos cómo consultar los distintos tipos de naves estelares.