Curso de Desarrollo Web con PHP y Yii2

Condicional Blade según si el usuario tiene el libro

Curso de Desarrollo Web con PHP y Yii2

Contenido del curso

Modelos, vistas y controladores

Enlaces, navegación y tablas intermedias

Condicional Blade según si el usuario tiene el libro

Resumen

Cuando construyes una app de catálogo de libros en Laravel, llega un momento donde necesitas que la vista cambie según el estado del usuario. Si ya tiene el libro, no tiene sentido mostrarle el botón de "agregar". Aquí aprenderás a crear un método booleano en el modelo User y usarlo con un condicional Blade para mostrar acciones distintas, como calificar el libro o eliminarlo de la colección.

¿Cómo decidir qué mostrar en la vista cuando el usuario ya tiene el libro?

La idea es simple: si el usuario ya tiene registrado el libro, la interfaz cambia. En lugar del botón para añadirlo, aparecen dos opciones nuevas, ya no tengo este libro y calificar del uno al cinco.

En la vista detail de Blade abres un condicional que pregunta directamente al objeto autenticado:

php @if($app->user->identity->hasBook($bookID)) {{-- Ya lo tengo: acciones de calificar y eliminar --}} @else {{-- No lo tengo: botón para agregarlo --}} @endif

Este patrón mantiene la vista limpia y delega la decisión al modelo, que es donde debe vivir la lógica de negocio [00:34].

¿Qué hace hasBook en el modelo User? Es un método que recibe el bookID y devuelve true o false según si existe un registro en la tabla intermedia que relacione a ese usuario con ese libro.

¿Cómo se programa el método hasBook en el modelo User?

El método se declara como public function dentro de la clase User y retorna un valor booleano. Aunque PHP no obliga a tipar el retorno, declararlo como bool ayuda a la legibilidad y al autocompletado del editor [01:20].

php public function hasBook($bookID): bool { $uv = UserBook::find()->where([ 'user_id' => $this->id, 'book_id' => $bookID ])->bringAll();

if (empty(\$uv)) { return false; } return true;

}

Fíjate en \$this->id. Como el método se invoca sobre una instancia de usuario ya autenticado, el id está disponible sin necesidad de pasarlo como parámetro. El bookID, en cambio, llega como argumento porque depende de la página donde estés.

¿Por qué usar el modelo UserBook como tabla intermedia?

UserBook representa la relación entre un usuario y un libro. Cada registro indica que cierto usuario posee cierto libro. La consulta usa where con un arreglo de condiciones donde user_id coincide con el id del usuario instanciado y book_id coincide con el libro consultado.

El método bringAll devuelve todos los registros que cumplan ambas condiciones. En un escenario de producción tendrías una regla de unicidad para que un usuario no pueda registrar el mismo libro dos veces, pero para fines educativos se trabaja con bringAll y se valida con empty [02:40].

¿Cuál es la diferencia entre traer uno o todos los registros? Si pides one, recibes una instancia única o null. Si pides bringAll, recibes un arreglo, vacío o con elementos. Para un booleano, empty sobre el arreglo basta.

¿Cómo se ven las acciones cuando el usuario ya tiene el libro?

Dentro del bloque del if se renderiza la frase ya lo tengo junto a dos elementos nuevos. El primero es un enlace mencionado como ya no lo tengo, que sería la acción inversa de agregar el libro y se deja planteada sin desarrollar.

El segundo es un formulario para calificar del uno al cinco. Este formulario no se queda en la misma vista, envía un POST a un controlador distinto:

  • El destino es BookController con la acción score.
  • El payload incluye únicamente la calificación seleccionada entre uno y cinco.
  • La ruta se resuelve como book/score.

Esto implica que en la siguiente iteración necesitas tres piezas nuevas, una tabla para guardar las calificaciones, un modelo que represente esa tabla y un controlador que reciba el POST y lo persista [05:10].

¿Qué ganas al separar la lógica entre modelo, vista y controlador?

La vista solo pregunta sí o no. El modelo User resuelve esa pregunta consultando UserBook. El controlador, cuando llegue, se ocupará solo de recibir la calificación y guardarla. Cada capa hace una sola cosa.

Esta separación se nota cuando pruebas en el navegador. Al refrescar la vista de un libro que ya tienes, como El perfume: History of a Murder, ves el bloque ya lo tengo. Si entras a un libro que no está asociado a tu usuario, vuelve a aparecer el botón para agregarlo.

El truco final fue agregar el atributo useClass en la vista para asegurar que Blade resolviera correctamente la instancia del usuario al invocar hasBook. Sin eso, el condicional no respondía como se esperaba [04:25].

¿Cómo estás manejando tú las relaciones muchos a muchos en tus proyectos Laravel? Cuéntalo en los comentarios.