Añadan ->label('Puntuación') para cambiar la etiqueta de Score a ...
->radioList()->label()
Construir un sistema de calificación de libros desde cero implica conectar varias piezas: una tabla nueva, un modelo con validaciones, un controlador con acciones separadas y una vista con formularios activos. Todo esto se resuelve en pocos pasos gracias a la estructura que ofrece el framework Yii2, donde cada capa tiene responsabilidades claras y el flujo de datos sigue un patrón predecible.
¿Cómo crear el modelo BookScore con validaciones?
El punto de partida es la tabla book_scores, que almacena tres datos esenciales: el user ID, el book ID y una calificación del uno al cinco [0:06]. La tabla incluye campos created_at y modified_at, además de una restricción de unicidad que impide que un mismo usuario califique dos veces el mismo libro.
El modelo BookScore se crea tomando como base otro modelo existente, en este caso Author, y se adapta con el nombre de tabla y la llave primaria correspondiente [0:42]. Lo más relevante es la función rules(), que define las reglas de validación:
score se marca como required para que no llegue vacío.bookId se configura como safe para que el modelo no lo filtre al recibir datos del formulario [7:30].php public function rules() { return [ ['score', 'required'], ['score', 'integer'], ['bookId', 'safe'], ]; }
Sin estas reglas, el método save() falla silenciosamente. Este es un error común: cuando no hay validaciones definidas, el modelo no sabe qué atributos aceptar del usuario y simplemente no guarda nada [6:21].
¿Cómo separar la lógica entre acciones del controlador?
Una decisión de diseño importante es que la acción que muestra el formulario no es la misma que lo procesa. En actionDetail se crea un objeto BookScore vacío, un placeholder, y se le asigna el bookId del libro actual antes de enviarlo a la vista [1:06]. Este sobre vacío viaja a la vista para que el formulario tenga un modelo al cual bindear sus campos.
La acción actionScore recibe los datos por POST, crea un nuevo BookScore, lo carga con load(), valida y guarda [3:22]. Si todo sale bien, redirige al detalle del libro con un flash message de éxito.
php public function actionScore() { $bs = new BookScore(); $bs->userId = Yii::$app->user->identity->id; if ($bs->load(Yii::$app->request->post()) && $bs->validate()) { $bs->save(); Yii::$app->session->setFlash('success', 'Gracias por tu calificación'); return $this->redirect(['book/detail', 'id' => $bs->bookId]); } }
Un detalle clave es que el user ID se asigna desde el servidor con Yii::$app->user->identity->id, no desde el formulario [8:15]. Esto se hace antes de validar por si alguna regla dependiera de ese valor.
¿Cómo apuntar el formulario a una acción diferente?
El ActiveForm en la vista necesita saber a dónde enviar los datos. Esto se configura con el parámetro action, que recibe un arreglo con la ruta del controlador [7:10]:
php $form = ActiveForm::begin([ 'id' => 'new-score', 'action' => ['book/score'], ]);
¿Cómo construir el formulario con dropdown y campos ocultos?
En la vista se utiliza un dropdown list para que el usuario seleccione su calificación de una a cinco estrellas [2:42]. La estructura del dropdown usa llaves numéricas como valores que se almacenan en la base de datos y las estrellas como labels visibles.
php echo $form->field($bookScore, 'score')->dropdownList([ 1 => '⭐', 2 => '⭐⭐', 3 => '⭐⭐⭐', 4 => '⭐⭐⭐⭐', 5 => '⭐⭐⭐⭐⭐' ]);
El bookId se envía como un hidden input para que no sea visible al usuario, y su label se oculta con ->label(false) [5:20]. Un detalle importante en las vistas de Yii2: algunos precompiladores como Twig son más estrictos y no permiten la coma final en el último elemento de un arreglo, algo que PHP sí acepta [3:52].
¿Qué tareas complementan este ejercicio?
Dos retos quedan planteados para reforzar lo aprendido:
onChange de HTML para hacer el submit automático del formulario al seleccionar una opción [5:46].Con la calificación guardada correctamente en la base de datos, se confirma que el flujo completo funciona: formulario, envío por POST a otra acción, validación del modelo, asignación del usuario desde el servidor y redirección con mensaje de éxito. ¿Has implementado sistemas de calificación similares? Comparte tu experiencia y las variantes que hayas probado.
Construir un sistema de calificación de libros desde cero implica conectar varias piezas: una tabla nueva, un modelo con validaciones, un controlador con acciones separadas y una vista con formularios activos. Todo esto se resuelve en pocos pasos gracias a la estructura que ofrece el framework Yii2, donde cada capa tiene responsabilidades claras y el flujo de datos sigue un patrón predecible.
El punto de partida es la tabla book_scores, que almacena tres datos esenciales: el user ID, el book ID y una calificación del uno al cinco [0:06]. La tabla incluye campos created_at y modified_at, además de una restricción de unicidad que impide que un mismo usuario califique dos veces el mismo libro.
El modelo BookScore se crea tomando como base otro modelo existente, en este caso Author, y se adapta con el nombre de tabla y la llave primaria correspondiente [0:42]. Lo más relevante es la función rules(), que define las reglas de validación:
score se marca como required para que no llegue vacío.bookId se configura como safe para que el modelo no lo filtre al recibir datos del formulario [7:30].php public function rules() { return [ ['score', 'required'], ['score', 'integer'], ['bookId', 'safe'], ]; }
Sin estas reglas, el método save() falla silenciosamente. Este es un error común: cuando no hay validaciones definidas, el modelo no sabe qué atributos aceptar del usuario y simplemente no guarda nada [6:21].
Una decisión de diseño importante es que la acción que muestra el formulario no es la misma que lo procesa. En actionDetail se crea un objeto BookScore vacío, un placeholder, y se le asigna el bookId del libro actual antes de enviarlo a la vista [1:06]. Este sobre vacío viaja a la vista para que el formulario tenga un modelo al cual bindear sus campos.
La acción actionScore recibe los datos por POST, crea un nuevo BookScore, lo carga con load(), valida y guarda [3:22]. Si todo sale bien, redirige al detalle del libro con un flash message de éxito.
php public function actionScore() { $bs = new BookScore(); $bs->userId = Yii::$app->user->identity->id; if ($bs->load(Yii::$app->request->post()) && $bs->validate()) { $bs->save(); Yii::$app->session->setFlash('success', 'Gracias por tu calificación'); return $this->redirect(['book/detail', 'id' => $bs->bookId]); } }
Un detalle clave es que el user ID se asigna desde el servidor con Yii::$app->user->identity->id, no desde el formulario [8:15]. Esto se hace antes de validar por si alguna regla dependiera de ese valor.
El ActiveForm en la vista necesita saber a dónde enviar los datos. Esto se configura con el parámetro action, que recibe un arreglo con la ruta del controlador [7:10]:
php $form = ActiveForm::begin([ 'id' => 'new-score', 'action' => ['book/score'], ]);
En la vista se utiliza un dropdown list para que el usuario seleccione su calificación de una a cinco estrellas [2:42]. La estructura del dropdown usa llaves numéricas como valores que se almacenan en la base de datos y las estrellas como labels visibles.
php echo $form->field($bookScore, 'score')->dropdownList([ 1 => '⭐', 2 => '⭐⭐', 3 => '⭐⭐⭐', 4 => '⭐⭐⭐⭐', 5 => '⭐⭐⭐⭐⭐' ]);
El bookId se envía como un hidden input para que no sea visible al usuario, y su label se oculta con ->label(false) [5:20]. Un detalle importante en las vistas de Yii2: algunos precompiladores como Twig son más estrictos y no permiten la coma final en el último elemento de un arreglo, algo que PHP sí acepta [3:52].
Dos retos quedan planteados para reforzar lo aprendido:
onChange de HTML para hacer el submit automático del formulario al seleccionar una opción [5:46].Con la calificación guardada correctamente en la base de datos, se confirma que el flujo completo funciona: formulario, envío por POST a otra acción, validación del modelo, asignación del usuario desde el servidor y redirección con mensaje de éxito. ¿Has implementado sistemas de calificación similares? Comparte tu experiencia y las variantes que hayas probado.
Añadan ->label('Puntuación') para cambiar la etiqueta de Score a ...
->radioList()->label()
También se puede usar Bootsrap para crear los forms, en lugar de widgets.
{use class="yii\bootstrap\ActiveForm"}
Pero parece que Smarty no es compatible, o ¿es que estoy haciendo algo mal?
Aqui la tarea, debo decir que la documentacion de Yii no me resulta clara, fue realmente complicado encontrar donde insertar el onchange. El radio button si fue relativamente facil.

Al punto de estar por concluir el curso, siento desde mi opinión muy personal que la peor decisión fue instalar smarty, ya que dependemos de una extensión, aprender su sintaxis, para muchos que utilizan vs code para programar ocupamos de extensiones para que tenga soporte a dicha extensión de archivo en dicho editor de código, además es un problema identificar cómo usar smarty, etc. Por mucho no es la mejor práctica, me hace ver más sucio el código con esas llaves poco explicitas. Un claro ejemplo el solo hecho de como se importa las clases.
José Jesús Palacios
Añadan ->label('Puntuación') para cambiar la etiqueta de Score a ...
->radioList()->label()
José Jesús Palacios
También se puede usar Bootsrap para crear los forms, en lugar de widgets.
{use class="yii\bootstrap\ActiveForm"}
Pero parece que Smarty no es compatible, o ¿es que estoy haciendo algo mal?
Alberto Alcocer
ProfesorHola José! Mira, nunca lo había intentado así, pero déjame averiguo la compatibilidad con Smarty. Te puedo decir que algunas cosas que uso no son compatibles con Smarty y esas vistas las tengo en PHP directo. Al final, el 97 de mis vistas son Smarty (practicidad y velocidad de creación) y el restante 3% son PHP (por compatibilidad con otras cosas y esa puede ser una de esas).
Kenneth Angulo L
Aqui la tarea, debo decir que la documentacion de Yii no me resulta clara, fue realmente complicado encontrar donde insertar el onchange. El radio button si fue relativamente facil.
Jorge Luis Chan Chan
Al punto de estar por concluir el curso, siento desde mi opinión muy personal que la peor decisión fue instalar smarty, ya que dependemos de una extensión, aprender su sintaxis, para muchos que utilizan vs code para programar ocupamos de extensiones para que tenga soporte a dicha extensión de archivo en dicho editor de código, además es un problema identificar cómo usar smarty, etc. Por mucho no es la mejor práctica, me hace ver más sucio el código con esas llaves poco explicitas. Un claro ejemplo el solo hecho de como se importa las clases.
Alberto Alcocer
ProfesorTener Smarty tiene varias ventajas pero la principal es la facilidad para integrar html diseñado por no-desarrolladores en los views.
Una de las más grandes ventajas de todo esto es que, siendo exactamente la misma lógica detrás del render, puedes no usar Smarty sin ningún problema :)