Cuando necesitas obtener datos que no están directamente conectados entre sí, las relaciones intermedias se convierten en tu mejor herramienta. En Laravel, la función viaTable permite cruzar información entre tres tablas de forma limpia, usando apenas dos líneas de código, y combinada con la sobrecarga de parámetros en los getters, abre posibilidades muy potentes para reutilizar funciones con distintos comportamientos.
¿Cómo se asocia una tabla con otra usando una tabla intermedia?
El escenario es claro: un autor no tiene relación directa con los votos (book scores), pero sí la tiene con los libros, y los libros sí se relacionan directamente con los votos. El objetivo es calcular el promedio de puntuación de todas las obras de un autor [01:06].
Para lograrlo, se trabaja desde la vista author_detail, donde se coloca un placeholder inicial que muestra un valor fijo como 1.2, y luego se reemplaza con la lógica real.
¿Qué hace la función getVotes con viaTable?
Dentro del modelo de autor se crea la función getVotes [02:18]. La relación se define así:
php
public function getVotes() {
return $this->hasMany(BookScore::class)
->viaTable('books', ['author_id' => 'author_id'])
->where('book_id', '=', 'book_id')
->all();
}
hasMany indica que un autor tiene muchos votos.
viaTable('books', ...) especifica que la tabla intermedia es books, y conecta el author_id del autor con el author_id del libro.
El segundo enlace conecta book_id del libro con book_id del book_score.
Esto equivale a una consulta SQL con LEFT JOIN que cruza las tres tablas [03:50]. Se verificó directamente en la base de datos que el autor Alessandro Baricco (id 88) tenía exactamente dos votos con puntuaciones que suman nueve, dando un promedio de 4.5.
¿Cómo se calcula el promedio de puntuación?
La función getScore itera sobre los votos obtenidos [05:00]:
php
public function getScore() {
$i = 0;
$sum = 0;
foreach ($this->votes as $vote) {
$i++;
$sum += $vote->score;
}
if ($i == 0) return 'sin votos';
return sprintf('%.2f con %d votos', $sum / $i, $i);
}
Se acumulan los puntajes y se cuenta el número de votos.
Si no hay votos, se retorna "sin votos".
Si los hay, se muestra el promedio formateado con dos decimales junto al total de votos.
El resultado en pantalla: "El promedio de todas sus obras es de 4.5 con dos votos" [05:52].
¿Qué es la sobrecarga de parámetros en los getters?
Aquí viene lo que realmente marca la diferencia. La función getVotes se modifica para aceptar un parámetro opcional book_id con valor null por defecto [06:10]:
php
public function getVotes($book_id = null) {
$query = $this->hasMany(BookScore::class)
->viaTable('books', ['author_id' => 'author_id'])
->where('book_id', '=', 'book_id');
Sin parámetro: trae todos los votos del autor, igual que antes.
Con book_id: filtra para traer solo los votos de un libro específico.
Esto permite invocar votes como atributo (sin parámetros) o como función con un book_id concreto. En la misma función conviven dos funcionalidades lógicamente unidas sin agregar complejidad innecesaria [07:02].
¿Cómo se aplica esto en la vista de detalle del libro?
En book_detail, se obtiene el promedio de un libro específico a través del autor [08:30]:
Aunque esta ruta es ineficiente para producción (porque se podría calcular directamente desde el libro), resulta perfecta para entender cómo fluye la información entre tablas. Al probarlo, los resultados fueron correctos: La esposa joven muestra un voto de cuatro, Seda aparece sin votos y Océano Amar con cinco [09:25].
Al agregar un nuevo voto de cinco a Seda y regresar al perfil de Baricco, el promedio se actualiza automáticamente a 4.75 con cuatro votos [10:00].
La combinación de viaTable para relaciones de tres tablas con la sobrecarga de parámetros en los getters ofrece código mantenible, ordenado y con una capacidad de crecimiento notable. ¿Has implementado relaciones intermedias en tus proyectos? Comparte tu experiencia.