¿No entiendes para que emplear las clases abstractas? Ejemplo práctico en Laravel/PHP
Quiero hacer aquí un video un poquito interesante que trata sobre las clases abstractas en PHP. Se podría decir que es un tema común, pero en este caso estamos trabajando dentro de un proyecto en Laravel, y más específicamente, usando Livewire.
Creo que de esta forma es un poco más divertido de explicar el ejemplo, ya que, gracias a toda la interactividad que ofrece Livewire, comunicamos fácilmente el cliente con el servidor mediante eventos. Entonces, por aquí creo que resulta más sencillo de entender.
¿Qué es una clase abstracta en PHP?
Básicamente, una clase abstracta es una clase "normalita" de toda la vida —por ejemplo, un modelo, un controlador, etc.— pero que no puede ser instanciada directamente. Eso no es exclusivo de PHP, sino de la programación orientada a objetos en general.
Entonces, ¿con qué se come esto? Bueno, para eso quiero darte este ejemplo, por si tú también eres un poquito necio como yo (me cuesta hacerlo, incluso cuando forma parte de mi curso completo sobre una tienda en línea con Laravel y Livewire).
De hecho, mientras grababa esa clase pensé:
“Oye, esto debería ser abstracto, ¿por qué no lo coloqué como abstracto?”
Entonces lo hice, lo marqué como abstracto, y de ahí surgió la idea de hacer este video, por si eres tan necio como yo.
El caso práctico: etiquetas en una relación muchos a muchos
abstract class TaggableModel extends Model
{
function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}
Estos métodos están definidos en ese trait y lo importante es que reciben una entidad que es de tipo TaggableModel. Este modelo, a su vez, hereda de Model y es abstracto, que es lo divertido aquí:
<?php
trait TaggableTrait
{
// tags
public function save(TaggableModel $modelTaggable)
{
if ($this->tagSelected) {
$tag = Tag::find($this->tagSelected);
$this->tagsSelected[$tag->id] = $tag->title;
if ($modelTaggable) {
$modelTaggable->tags()->sync(array_keys($this->tagsSelected));
}
}
}
public function delete(TaggableModel $modelTaggable, int $id)
{
unset($this->tagsSelected[$id]);
if ($modelTaggable) {
$modelTaggable->tags()->sync(array_keys($this->tagsSelected));
}
}
}
En él definimos métodos comunes como tags(), que devuelve el listado de etiquetas en la relación polimórfica muchos a muchos.
¿Por qué usar una clase abstracta?
Aquí es donde entra la magia de usar una clase abstracta.
Cuando trabajamos con relaciones polimórfica muchos a muchos, como en este caso, donde una etiqueta puede estar asignada tanto a un Post como a un Book, necesitamos que la lógica común esté agrupada, pero sin permitir instanciar directamente la clase base (TaggableModel).
Fíjate en este ejemplo: usamos TaggableModel como tipo para los métodos en el trait, y lo reutilizamos para Book y Post, que heredan de él.
Laravel está feliz porque un modelo debe heredar de Model, y nosotros estamos felices porque controlamos qué tipos se permiten:
class Post extends TaggableModel
Si no usamos abstract, alguien podría pasarle cualquier cosa: un entero, un string, un objeto que no tenga nada que ver, incluso… la novia. Y no queremos eso. Queremos que el tipo sea lo más estricto posible.
¿Qué pasa si intentamos instanciar una clase abstracta?
Supón que en vez de pasarle a save() un Post, le pasamos directamente una instancia de TaggableModel. Laravel va a tratar de instanciarla. ¿Y qué pasa? Error: no se puede instanciar una clase abstracta.
Eso es justo lo que queremos. Queremos que quede claro para cualquier desarrollador que esa clase no debe ser instanciada directamente, porque no representa una entidad real por sí sola. Solo existe para ser heredada.
De hecho, si le quitamos el abstract a TaggableModel, Laravel intentará instanciarla cuando se inyecte en el componente, y ahí obtendremos un error distinto, como que no existe la tabla asociada, porque no la hemos creado (ni deberíamos).
Conclusión
Con esto puedes ver más claramente para qué demonios sirve una clase abstracta, y cómo usarla en un caso real con Laravel y Livewire. También te queda claro qué pasa si se intenta instanciar una clase abstracta, y cómo esto ayuda a tener un código más modular, claro y mantenible.
Es como los clásicos ejemplos de orientación a objetos con Animal, Perro, Gato, etc. En nuestro caso, TaggableModel es Animal, y Post y Book son Perro y Gato.
Acepto recibir anuncios de interes sobre este Blog.
Hablamos sobre las clases abstractas que son y un ejemplo práctico de Clases Tradicionales VS Clases Abstractas.
- Andrés Cruz