Realizaremos una práctica de acceso a base de datos en Laravel, con varias operaciones a partir de modelos de Eloquent en Laravel 5.
Vamos a hacer una pausa para la práctica en la sección de bases de datos del Manual de Laravel 5. Hemos visto de manera detallada la creación y modificación de tablas mediante las migraciones y además hemos conseguido insertar datos de prueba en las tablas gracias a los seeders, así que nos resta jugar un poco con todo ello.
Nuestro ejercicio nos permitirá poner en práctica los conocimientos que ya poseemos hasta el momento y explorar algún área nueva, sobre todo en lo que respecta a los modelos. El objetivo es realizar un sencillo sistema para listar datos que tenemos en una tabla y para insertar datos nuevos en dicha tabla. Resultará bastante sencillo de entender si digo que nos quedaremos a la mitad del camino de lo que se conoce como CRUD. Osea, veremos solamente el "CR" Create y Read, dejando para más adelante el "UD" Update y Delete.
Aprovecharemos que en los artículos anteriores sobre los seeders poblamos la tabla de libros para usarla en esta práctica. Partimos pues sobre una tabla, ya rellena con información de prueba, llamada "books". Si no la tienes, por favor, sigue los pasos el artículo sobre los Seeders.
RutasComenzaremos observando las rutas nuevas que hemos creado para este ejemplo.
route::get('libros', 'BookController@index');
route::get('libros/{id}', 'BookController@show')->where(['id' => '[0-9]+']);
route::get('libros/crear', 'BookController@create');
route::post('libros/crear', 'BookController@store’);
Por orden de aparición, las rutas serán usadas para las siguientes páginas:
- libros: para ver un listado de todos los libros
- libros/{id}: para ver un detalle de un libro en particular
- libros/crear (GET): para mostrar el formulario con el que crearemos libros
- libros/crear (POST): para recibir el formulario con los datos de un nuevo libro y si valida correctamente, insertarlo
Con la lectura del código de las rutas debería quedar claro que el controlador encargado de ejecutar todas las solicitudes se llama BookController. Los métodos que tendremos que definir, con el código de cada operación, se llaman index(), show(), create(), store().
Controlador BookController
Vamos a contar con un único controlador que realizará todas las acciones que serán necesarias para resolver nuestros objetivos.
El controlador, como sabes, se puede generar con un comando de artisan: “make:controller”. Es parte te la dejamos para ti, porque ya lo vimos en el capítulo de introducción a los controladores, pero sin embargo quiero que prestes atención a los nombres de las acciones en el controlador por defecto que se ha creado. Son los mismos que hemos proyectado en las rutas anteriores, así que ya tenemos mucho del trabajo adelantado.
Nuestro código para gestionar esas acciones se puede ver a continuación:
El listado de todos los libros:
La primera acción debe mostrar todos los libros de la tabla books.
public function index()
{
$libros = Book::all();
return view('libro.todos', ['libros' => $libros->toArray()]);
}
Hacemos una búsqueda de los libros a través del modelo Book. Luego llamamos a una vista que se encargará de mostrar el listado de todos los libros.
Cuando solicitamos información a un modelo como en este caso, Book::all(), lo que nos devuelve el método all() es un objeto colección “collection". No hemos llegado a explicar qué hay de útil detrás de las colecciones y por ahora vamos a dejarlo aparte, pero comenzaremos a usarlas. De momento lo único que hacemos con la colección es invocar el método toArray(), que nos devuelve los datos que contiene, pero en formato de array.
Ese array generado a partir de la colección se lo pasamos a la vista para que sea capaz de pintar los libros que se han encontrado en la consulta a la tabla.
Por cierto, en el controlador no se te debe olvidar hacer el “use” de la clase donde está el modelo Book.
use App\Book;
El detalle de un libro:
La segunda acción que vamos a observar es la que se invoca cuando se solicita el detalle de un libro, método show().
public function show($id)
{
$libro = Book::find($id);
if (!is_null($libro))
return view('libro.mostrar', ['libro' => $libro->toArray()]);
else
return response('no encontrado', 404);
}
Este método recibe como parámetro el id del libro que se desea visualizar. Ese id se lo pasamos a la ruta y gracias al where() del código de la ruta ya está comprobado que sea un número.
route::get('libros/{id}', 'BookController@show')->where(['id' => '[0-9]+']);
Tal como está construida, solo se activará cuando el parámetro id sea un número.
En esta acción del controlador solicitamos información al modelo de los libros (Book) mediante el método find(), que sirve para buscar un elemento a partir de su clave primaria. Si encontró alguna cosa me devolverá la colección con aquello que se haya encontrado. Si no encuentra nada me devolverá null. En función del valor de retorno, se realizan cosas distintas.
- Devuelvo una vista para mostrar el libro, en caso que no sea nulo, enviándole a la vista los datos del libro en un array
- O bien, si no se encontró nada, devuelvo un objeto response con un error 404 de página no encontrada y un mensaje
Formulario de creación de un libro:
Ahora veamos la acción que comienza el proceso de creación de un libro, que se encarga de mostrar el formulario para introducir los datos de un libro.
public function create()
{
return view('libro.formlibro');
}
Este es el más sencillo de todos los métodos del controller, porque solo tiene la llamada a una vista, que tendrá el HTML del formulario de alta de un libro.
Creación del libro, a partir de los datos del formulario:
Para acabar el controlador nos queda ver el método store, que es el que realmente se encarga de crear el nuevo libro.
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|min:5',
'author' => 'required|min:8',
'isbn' => 'required'
]);
Book::create($request->all());
return redirect('/libros');
}
El método store() recibe la Request, dependencia que se inyecta en la llamada al método de la acción, necesario porque vamos a tener que recuperar los datos de la solicitud POST (los datos enviados en el formulario).
Dentro del método realizamos una validación de los datos recibidos por formulario y si todo ha ido bien, es cuando creamos el recurso, gracias al método create() del modelo Book, enviándole los datos de la solicitud, todos convertidos a array mediante el método all().
Una vez creado el libro en la tabla, se redirige a la página “home” de libros, que se encarga de mostrar el listado de los libros que hay en el sistema.
Esta es la parte más compleja de la práctica, porque entran en juego las validaciones, sin embargo esperamos que puedas recordar los mecanismos que ya fueron relatados en el artículo de validaciones en Laravel.
Modificación en el modelo Book
Tal como está nuestro código y el uso que hacemos del modelo con el método Book::create(), vamos avisando que Laravel nos arrojará una excepción cuando se intente crear el nuevo libro.
MassAssignmentException in Model.php line 424:
No es la primera vez que nos aparece un error similar, así que vamos a intentar concretar ya de una manera sencilla qué es lo que está pasando.
El método Book::create() nos permite crear un elemento e insertarlo en la base de datos. Para ello recibe un array y usa todos los datos que le estamos pasando. Esos datos en nuestro ejemplo vienen directamente de la solicitud y los volcamos tal cual para poder crear ese nuevo libro:
Book::create($request->all());
Imagina que viene a través de la solicitud un dato que realmente no estábamos esperando, por ejemplo un dato de control que usas internamente para saber el estado de un libro y no deseas que sea parte de la entrada del usuario. ¿Cómo podría ocurrir que te manden otros datos, además de los que deseas? pues simplemente un usuario con conocimientos mínimos podría crear otros campos en el formulario, editando la página con las herramientas de desarrolladores y así se enviarían también en la solicitud por el método POST al enviar ese formulario normalmente.
Así que Laravel, para evitar estas cosas, al usar el método create() que es potencialmente peligroso por una posible inyección de datos no deseados, obliga a que tengas declarados en el modelo aquellos campos que se van a poder insertar al hacer un create().
Esta configuración se realiza a través de una propiedad que tendremos que crear dentro del modelo, llamada $fillable. El código te quedará así:
class Book extends Model
{
protected $fillable = ['name', 'author', 'isbn'];
}
Vistas de la aplicación
Ya solo nos queda ver las vistas que hemos creado para esta práctica. Todas las hemos situado en la carpeta resources/views/libro.
Vista libro.todos: (archivo todos.php)
Esta vista hace el recorrido a array de libros que se le ha pasado y los va mostrando uno a uno.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Todos los libros</title>
</head>
<body>
<?php foreach ($libros as $libro): ?>
<p>
<?=$libro['name']?>, por <?=$libro['author']?>
<br>
ISBN: <?=$libro['isbn']?>
</p>
<?php endforeach ?>
</body>
</html>
En este caso usamos un “foreach” clásico de PHP (con el código sugerido como alternativa a las vistas), aunque más tarde al aprender Blade veremos una forma más clara de escribir este mismo código.
Vista libro.mostrar: (archivo mostrar.php) Esta segunda vista muestra el detalle de un libro.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Mostrar un libro</title>
</head>
<body>
<h1>
<?=$libro['name']?>
</h1>
<p>
Por <?=$libro['author']?>
<br>
ISBN: <?=$libro['isbn']?>
</p>
</body>
</html>
vista libro.formlibro: archivo formlibro.blade.php Esta tercera vista, y última, es la que usaremos para mostrar el formulario de un libro, que de momento estamos usando para la creación de libros. Hemos elegido como extensión .blade.php porque vamos a usar un pedazo de código con sintaxis blade para mostrar los posibles errores de validación de los datos del formulario.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Crear un libro</title>
</head>
<body>
<h1>Crear un libro</h1>
@if(count($errors) > 0)
<div class="errors">
<ul>
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="/libros/crear" method="post">
Nombre: <input type="text" name="name" value="{{old('name')}}">
<br>
Autor: <input type="text" name="author" value="{{old('author')}}">
<br>
ISBN: <input type="text" name="isbn" value="{{old('isbn')}}">
<br>
<input type="submit" value="Crear">
</form>
</body>
</html>
Esta vista tiene varios detalles importantes, primero el recorrido a los errores de validación y luego la “memoria” en los campos de formulario para que sean capaces de recordar su estado cuando fue enviado el formulario la última vez. Todo esto no lo vamos a explicar porque se vio con suficiente detalle en el capítulo de validaciones en Laravel.
Con esto termina esta práctica, ya solo faltaría poner en marcha el ejercicio accediendo a las rutas de la aplicación definidas.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...