Sei sulla pagina 1di 101

FUNDAMENTOS DE LARAVEL

Versión 5.n

Talleres Temáticos - Parte 1

Por
Carlos Cuesta Iglesias

Departamento de Sistemas e Informática


Universidad de Caldas
Laravel - Configuración del Entorno de trabajo

Documentación sobre el entorno de servidor utilizado:


● https://laragon.org/
● https://tecnonucleous.com/2017/01/31/1558/
● https://styde.net/laragon-un-entorno-de-desarrollo-para-laravel-en-windows/

Instalación

Se utilizará Laragon para Windows, terminada la instalación se recomienda marcar las


casillas “Ver el archivo README” y “Ejecutar Laragon”:

MySQL login*:
user: root
password:cd..

*The default username is 'root' and empty password


----------------------------------------------------
Document Root:
C:\laragon\www
----------------------------------------------------
Hotkey to open Terminal globally:
CTRL+ALT+T

For more information, please visit: http://laragon.org

Thank you for using Laragon.


leokhoa@gmail.com

Importante​:

Las siguientes rutas deberían quedar referenciadas en las variables de entorno a nivel de
sistema​:

● X:\laragon\bin\composer

● X:\laragon\bin\php\php-7.2.11-Win32-VC15-x64

● Y:\Users\Cuesta\AppData\Roaming\Composer\vendor\bin

Siendo ​X​ la unidad donde se instaló Laragón y ​Y​ la unidad donde el sistema
operativo referencia las aplicaciones.

Si es usuario de Mac, se recomienda ​instalar Laravel Valet​, pero si tiene ya instalado MAMP
o XAMPP puede instalar sobre dichos servidores. Tenga en cuenta que también se requiere
Composer​, PHP y un gestor de base de datos.
Se recomienda mirar:

● https://brew.sh/index_es​.

● https://laravel.com/docs/5.8/valet/introduction​ (ver estas instrucciones)

● Como manejador de base de dato se recomienda Sequel Pro.

Luego de instalar:
● Mostrar el readme
○ Document Root c:\laragon\www
○ Abrir terminal: ​Ctrl+Alt+T
○ Si se requiere MySQL, el usuario es root y el password (nada)

El botón ​Iniciar todo​, carga los servicios de Apache (p80) y MySQL (p.3306)
Si se utiliza Wampp o XAMPP, apagar los puertos para evitar conflictos.

El segundo botón, ​Web​, lleva al index de Laragon.

El tercer botón permite seleccionar el gestor de ​bases de datos​.

Elbotón ​Root ​lleva a la carpeta donde se almacenan las aplicaciones.

El botón ​Terminal​, lleva a la terminal de desarrollo.

● Se muestra que usa Git para el control de versiones.

● Importante​. Verificar la existencia del manejador de paquetes:

○ Digitar ​composer​ ​(Intro)


● Se pueden crear aplicaciones desde la ventana de Laragon pero también se puede
hacer utilizando el comando​ laravel​. Si dicho comando no está disponible, se
recomienda instalarlo en ámbito global, utilizando ​composer​. El comando es:

composer global require laravel/installer​ (Intro)

Esperar...es posible que se demore un momento en empezar.

Probar nuevamente el comando ​laravel ​para verificar que funcione.

Crear una nueva aplicación de Laravel

En la ​interfaz de línea de comandos ​Artisan​, verificamos que se está dentro de la carpeta


www ​y ejecutamos:

laravel new nombreAplicacion


La primera vez esta acción puede demorar un tiempo considerable, dado que deben
descargarse dependencias que aún no han sido instaladas.

Terminada la descargar, use el comando ​ls​ para comprobar la creación ​www ​de la nueva
aplicación.
En conclusión, como en wamp, las aplicaciones se crean en la carpeta ​www​.

Importante​: de ahora en adelante, cada vez que se ejecute un comando de ​Artisan​, se


hará en la carpeta del proyecto.

Recuerde que para navegar entre carpetas se utiiza el comando ​cd​.

Acceder a una aplicación

● Para acceder a la aplicación desde el navegador, basta simplemente con ingresar


nombreAplicacion.test​, Si no carga, recuerde que cada vez que cree o haga
cambios en una aplicación, debe recargar Apache. Esto se hace fácilmente desde la
interfaz de Laragon, dando clic en ​recargar​.

Importante​:
● En caso de presentarse errores de este tipo:

Warning​: require(C:\laragon\www\app1\public/../vendor/autoload.php): failed


to open stream: No such file or directory in
C:\laragon\www\app1\public\index.php​ on line ​24

Fatal error​: require(): Failed opening required


'D:\laragon\www\app1\public/../vendor/autoload.php'
(include_path='.;D:/laragon/etc/php/pear') in
C:\laragon\www\app1\public\index.php​ on line ​24

Será necesario, estando en la carpeta del proyecto, ejecutar el comando:

composer dumpautoload

● En esta parte, tenga cuidado con proporcional los permisos necesarios para
evitar la denegación de acceso del sistema operativo al servidor web.

● Cada vez que ejecute​ php artisan​ debe hacerse dentro de la carpeta del
proyecto.
Vista General de Estructura de Carpetas

Carpeta Descripción

app Contiene el código fuente de la aplicación. La mayoría de las clases se


crearán en esta carpeta.

bootstrap Contiene el archivo ​app.php​ que inicia el Framework y una carpeta


para el manejo de la memoria caché con el fin de optimizar el
rendimiento de la aplicación.

config Contiene la configuración de la aplicación:


● Nombre de la aplicación
● Definir si se está en producción o en desarrollo.
● La visualización o no de errores.
● ...
Es preferible no hacer cambios aquí sino en el archivo ​.env

database Guarda las factorías, las migraciones y los semilleros.

public Único directorio accesible públicamente. Contiene el archivo


index.php​ que es el punto de entrada a todas las peticiones que se
hagan a la aplicación.

resources Contiene las vistas de la aplicación y los archivos no compilados de


CSS y Javascript.
También contiene los archivos de idiomas.

routes Almacena 4 archivos de rutas:


● web.php​ - rutas de la aplicación
● api.php - rutas para las API de la aplicación.
● channels.php - canales de transmisión o broadcast que
soportará la aplicación.

storage Almacena las vistas compiladas, sesiones basadas en archivos,


archivos en caché.
Tener en cuenta que la subcarpeta /app/public no es pública.

tests Carpeta de pruebas.


../Feature/ExampleTest.php muestra un ejemplo de pruebas unitarias.

vendor Contiene todas las dependencias de Composer...no se toca.


Rutas

Laravel tiene ​cuatro tipos de archivos de rutas​: api.php, channels.php, console.php y


web.php. Estos archivos nos permiten organizar las ​rutas según el propósito​ que tienen:

● En ​web.php​ se definen las rutas de la aplicación web (aquellas que consultan los
usuarios desde el navegador). Inicialmente nos centraremos en este tipo.

● En ​api.php​ podemos declarar las rutas de nuestra o nuestras APIs.

● En ​console.php​ han de estar nuestros propios comandos artisan.

● Y en ​channels.php​ se permite definir​ ​canales de comunicación en tiempo real​.

Por defecto en el archivo ../routes/​web.php​, están las instrucciones para direccionar a la


vista principal de Laravel:

Route::​get​(​'/'​, ​function​ ​()​ {


​return​ ​view​(​'welcome'​); ​// ver la vista en ../resources/views/
});

Note que el objeto Route proporciona un método get cuyo primer argumento hace referencia
en este caso a la ruta raíz de la aplicación y un segundo argumento, una función anónima o
clousure​ que retorna una vista disponible en la carpeta ../resources/views/

Se podría cambiar la vista anterior por una simple cadena:

Route::​get​(​'/'​, f
​ unction​ ​()​ {
​return​ ​'Hola mundo'​;
});

También se podría indicar cualquier otra ruta de la aplicación. ejemplo

Route::​get​(​'ventas'​, ​function​ ​()​ {


​return​ ​'Esta es la página de ventas'​;
});

También es posible utilizar otros tipos de peticiones como post, put, patch y delete, las 3
últimas no soportadas por los navegadores pero que se pueden emular en Laravel. Más
adelante se verá cómo.
Paso de parámetros a las rutas

Se pueden pasar parámetros desde la barra de direcciones. Estos se pasan entre llaves y
por cada parámetro que se indique, el clousure debe recibe el argumento respectivo.
Ejemplo:

Route::get(​'usuario/​{usuario}​'​, ​function​ ​(​$usuario​)​ {


​return​ ​'Hola '​ . $usuario;
});

Así, se podría incluir algo como esto en la barra de direcciones:

appx.test/usuario/Carlos

Por supuesto se pueden pasar varios parámetros. Observe:

Route::get(​'usuario/{​id​}/{​nombre​}'​, ​function​($id, $nombre)​ {


​return​ ​"Hola $​id​ - $​nombre​"​;
});

Hasta el momento, el uso de la URL fallará si no se pasan parámetros porque estos son
obligatorios. Esto se puede corregir fácilmente mediante el uso de parámetros por defecto:

Route::get(​'usuario/{usuario?}'​, ​function​ ​($usuario=​'visitante anónimo'​)​ {


​return​ ​'Hola '​ . $usuario;
});

Resumen del código de ​app3/routes/web.php

// ruta principal: se accede mediante app3.test


Route::get(​'/'​, ​function​ ​()​ {
​return​ ​'hola mundo'​;
});

// acceso a subcarpeta: app3.test/ventas


Route::get(​'ventas'​, ​function​ ​()​ { ​// subruta
​return​ ​'Esta es la página de ventas'​;
});

// envío de un argumento: app3.test/usuario/Carlos


Route::get(​'usuario/{usuario}'​, ​function​ ​($usuario)​ {
​return​ ​'Hola '​ . $usuario;
});
// envío de varios argumentos: app3.test/usuario2/C01/Carlos
Route::get(​'usuario2/{id}/{nombre}'​, ​function​($id, $nombre)​ {
​return​ ​"Hola $id - $nombre"​;
});

// parámetros por defecto: app3.test/usuario3/


Route::get(​'usuario3/{usuario?}'​, ​function​ ​($usuario=​'visitante anónimo'​)​ {
​return​ ​'Hola '​ . $usuario;
});

Rutas con nombres

Suponga que se tiene la siguiente ​“sección productos​” a la que se accede desde diferentes
enlaces (​ver app4 / app5​):

Route::get(​'​productos​'​, ​function​ ​()​ {


​return​ ​'Sección de productos'​;
});

Los enlaces desde los los que se accede se pueden definir de la siguiente manera:

Route::get(​'/'​, ​function​ ​()​ {


$html = ​'
<a href="/​productos​">Agregar productos</a><br>
<a href="/​productos​">Actualizar productos</a><br>
<a href="/​productos​">Dar de baja productos</a><br>
<a href="/​productos​">Comprar productos</a><br>
'​;
​echo​ $html;
});

Así, al visitar ​appx.test/​ deben verse los cinco enlaces que llevan a la página de
“Productos”.

Para efectos de pensar en nuevas versiones, en donde posiblemente haya que reemplazar
nombres de rutas, esta no es la manera óptima de definir los enlaces. En el siguiente
ejemplo, se tiene una ruta “articulos” a la cual le hemos dado el alias o nombre ​productos​.

Route::get(​'articulos'​, ​function​ ​()​ { ​// la ruta dada


​return​ ​'Sección de productos'​;
})->​name​(​'productos'​)​; ​// nombre dado a la ruta

Ahora es posible utilizar la función para referirnos al nombre de la ruta:


Route::get(​'/'​, ​function​ ​()​ { ​// ruta base de la aplicación
​// usar la función route() para indicar la ruta
$html = ​'
<a href="'​ . ​route​(​'productos'​) . ​'">Agregar productos</a><br>
<a href="'​ . ​route​(​'productos'​) . ​'">Actualizar productos</a><br>
<a href="'​ . ​route​(​'productos'​) . ​'">Dar de baja productos</a><br>
<a href="'​ . ​route​(​'productos'​) . ​'">Comprar productos</a><br>
'​;
​echo​ $html;
});

Pruebe ahora a cambiar la ruta ​‘articulos’​ por cualquier otra ruta (inventario, almacen, etc) y
al pulsar clic sobre los enlaces, notará que la aplicación se direcciona correctamente al
enlace indicado.

Rutas de vistas

Recuerde que las vistas están en ​../resources/views/​ y que por defecto hay una vista en
dicho paquete, llamada ​welcome.blade.php.​ La forma de utilizar dicha vista sería mediante
​ esde ​appX/routes/web.php​ para
la inclusión de una instrucción que llame la función ​view d
que reciba como argumento el nombre de la vista:

Route::get(​'/'​, ​function​ ​()​ { ​// la ruta principal de la aplicación


​return​ ​view​(​'welcome'​);
})->name(​'home'​); ​// nombre dado a la ruta

Como se pude observar, no se requiere indicar la ruta ni la extensión de la vista.

Creación de vistas

Pruebe a agregar una página HTML ../resources/views/inicio​.blade.php​, incluya un breve


contenido y luego modifique el archivo ../routes/​web.php​ para que tenga el siguiente
contenido (​ver app6​):

Route::get(​'/'​, ​function​ ​()​ {


​return​ view(​'inicio'​);
})->name(​'inicio'​);

Al ejecutar la aplicación debería funcionar correctamente.

Paso de datos a las vistas

Supongamos que la vista que acaba de crear (​ver app6​) tiene el siguiente cuerpo en el que
se ha incluido una porción de código PHP para mostrar el dato recibido ​$nombre​:
<body>
​<h1>​Tienda online​</h1>
Bienvenid@ ​<?php​ e​ cho​ $​nombre ?? ​'Visitante anónimo'​ ​?>
</body>
Una de las formas de enviar el dato desde ​web.php​ a la vista sería el siguiente:

Route::get(​'/'​, ​function​ ​()​ {


$nombre = ​'Carlos'​;
​return​ view(​'inicio'​)->​with​(​'nombre'​, $nombre);
})->name(​'inicio'​);

El mismo resultado se obtiene si los datos se envían como un array:

Route::get(​'/'​, ​function​ ​()​ {


$nombre = ​'Carlos'​;
​return​ view(​'inicio'​)->with(​[​'nombre'​ => $nombre]​);
})->name(​'inicio'​);

Una tercera forma de enviar los datos a la vista, es enviar un array asociativo como
segundo argumento del método view:

Route::get(​'/'​, ​function​ ( ​ )​ {
$nombre = ​'Carlos'​;
​return​ ​view​(​'inicio'​, [​ ​'nombre'​ => $nombre]​);
})->name(​'inicio'​);

Una cuarta forma utiliza la función ​compact()​, ejemplo:

Route::get(​'/'​, ​function​ (
​ )​ {
$nombre = ​'Carlos'​;
​return​ view(​'inicio'​, c​ ompact​(​'nombre'​));
})->name(​'inicio'​);
Nota​: por cada variable, ​compact​() busca una variable con ese nombre en la tabla de
símbolos actual y las añade al array de salida de modo que el nombre de la variable se
convierte en la clave y el contenido de la variable se convierte en el valor para esa clave. En
pocas palabras, hace lo contrario que ​extract​().

La forma recomendada cuando se pasan pocos o ningún dato, es la siguiente:

$nombre = ​'Carlos'​;
Route::view(​'/'​, ​'inicio'​, [​'nombre'​ => $nombre]);

De esta manera también puede darle nombre a la ruta:


$nombre = ​'Carlos'​;
Route::view(​'/'​, ​'inicio'​, [​'nombre'​ => $nombre])->​name(​'inicio'​)​;

El motor de plantillas Blade

Supongamos que se tienen en ../routes/​web.php​ las siguientes rutas (​ver app7​):

Route::view(​'/'​, ​'inicio'​)->name(​'inicio'​);
Route::view(​'/acercade'​, ​'acercade'​)->name(​'acercade'​);
Route::view(​'/catalogo'​, ​'catalogo'​)->name(​'catalogo'​);
Route::view(​'/contacto'​, ​'contacto'​)->name(​'contacto'​);

También se tienen en ​../resources/views​ los archivos: ​acercade.blade.php,


contacto.blade.php y​ ​ catalogo.blade.php​, por el momento, cada uno de ellos con una línea
de texto en el body, representativa del archivo al que corresponde.

Por último, tambien se tiene la página ​inicio.blade.php​, con el siguiente contenido:

<nav>
<ul>
<li><a href=​"/"​>Inicio</a></li>
<li><a href=​"/acercade"​>Acerca de...</a></li>
<li><a href=​"/catalogo"​>Catálogo de productos</a></li>
<li><a href=​"/contacto"​>Contacto</a></li>
</ul>
</nav>

<h1>Tienda online</h1>
Bienvenid@ ​<?php​ ​echo​ $nombre ?? ​'Visitante anónimo'​ ​?>

Pruebe que la aplicación y sus enlaces se ejecuta correctamente, pero que para las páginas
acerca de, catálogo y contacto la navegación se pierde y no se quiere repetir el código de
navegación para cada una de estas páginas. Ahora sí observe a solución utilizando blade
(​ver app6​):

1. Cree una copia del archivo ​inicio.blade.php​ y renombre la copia con el nombre
plantilla.blade.php​ o cualquier otro nombre que le recuerde que es una plantilla.

2. Modifique la plantilla para que quede con el siguiente contenido:

<!DOCTYPE html>
<html lang=​"es"​>
<head>
<meta charset=​"UTF-8"​>
...
<title>​@​yield​(​'titulo'​, ​'Tienda'​)​</title>
</head>

<body>

<nav>
<ul>
<li><a href=​"/"​>Inicio</a></li>
<li><a href=​"/acercade"​>Acerca de...</a></li>
<li><a href=​"/catalogo"​>Catálogo de productos</a></li>
<li><a href=​"/contacto"​>Contacto</a></li>
</ul>
</nav>

​ @​yield​(​'contenido'​)

</body>

</html>

Todo lo que hay que saber por ahora de una plantilla Blade es que las directivas
@yield​ permiten indicar secciones como argumentos, en las que luego se colocará
contenido específico. Opcionalmente, estas directivas admiten un segundo
parámetro para indicar un valor por defecto. Veamos un ejemplo de su uso:

3. Reemplace el contenido del archivo ​inicio.blade.php​ por lo siguiente:

@extends(​'plantilla'​)

@section(​'titulo'​)
Tienda Online
@endsection

@section(​'contenido'​)
<h1>Página de inicio</h1>
Bienvenid@ ​{{ $nombre ?? ​'Invitado'​ }}
@endsection

Miremos lo que se resalta del código anterior:

● Especificar el diseño que debe heredarse​. para estos casos use la


directiva ​@extends​.

● Secciones de diseño​. Las vistas que extienden un diseño Blade, pueden


inyectar contenido en las secciones del diseño mediante la directiva
@section ​...​ @endsection​. Recuerde que, los contenidos de estas
secciones se especifican en las plantillas utilizando ​@yield​.

● Visualización de datos​: en cambio de ​echo​, ​se pueden mostrar los datos


pasados ​a las vistas de Blade, envolviendo la variable entre llaves (​{{​...​}}​).
Esta notación no está limitada a variables pasadas a las vistas, también
puede hacer eco de esta manera, de los resultados de cualquier función de
PHP, de hecho, puede poner cualquier código PHP que desee entre doble
llave.

La ventaja de esta notación es que evita ​ataques de tipo XSS​. Ensaye por
ejemplo a enviar un echo de un alert y luego intente enviar el mismo alert
mediante doble llave. La segunda forma no funcionará.

Si esta notación entra en conflictos con marcos de JavaScript que también lo


usen, puede usar la notación @{{...}} para evitar el problema.

● En la carpeta ​../storage/framework/views​ se guardan las vistas ya


compiladas, la compilación que sucede sólo cuando se realizan cambios. La
siguiente instrucción es un ejemplo de lo que se puede encontrar en dicha
carpeta:

<?php​ ​echo​ ​e($nombre ?? ​'Invitado'​);​ ​?> ​// ver función ​e()​ de PHP

● Una alternativa a ​@section ​...​ @endsection​ para cuando el contenido es


corto, es utilizar ​@section(​'​sección​', '​contenido​'​)​.

4. De manera similar a como se muestra en el punto 3, proceda a definir el contenido


de las vistas para las páginas: acerca de, contacto y catalogo.

Estructuras de control con Blade

Para este ejercicio agregue en ../routes/​web.php​, un array de catálogo de productos:

$catalogo​ = [
[​'producto'​ => ​'Harina de trigo'​],
[​'producto'​ => ​'Arroz parvorizado'​],
[​'producto'​ => ​'Chocolate en polvo'​],
[​'producto'​ => ​'Café liofilizado'​],
[​'producto'​ => ​'Queso campesino'​]
];
Route::view(​'/'​, ​'inicio'​, [​'nombre'​ => ​'Carlos'​])->name(​'inicio'​);
Route::view(​'/acercade'​, ​'acercade'​)->name(​'acercade'​);
Route::view(​'/catalogo'​, ​'catalogo'​,
​compact('catalogo')
)->name(​'catalogo'​);
Route::view(​'/contacto'​, ​'contacto'​)->name(​'contacto'​);
También se ha agregado a ​catalogo.blade.php​ el siguiente código para mostrar los
elementos del array en una lista:

@extends(​'plantilla'​)

@section(​'titulo'​)
Catálogo
@endsection

@section(​'contenido'​)
<h1>Catálogo de productos</h1>
<ul>
​<?php
​foreach​ ​($catalogo ​as​ $itemCatalogo) {
​echo​ ​"<li>"​ . $itemCatalogo[​'producto'​] . ​"</li>"​;
}
​?>
</ul>
@endsection

Si ejecuta el código anterior, como es de esperarse, el contenido del array se muestra ahora
en una lista no ordenada.

En el código anterior, el código PHP que lleva a cabo la iteración, se puede reemplazar por
el siguiente código que mezcla Blade y PHP:

​<?php​ ​foreach​ ​($catalogo ​as​ $itemCatalogo): ​?>


<li> {{ $itemCatalogo[​'producto'​] }} </li>
​<?php​ ​endforeach​ ​?>

Mucho mejor si la iteración se escribe completamente en Blade. El código quedaría así:

@​foreach​ ($catalogo ​as​ $itemCatalogo)


<li> {{ $itemCatalogo[​'producto'​] }} </li>
@​endforeach

Comente ahora el contenido del array para ejemplificar el uso condiciones, inicialmente para
saber qué hacer si el array está vacío:

@​if​ ($catalogo)
@​foreach​ ($catalogo ​as​ $itemCatalogo)
<li> {{ $itemCatalogo[​'producto'​] }} </li>
@​endforeach
@​else
<li>No hay productos para mostrar</li>
@​endif

Y finalmente para condicionar el caso en que la variable del array no ha sido definida:

<ul>
@​isset​ ($catalogo)
@​if​ ($catalogo)
@​foreach​ ($catalogo ​as​ $itemCatalogo)
<li> {{ $itemCatalogo[​'producto'​] }} </li>
@​endforeach
@​else
<li>No hay proyectos para mostrar</li>
@​endif
@​else
<li>Catálogo no definido</li>
@endisset
</ul>

Otra alternativa más compacta, es utilizar forelse:

<ul>
@​isset​ ($catalogo)
@​forelse​($catalogo ​as​ $itemCatalogo)
​<li> {{ $itemCatalogo[​'producto'​] }} </li>
@​empty
<li>No hay proyectos para mostrar</li>
@endforelse
@​else
<li>Catálogo no definido</li>
@endisset
</ul>

Existe una variable $loop disponible dentro de cada bucle @foreach. Esta variable es de
tipo stdClass y proporciona metainformación sobre el bucle en el que se encuentra
actualmente. Para echar un vistazo al tema, cambie la parte resaltada del código anterior
por lo siguiente:

<li> {{ $itemCatalogo[​'producto'​] }} ​<pre> {{ ​var_dump​(​$loop​) }} </pre>​ </li>

El resultado devuelto por ​var_dump​ o ​print_r​, será similar al siguiente, para la primera
iteración:
object(stdClass)​#214 (10) {
[​"iteration"​] => int(​1​)
[​"index"​] => int(​0​)
[​"remaining"​] => int(​4​)
[​"count"​] => int(​5​)
[​"first"​] => bool(​true​)
[​"last"​] => bool(​false​)
[​"odd"​] => bool(​true​)
[​"even"​] => bool(​false​)
[​"depth"​] => int(​1​)​ // nivel de profundidad
[​"parent"​]=> ​NULL
}

La importancia de la información proporcionada por este objeto, radica en que podría, por
ejemplo, formater de manera diferente el primero o último elemento de una lista o cebrarla
fácilmente. Ejemplo, usando el operador condicional:

$loop→last ? ...
$loop→first ? ...

En el apartado “​estructuras de control​” de Laravel puede consultar sobre otras directivas


para el control de la ejecución de las aplicaciones.

Controladores

Nota​: si usa Visual Studio Code (en adelante VSC) active la terminal (menú ​Terminal ​>
Nueva Terminal​ o ​Ctrl​+​Mayúsculas​+​ñ)​ , la necesitaremos:

Importante​: asegúrese de estar dentro de la carpeta principal del proyecto, antes de


ejecutar los siguientes comandos ​Artisan​.
Al inicio del tema anterior se agregó en ../routes/​web.php​, un array para ejemplificar
estructuras de control. Sin embargo, con esto se contaminó el código con una instrucción
que no corresponde a la responsabilidad de un archivo de rutas. A propósito, para ver
desde la consola las rutas que se tienen en ​../routes​, use el comando ​route:list​ como se
muestra en la figura siguiente, la cual también incluye los resultados de dicho comando:

Del listado que se visualiza, vale la pena resaltar que en la columna “Action”, se muestra el
controlador ​ViewController.php ​que se ejecuta cuando se utiliza el método ​view​ en las
rutas, el cual se encuentra en ​../vendor/laravel/framework/src/Illuminate/Routing.​

Tenga en cuenta que éste y todos los componentes de Laravel están bajo el namespace
Illuminate​.

Volviendo al tema de la contaminación del código lo mejor es crear un nuevo controlador


para la URL catalogo. El comando Artisan para crear controladores y sus distintas opciones
puede consultarse de la siguiente manera:

php artisan ​make:controller​ -h

Según la descripción proporcionada por el comando anterior, para crear controladores


básicos, se usa un comando como el siguiente:

php artisan ​make:controller​ NombreControlador

El controlador se creará en la carpeta ​app/Http/Controllers/​. Pruebe entonces:

php artisan ​make:controller​ CatalogoController

Puede ver el controlador creado en la ruta ​../app/Http/Controllers/.​ Observe que es una


simple clase PHP que hereda de la clase Controller que se encuentra en la misma carpeta:

namespace​ ​App​\​Http​\​Controllers​;

class​ ​CatalogoController​ ​extends​ ​Controller​ {


​//
}

Elimine ​CatalogoController​ para probar otra opción de creación:

php artisan ​make:controller​ CatalogoController -i


La opción ​-i​ o ​--invokable​ ​se utiliza para generar un controlador con un único ​método
mágico​ ​__invoke​ que hace ​invocable​ a la clase. Las clases ‘invocables’, son clases
convertidas en callbacks/callables, es decir, pueden ser llamadas como una función.

Esto ayuda a mantener el principio de responsabilidad única de los patrones SOLID, ya que
si una clase implementa el método __invoke() está declarando de forma implícita cual es la
acción, funcionalidad o responsabilidad de dicha clase.

Volviendo a nuestro controlador recien creado, vemos que ahora quedó de la siguiente
manera:

<?php

namespace​ ​App​\​Http​\​Controllers​;

use​ ​Illuminate​\​Http​\​Request​;

class​ ​CatalogoController​ ​extends​ ​Controller​ {


​/**
* Handle the incoming request.
*
​ param​ \Illuminate\Http\Request $request
* @
* @​ return​ \Illuminate\Http\Response
*/
​public​ ​function​ ​__invoke​(Request $request)​ {
​//
}
}

En los pasos siguientes se indica cómo descontaminar el código del archivo


../routes/​web.php​, que como recordará, hasta ahora incluye un array que no debería ser
definido en él.

1. Para esta prueba, el archivo ../routes/​web.php​, queda implementado de la siguiente


manera:

Route::view(​'/'​, ​'home'​, [​'nombre'​ => ​'Carlos'​])->name(​'inicio'​);


Route::view(​'/acercade'​, ​'acercade'​)->name(​'acercade'​);
Route::​get​(​'/catalogo'​, ​'CatalogoController'​)->name(​'catalogo'​)​;
Route::view(​'/contacto'​, ​'contacto'​)->name(​'contacto'​);

Importante​: se pueden ​proteger los parámetros​ que definimos en nuestras rutas.


Para esto, se hace un llamado al método​ where()​ del objeto que devuelve get para
inicar al sistema de rutas como debe validar cada parámetro. Por ejemplo:
Route::get(​'user/{name}'​, ​'UserController@show'​)->where(​'name'​, ​'[A-Za-z]+'​);
Route::get(​'user/{id}'​, UserController@show)->where(​'id'​, ​'[0-9]+'​);
Route::get(​'user/{id}/{name}'​, UseController@show)->
where([​'id'​ => ​'[0-9]+'​, ​'name'​ => ​'[a-z]+'​]);

2. También implemente en la clase ../app/Http/Controllers/​CatalogoController.php​, el


método mágico, como se muestra enseguida:

​public​ ​function​ ​__invoke​(Request $request)​ {


$catalogo = [
[​'producto'​ => ​'Harina de trigo'​],
[​'producto'​ => ​'Arroz parvorizado'​],
[​'producto'​ => ​'Chocolate en polvo'​],
[​'producto'​ => ​'Café liofilizado'​],
[​'producto'​ => ​'Queso campesino'​]
];
​return​ view(​'catalogo'​, compact(​'catalogo'​));
}

3. Pruebe la aplicación y los resultados deberán ser los mismos que obtuvo en
anteriores implementaciones.

Se sigiere que consulte información adicional importante ​aquí​.

4. Pruebe de nuevo la ejecución del comando ​php artisan route:list​ o lo que es lo


mismo,​ php artisan r:l​ y​ observe el listado de rutas que se obtiene ahora.

Clases controladoras de recursos y API

Vamos a crear un ​controlador​ que provea un grupo de rutas de recursos con peticiones de
tipo ​index, create, show, edit, store, update ​y​ destroy​. Estas peticiones son ampliamente
usadas y para no tener que crear una ruta para cada método, es que Laravel agrupa todos
éstos en un controlador de tipo resource. El significado de estos métodos es el siguiente:

● index​: se usa para listar recursos.

● create​: mostrar un formulario para crear un nuevo recurso.

● show​: se muestra un recurso especifcado por el argumento que recibe el método..

● edit​: muestra un formulario para editar un recurso especificado.

● store​: guardar un recurso creado con el método ​create​, en la base de datos.

● update​: guarda los cambios realizados con ​edit​.

● destroy​: dado un identificador, elimina un recurso.


Para crear un controlador con las características descritas, proceda según los pasos que se
indican a continuación:

1. Utilice uno de los siguientes comandos:

● php artisan ​make:controller​ CatalogoController ​--resource

● php artisan ​make:controller​ CatalogoController ​-r

2. Implemente el método ​index​() del nuevo controlador


(../app/Http/Controllers/​CatalogoController.php​), como se indica enseguida:

​public​ ​function​ ​index​()​ {


$catalogo = [
[​'producto'​ => ​'Harina de trigo'​],
[​'producto'​ => ​'Arroz parvorizado'​],
[​'producto'​ => ​'Chocolate en polvo'​],
[​'producto'​ => ​'Café liofilizado'​],
[​'producto'​ => ​'Queso campesino'​],
];

​return​ view(​'catalogo'​, compact(​'catalogo'​));


}

3. En ../routes/​web.php​, sólo hay que agregar la referencia al método ​index​ del


controlador, para que la aplicación funcione como estaba hasta ahora. Observe la
parte resaltada del código:

Route::view(​'/'​, ​'inicio'​, [​'nombre'​ => ​'Carlos'​])->name(​'inicio'​);


Route::view(​'/acercade'​, ​'acercade'​)->name(​'acercade'​);
Route::get(​'/catalogo'​, ​'​CatalogoController@index​'​)->name(​'catalogo'​);
Route::view(​'/contacto'​, ​'contacto'​)->name(​'contacto'​);

4. Pruebe que el funcionamiento de la aplicación sea el esperado.

En los siguientes pasos se mostrarán brevemente otros aspectos sobre los que se
profundizará luego.

5. Comente todas las rutas del paso 3 y deje sólo lo siguiente:

Route::resource(​'catalogo', '​CatalogoController​'​);

6. Guarde y ejecute:

php artisan r:l

Observe que se generan las 7 rutas documentadas al inicio (index, create, ...).
7. Con el siguiente comando se generan sólo las rutas indicadas:

Route::resource(​'catalogo', '​CatalogoController​'​)->only([
'index', 'show'
]);

8. Guarde y ejecute nuevamente:

php artisan r:l

Observe que se generan sólo las 2 rutas incluidas en el array.

9. Generar todas las rutas, excepto las rutas indicadas en el array:

Route::resource(​'catalogo', '​CatalogoController​'​)->except([
'index', 'show'
]);

10. Guarde, ejecute nuevamente el comando Artisan y observe los resultados:

php artisan r:l

11. Renombre nuevamente a ../app/Http/Controllers/​CatalogoController.php​,

12. Ejecute la siguiente instrucción:

php artisan make:controller CatalogoController --api

Verifique el contenido del controlador y observe que la única diferencia es que se


excluyen los métodos create y edit que no se requieren en una API.

Un resultado similar se obtiene con el siguiente paso:

13. Agregue en ../routes/​web.php​, el siguiente comando:

Route::apiResource('catalogo', 'CatalogoController');

14. Guarde los cambios y ejecute nuevamente

php artisan r:l

Note que efectivamente se obtienen los mismos resultados. Por supuesto también se
tienen a disposición los métodos only y except.

15. Si desea cambiar el nombre de los verbos o acciones, en ../routes/​web.php​, cambie


la instrucción ​Route::​apiResource​(...)​ por:

Route::​resource​('catalogo', 'CatalogoController');

16. Abra el archivo ../app/Providers/​AppServiceProvider.php​ y asigne los nombres


como se indica a continuación:
​public​ ​function​ ​boot​()​ {
​\Route​::resourceVerbs([ ​// “\” indica que se debe importar
​'create'​ => ​'crear'​,
​'edit'​ => ​'editar'​,
​'update'​ => ​'actualizar'​,
​'index'​ => ​'inicio'​,
​'destroy'​ => ​'eliminar'​,
...
]);
}
Importante​: el backslash (“\”) antes de Route, indica que hay que importar dicha
clase. Esto equivale a la instrucción:

use​ ​Illuminate​\​Support​\Facades\​Route​;

Enlaces y un poco de Helpers

Como excusa para continuar con los temas fundamentales, enseguida se plantea un
ejemplo trivial de cómo activar un enlace de navegación e indicar la ruta de navegación. No
se preocupe si se empieza a embeber código dentro de HTML, luego se indicará como
mantener debidamente separado el código CSS, HTML, JavaScript y PHP.

1. Seleccione el archivo ../resources/views/​plantilla.blade.php​ y

<head>
...
<style>
.​activo​ a {
color: red;
text-decoration: none;
}
</style>
</head>

<body>
<nav>
<ul>
​<li ​class​="​activo​"><​a​ ​href​="/">​Inicio​</​a​></​li​>
<​li​><​a​ h
​ ref​="/​acercade​">​Acerca​ ​de​...</​a​></​li​>
<​li​><​a​ h​ ref​="/​catalogo​">​Cat​á​logo​</​a​></​li​>
<​li​><​a​ h ​ ref​="/​contacto​">​Contacto​</​a​></​li​>
</​ul​>
</​nav​>

@​yield​('​contenido​')

</​body​>
</​html​>
2. HTTP define un conjunto de métodos de petición (​request​) para indicar la acción que
se desea realizar para un recurso determinado.Esto se maneja en Laravel mediante
una instancia de la clase ​Request.​ Eseguida vamos a usar la función ​request()​ que
​ ara ver algunas
devuelve una nueva instancia de ​Illumitate/Http/Request, p
características de dicho objeto:

<body>
<nav>
<pre> {{ request() }} </pre>
<ul>
​<li ​class​="​activo​"><​a​ ​href​="/">​Inicio​</​a​></​li​>
<​li​><​a​ h
​ ref​="/​acercade​">​Acerca​ ​de​...</​a​></​li​>
<​li​><​a​ h​ ref​="/​catalogo​">​Cat​á​logo​</​a​></​li​>
<​li​><​a​ h ​ ref​="/​contacto​">​Contacto​</​a​></​li​>
</​ul​>
</​nav​>

@​yield​('​contenido​')

</​body​>

Si ejecuta la aplicación podrá ver las características esenciales de dicho objeto,


dichas características también pueden verse en formato JSON si usa la función
dump()​. Veamos:

<pre> {{ ​dump​(request()) }} </pre>

Pruebe la visualización de resultados de esta forma.

3. Pruebe ahora el resultado que devuelve el método ​url​()​ del objeto Request:

{{ dump(​request​()->url()) }}

Deberá obtener algo como esto:

"http://appxx.test/contacto"

4. Pruebe también el método ​path(​ )​ para ver sólo la carpeta actual y no la ruta
completa:

{{ dump(​request​()->path()) }}
5. Y uno más, el método ​routeIs​()​ que devuelve t​ rue​ si el argumento dado corresponde
a una ruta. Con este método es posible resaltar la ruta seleccionada actualmente.
Veamos:

<li ​class​="{{ request()->routeIs(​'inicio'​) ? ​'activo'​ : ​''​ }}​">


<a href="​/​">Inicio</a></li>
<li class="​{{ request()->routeIs(​'acercade'​) ? ​'activo'​ : ​''​ }}​">
<a href="​acercade​">Acerca de...</a></li>
<li class="​{{ request()->routeIs(​'catalogo'​) ? ​'activo'​ : ​''​ }}​">
<a href="​catalogo​">Catálogo</a></li>
<li class="​{{ request()->routeIs(​'contacto'​) ? ​'activo'​ : ​''​ }}​">
<a href="​contacto​">Contacto</a></li>

Pruebe que efectivamente esta lógica funciona para pasar a optimizar dicho código,
mediante el uso de una función.

6. Cree el archivo ../app/​helpers.php​ y en él agregue la función que se utilizará:

<?php
function​ ​seleccionado​($nombreEnlace)​ {
​return​ request()->routeIs(​$nombreEnlace​) ? ​'activo'​ : ​''​;
}

7. Modifique en ./resources/views/​plantilla.blade.php​ los elementos de lista para


utilizar la función creada en el punto anterior:

<li ​class​="{{ ​seleccionado​(​'inicio'​) }}​">


<a href="​/​">Inicio</a></li>
<li class="​{{ ​seleccionado​(​'acercade'​) }}​">
<a href="​acercade​">Acerca de...</a></li>
<li class="​{{ ​seleccionado​(​'catalogo'​) }}​">
<a href="​catalogo​">Catálogo</a></li>
<li class="​{{ ​seleccionado​(​'contacto'​) }}​">
<a href="​contacto​">Contacto</a></li>

8. Ahora es necesario indicar a Composer que además de las clases, también se debe
cargar el archivo del paso 6 para poder utilizarlo en cualquier ruta. Así que modifique
el elemento “autoload” del archivo ../​composer.json​ de tal manera que se agregue
la entrada que se resalta enseguida:

​"​autoload​"​: {
​"psr-4"​: {
​"App\\"​: ​"app/"
},
​"classmap"​: [
​"database/seeds"​,
​"database/factories"
],
​"​files​"​: [​"app/helpers.php"​]
}

9. Importante​: cada vez que se modifica el archivo ../​composer.json​ es necesario


indicarle a Composer que se debe compilar el autocargador, para ello, en la terminal
ingrese el comando:

composer ​dumpautoload

10. Pruebe que efectivamente la aplicación continúe trabajando correctamente y


observe que el código de los enlaces es mucho más limpio que el de los ejemplos de
los pasos anteriores.

Como la navegación de una aplicación web puede ser algo que cambie
constantemente, de ahora en adelante, vamos a mantenerla separada.

11. Cree la carpeta ..resources/views/​partials​ y dentro de ella cree el archivo


nav.blade.php​.

12. Corte el bloque ​<nav>...</nav> ​que actualmente se encuentra en el archivo


../resources/views/​plantilla.blade.php ​y agréguelo al archivo ​nav.blade.php​.

13. Agregue la directiva ​@include('partials/nav')​ en ​plantilla.blade.php​, de manera


que ahora el código del cuerpo del script sea el siguiente:

<body>
@​include​(​'partials/nav'​)
@​yield​(​'contenido'​)
</body>

14. Pruebe que todo siga funcionando…

Envío de formularios

Hasta aquí se tiene una aplicación en la que se ha trabajado con la siguiente estructura:

● Appxx
○ Http
■ Controllers
● CatalogoController.php
■ helpers.php
○ resources
■ views
● partials
○ nav.blade.php
● acercade.blade.php
● catalogo.blade.php
● contacto.blade.php
● inicio.blade.php
● plantilla.blade.php
○ routes
■ web.php
○ composer.json

Ahora se requiere modificar el archivo ../resources/views/​contacto.blade.php​ para incluir


un formulario muy básico, como el que se muestra enseguida:

El código base para dicho formulario, es el siguiente:

@extends(​'plantilla'​)

@section(​'titulo'​, ​'Contacto'​)

@section(​'contenido'​)
<h1>Contacto</h1>
<form>
<input name=​"nombre"​ placeholder=​"Nombre..."​><br>
<input type=​"email"​ name=​"correo"​ placeholder=​"Correo..."​><br>
<input name=​"asunto"​ placeholder=​"Asunto..."​><br>
<textarea name=​"contenido"​ placeholder=​"Mensaje..."​></textarea><br>
<button>Enviar</button>
</form>
@endsection
Si prueba a usar un formulario como el que se le pide implementar, notará que los datos
envíados se muestran en la barra de dirección, lo que indica que por defecto se están
enviando por ​GET​. Cambie para enviar los datos por ​POST​:

<form method=​"post"​>

Si hecho este cambio, intenta enviar de nuevo los datos, obtendrá el ​“error The POST
method is not supported for this route. Supported methods: GET, HEAD”,​ esto sucede
porque en web.php, para el formulario de contacto sólo se ha definido la ruta:

Route::​view​(​'/contacto'​, ​'contacto'​)->name(​'contacto'​);

Y view, registra rutas de tipo ​GET​. Así que proceda con los siguientes pasos para adecuar
este formulario para que funcione mediente el método POST:

1. Indique que la acción POST lleva también a la ruta contacto (aunque no es


necesario, es una buena práctica):

<form method=​"post" ​action={{ route(​'contacto'​) }}​>

2. Agregue en ../routes/web.php una nueva ruta para indicar que se utilizará un nuevo
controlador que haga persistibles los datos:

Route::post(​'contacto'​, ​'MensajesController​@store'​);

3. Estando dentro de la carpeta de la aplicación, desde la ventana de comandos cree el


controlador del paso 2:

php artisan make:controller ​MensajesController

4. Abra el archivo ../app/Http/Controllers/​MensajesController​.php para editarlo y


agregar una instrucción de prueba:

<?php
namespace​ ​App​\​Http​\​Controllers​;

use​ ​Illuminate\Http\​Request​;
class​ ​MensajesController​ ​extends​ ​Controller​ {

​ ​public​ ​function​ ​store​()​ {


​return​ ​'formulario procesado'​;
}

}
5. Intente ejecutar de nuevo. Obtendrá un error 419 “Page Expired”. Esto sucede
porque Laravel protege automáticamente contra ataques XSS, un tipo de ataque de
suplantación de identidad que inyectta código malicioso para su posterior ejecución.

Esto se corrige agregando siempre la directiva ​@csrf​ que agrega un campo oculto
con el token de usuario y que Laravel verifica automáticamente:

<form method=​"post"​ action={{ route(​'contacto'​) }}>


​@csrf
<input name=​"nombre"​ placeholder=​"Nombre..."​><br>
...
<button>Enviar</button>
</form>

Recargue el formulario y pruebe nuevamente. Todo deberá funcionar ahora.

Ahora veamos las formas de acceder a los datos que el usuario ingresó en el
formulario:

6. Modifique el método del paso 4 para devolver un objeto JSON con los datos del
formulario:

public​ ​function​ ​store​(Request $request)​ {


​return​ $request;
}

Recargue el formulario y pruebe nuevamente el envío de datos. ejemplo:

{
"​_token​": ​"xawl6QZrFHmAJHtkFdjKglkT19aOzXPyNFSjBuBa"​,
"nombre": ​"Carlos"​,
"correo": ​"carlos.cuesta@ucaldas.edu.co"​,
"asunto": ​"Prueba de Request"​,
"contenido": ​"Devolviendo datos correctamente"
}

Importante​: tenga en cuenta que el atributo ​name ​es requerido en los formularios ya
que si no se incluye, los datos no serán enviados.

7. Si sólo requiere el dato de un campo, use el método get del objeto Request.
Ejemplo:

public​ ​function​ ​store​(Request $request)​ {


​return​ $request->get(​'nombre'​);
}
8. También se puede usar una forma resumida que no requiere la importación de la
clase Request. Observe:

<?php

namespace​ ​App​\​Http​\​Controllers​;
class​ ​MensajesController​ ​extends​ ​Controller​ {

​public​ ​function​ ​store​()​ {


​return​ ​request(​'nombre'​)​;
}

Validación de formularios en Laravel

Este curso no incluye validación de datos del lado del cliente usando HTML y JavasScript,
pero si aborda el tema de las ​validaciones datos del lado del servidor​.

Modifique el archivo ../app/Http/Controllers/​MensajesController​.php para utilizar el método


validate ​de la clase Request con el fin de verificar mediante la ​regla​ ​required​, si el usuario
ingresa el valor para el campo nombre:

<?php

namespace​ ​App​\​Http​\​Controllers​;

class​ ​MensajesController​ ​extends​ ​Controller​ {

​public​ ​function​ ​store​()​ {


​// ver las r ​ eglas​ que se pueden ingresar en el array asociativo
request()->validate([
​'nombre'​ => ​'required',
​'correo'​ => ​'required|email', ​// también ['required','email']
​'asunto'​ => ​'required',
​'contenido'​ => [​'required', 'min:5']
]);

​// si se despliega este mensaje la validación pasó, si no,


​// validate, regresa automáticamente a la página del formulario
​return​ ​'datos válidos'​;
}

}
Pruebe este cambio, notará que cuando no se ingresa el nombre o el correo, se regresa al
formulario y que en caso contrario, se muestra el mensaje “datos válidos”.

Laravel proporciona la variable $​error​ que se puede utilizar para conocer el estado de los
errores. Para ver su funcionamiento, inclúyala en el formulario de contacto,
../resources/views/​contacto.blade​.php, justo en la parte que se indica en la siguiente
porción de código:

@section(​'contenido'​)
<h1>Contacto</h1>
​{{ $errors }}

<form method=​"post"​ action={{ route(​'contacto'​) }}>


...
</form>
@endsection

Si vuelve a probar la aplicación, notará que cuando se proporcional el nombre, se muestra


un array vacío y que cuando no se proporciona el nombre, se despliega el error:

{"nombre":["The nombre field is required."]}

También se puede verificar mediante ​$errors->​any​() ​si existe o no existe algún error.
Ejemplo:

@section(​'contenido'​)
<h1>Contacto</h1>
​{{ var_dump(​$errors->​any​()​) }}

<form method=​"post"​ action={{ route(​'contacto'​) }}>


...
</form>
@endsection

O también, con una variación del caso anterior, mostrar errores si los hay:

@section(​'contenido'​)
<h1>Contacto</h1>

@​if​($errors->any())
{{ ​var_dump​($errors->all()) }}
@​endif
<form method=​"post"​ action={{ route(​'contacto'​) }}>
...
</form>
@endsection

Por supuesto, la implementación de la directiva @if se puede mejorar con el fin de ir


pensando en algunas ideas de cómo mostrar los errores de una manera inteligible a los
usuarios. Pruebe por ejemplo a hacer que todos los campos sean requeridos y luego
ensaye a mostrar los errores de la siguiente manera:

@​if​($errors->any())
@​foreach​($errors->all() ​as​ $error)
<p> {{ $error }} </p>
@​endforeach
@​endif

Pruebe también a mostrar los errores debajo de cada campo:

@extends(​'plantilla'​)

@section(​'titulo'​, ​'Contacto'​)

@section(​'contenido'​)
<h1>Contacto</h1>

<form method=​"post"​ action={{ route(​'contacto'​) }}>


@csrf
<input name=​"nombre"​ placeholder=​"Nombre..."​><br>
​{{ $errors->first(​'nombre'​) }} <br>
<input type=​"email"​ name=​"correo"​ placeholder=​"Correo..."​><br>
​{{ $errors->first(​'correo'​) }} <br>
<input name=​"asunto"​ placeholder=​"Asunto..."​><br>
​{{ $errors->first(​'asunto'​) }} <br>
<textarea name=​"contenido"​ placeholder=​"Mensaje..."​></textarea><br>
​{{ $errors->first(​'contenido'​) }} <br>
<button>Enviar</button>
</form>
@endsection

Incluso puede experimentar a dar formato a cada uno de los mensajes, enviando como
segundo parámetro una estructura HTML:
<form method=​"post"​ action={{ route(​'contacto'​) }}>
@csrf
<input name=​"nombre"​ placeholder=​"Nombre..."​><br>
​{!! $errors->first(​'nombre'​, ​'<small>:message</small>'​) !!} <br>
<input type=​"email"​ name=​"correo"​ placeholder=​"Correo..."​><br>
​{!! $errors->first(​'correo'​, ​'<small>:message</small>'​) !!} <br>
<input name=​"asunto"​ placeholder=​"Asunto..."​><br>
​{!! $errors->first(​'asunto'​, ​'<small>:message</small>'​) !!} <br>
<textarea name=​"contenido"​ placeholder=​"Mensaje..."​></textarea><br>
​{!! $errors->first(​'contenido'​, ​'<small>:message</small>'​) !!} <br>
<button>Enviar</button>
</form>

 Cuando las validaciones son muy complejas, puede ser conveniente usar una clase
especial de Laravel llamada ​Form Request​ que permiten separar la lógica de validación de
datos (validación, mensajes de errores, autorización de usuarios y redirección en caso de
fallar) de la lógica del controlador. Esta clase intercepta la solicitud o request y valida los
datos que vienen de una petición HTTP antes de pasar al controlador.

1. Suponga que tiene en ..\resources\views\productos\create.blade.php la siguiente


implementación del método store():

​public​ ​function​ ​store​()​ {


$campos = request()->validate([
​'nombre'​ => ​'required'​,
​'precio'​ => ​'required'​,
​'iva'​ => ​'required'​,
​'cantidad_disponible'​ => ​'required'
...
]);

Producto::create($campos);
​return​ redirect()->route(​'productos.index'​);
}

2. Suponga también que se quiere mostrar los errores de validación al usuario. Una
forma rápida de hacerlo, sería incluir la validación en la plantilla que muestra el
formulario: ..\resources\views\productos\create.blade.php

@extends(​'plantilla'​)
@section(​'titulo'​, ​'Agregar producto'​)
@section(​'contenido'​)
<h1>Agregar nuevo producto</h1>

@​if​($errors->any())

<ul>
@​foreach​($errors->all() ​as​ $error)
<li>{{ $error }}</li>
@​endforeach
</ul>
@​endif

<form method=​"POST"​ action=​"{{ route('productos.store') }}"​>


@csrf
...
</form>

@endsection

3. Realice estos dos cambios y observe el comportamiento de la validación cuando se


intenta guardar un registro sin datos.

Form Request​ nos provee otra manera de hacer las validaciones. Veamos:

4. Estando en la carpeta de la aplicación, en la ventana de comandos, ingrese el


comando:

php artisan make:​request ​CreateProductoRequest

Esta instrucción creará un form request con el nombre dado como argumento, en
​ reateProductoRequest​.php​.
este caso ​..\app\Http\Requests\C

5. Implemente dicha clase como se muestra enseguida, sabiendo que el método


authorize()​ define qué usuarios pueden agregar registros y que el método ​rules()
define las reglas de validación que se deben aplicar:

<?php

namespace​ ​App​\​Http​\​Requests​;

use​ ​Illuminate​\​Foundation​\​Http​\​FormRequest​;

class​ ​CreateProductoRequest​ ​extends​ ​FormRequest​ {

​/**
* Determina si el usuario está autorizado para hacer el request
* Si las reglas incluidas aquí se producirá un error 403,
* indicando que no hay autorización para realizar la acción.
* @​ return​ bool
*/
​public​ ​function​ ​authorize​()​ {
​return​ ​true​; ​// <-- OJO cualquier usuario está autorizado
}

​/**
* D​ efina aquí las reglas de validación que se aplican al request.
* @ ​ return​ array
*/
​public​ ​function​ ​rules​()​ {
​return​ [
​'nombre'​ => ​'required'​,
​'precio'​ => ​'required'​,
​'iva'​ => ​'required'​,
​'cantidad_disponible'​ => ​'required'
...
];
}

6. Enseguida se resaltan los cambios que hay que hacer en el controlador


..\app\Http\Controllers\ProductoController.php para utilizar la clase
CreateProductoRequest​,

<?php

namespace​ ​App​\​Http​\​Controllers​;
​ pp​\​Producto​;
use​ A
use​ I​ lluminate​\​Http​\​Request​;
use​ A ​ pp​\​Http​\​Requests​\​CreateProductoRequest​;

​ roductoController​ ​extends​ ​Controller​ {


class​ P
​// Para utilizar ​CreateProductoRequest​, sólo se requiere
// importar la clase e implementar el método store() de la
// siguiente manera:

​public​ ​function​ ​store​(CreateProductoRequest $request)​ {


Producto::create($request->validated());
​return​ redirect()->route(​'productos.index'​);
}
}

Pruebe que todo sigue funcionando como antes pero utilizando ahora el form
request.

7. En ​..\app\Http\Requests\​CreateProductoRequest.​ php,​ también se puede agregar el


método ​messages()​ para personalizar las reglas de validación:
​function​ ​messages​()​ {
​return​ [
​'nombre.required'​ => ​'El producto requiere un nombre'​,
​'precio.required'​ => '
​ No ha ingresado el precio',
...
];
}

Importante​:

● Verifique que sucede si en vez de ​{!! … !!}​, utiliza ​{{ … }}​ y consulte qué relación
tienen estas dos notaciones con la inyección de código.

● También realice una mejora para el usuario, haga que los datos se conserven
cuando el formulario no pase la validación, para evitar que el usuario tenga que
volverlos a llenar todos:

<form method=​"post"​ action={{ route(​'contacto'​) }}>


@csrf
<input name=​"nombre"​ placeholder=​"Nombre..."
value=​"{{ ​old​('nombre') }}"​><br>
{!! $errors->first(​'nombre'​, ​'<small>:message</small>'​) !!} <br>

<input type=​"email"​ name=​"correo"​ placeholder=​"Correo..."


value=​"{{ ​old​('correo') }}"​><br>
{!! $errors->first(​'correo'​, ​'<small>:message</small>'​) !!} <br>

<input name=​"asunto"​ placeholder=​"Asunto..."


value=​"{{ ​old​('asunto') }}"​><br>
{!! $errors->first(​'asunto'​, ​'<small>:message</small>'​) !!} <br>

<textarea name=​"contenido"​ placeholder=​"Mensaje..."


​value​=​"{{ ​old​('contenido') }}"​></textarea><br>
{!! $errors->first(​'contenido'​, ​'<small>:message</small>'​) !!} <br>

<button>Enviar</button>
</form>

Complemento sobre manejo de respuestas del servidor (RESPONSES)

En esta sección se estudiarán algunos temas complementarios a lo visto hasta aquí. Para
ello se recomienda que cree una copia de la aplicación y sobre la copia, a su vez cree un
nuevo controlador de pruebas:
1. Estando en la carpeta de la aplicación utilice el siguiente comando:

php artisan make:controller ​OtrasPruebasController

2. De manera general un ​helper​ es una función de ayuda que realiza una tarea o
procedimiento específico y que su uso puede repetirse a lo largo de nuestro
proyecto. Laravel ofrece algunos helpers que son de gran utilidad en nuestras
aplicaciones. Algunos de ellos están definidos en el archivo
..\vendor\laravel\framework\src\Illuminate\Foundation\​helpers.php​. De este archivo
nos interesa la función response() cuya cabecera es la siguiente:

function​ ​response​($content = ​''​, $status = ​200​, array $headers = [])​ {..}

Como lo indica la documentación, esta función devuelve una nueva respuesta de la


aplicación y recibe tres argumentos: el contenido que se quiere devolver, el estado
que por defecto es 200 y un array con elementos de cabecera adicionales. Para
verla en acción, agregue en ..\app\Http\Controllers\​OtrasPruebasController.php​, el
siguiente método:

public​ ​function​ ​inicio​()​ {


​return​ response(​'Contenido de la respuesta'​, 2 ​ 01​,
[​'ESTADO-TOKEN'​ => ​'Hasta ahora todo bien'​]);
}

El resultado obtenido al ejecutar la aplicación debería ser el que se muestra en la


figura siguiente. Para lograrlo, lleve a cabo primero el paso 3.
3. Modifique en el archivo ..\routes\web.php la ruta raíz para que que quede apuntando
a la función creada en el paso 2:

Route::get(​'/'​, ​'OtrasPruebasController@inicio'​)->name(​'inicio'​);

4. Observe los resultados y compruebe que puesto que el tercer argumento es de tipo
array, puede enviar varias cabeceras.

5. Una sintaxis alternativa y mucho más clara para el envío de cabeceras y de cookies
es la que se indica en el siguiente ejemplo:

​public​ ​function​ ​inicio​()​ {


​return​ ​response​(​'Estamos en la página inicial'​, 2
​ 01​)
->header(​'ESTADO-TOKEN'​, ​'Hasta ahora todo bien'​)
->header(​'ESTADO2-TOKEN'​, ​'La cosa sigue bien'​)
->cookie(​'MI-COOKIE'​, ​'Este dato se envía encriptado'​);
}

Dele una mirada a la ​información oficial​ del tema y compruebe que tanto las dos
cabeceras como el ​cookie​ son recibidos por el navegador (http://appxx.test/).

6. Actualice la clase dsds para que quede como se muestra enseguida:

<?php

namespace​ ​App​\​Http​\​Controllers​;

use​ ​Illuminate​\​Http​\​Request​;

class​ ​OtrasPruebasController​ ​extends​ ​Controller​ {

​public​ ​function​ ​inicio​()​ {


​return​ response(​'Contenido de la respuesta'​, ​201​)
->header(​'ESTADO-TOKEN'​, ​'Hasta ahora todo bien'​)
->header(​'ESTADO2-TOKEN'​, ​'La cosa sigue bien'​)
->cookie(​'MI-COOKIE'​, ​'Este dato se envía encriptado'​);
}

​public​ ​function​ ​respuestaContacto​(Request $request)​ {


$data = $request->all();
​return​ ​response​()->json([
​'data'​ => $data
], ​202​)->header(​'MI-TOKEN'​, ​'cualquier cosa'​);
}

}
Con el nuevo método lo que se pretende es ver la respuesta que el servidor da
cuando se ejecuta la aplicación (appxx.test/​contacto​) y se pulsa clic en enviar. por lo
tanto haga el cambio que se indica a continuación:

7. Actualice el archivo ..\routes\web.php para que quede como se muestra enseguida:

<?php

Route::get(​'/'​, ​'OtrasPruebasController@inicio'​)->name(​'inicio'​);

Route::view(​'/acercade'​, ​'acercade'​)->name(​'acercade'​);
Route::get(​'/catalogo'​, ​'CatalogoController@index'​)->name(​'catalogo'​);

​ contacto'​)->name(​'contacto'​);
Route::view(​'contacto'​, '
Route::post(​'contacto'​, '​ OtrasPruebasController@respuestaContacto'​);

8. Si ejecuta la aplicación (appxxx.test/​contacto​) y pulsa clic en enviar, el servidor


enviará como respuesta en formato JSON los mismos datos incluidos en el
formulario, ya no con el estado 200 sino con el 202 y adicionalmente se agregará la
cabecera “mi-token”.

9. En el mismo helper que incluye la función response() documentada en los puntos


anteriores, se incluye la función:

​function​ ​redirect​($​ to = null, $status = ​302​, $headers = [], $secure = null​) ​{


.
.
.
}

Esta función retorna una instancia de la clase Redirector con una respuesta y un
estado 302, que significa redirección.

Para probar la redirección, realice el siguiente cambio en la función


respuestaContacto para que tan pronto se envíe el formulario, se redireccione a la
página inicial:

​public​ ​function​ ​respuestaContacto​(Request $request)​ {


$data = $request->all();
​return​ ​redirect​()->route(​'inicio'​);
}
 El tema de sesiones es algo bien importante en PHP dado que son una forma
sencilla de almacenar datos para usuarios de manera individual usando un ID de
sesión único. Enseguida se mostrará cómo utilizar ​sesiones​ en Laravel.

10. Nuevamente modifique la función respuestaContacto para que quede implementada


de manera que se redireccione al mismo formulario de contacto:

​public​ ​function​ ​respuestaContacto​(Request $request)​ {


$data = $request->all();
​return​ redirect()
->route(​'contacto'​)
->​with​(​'info'​, ​'Mensaje enviado correctamente'​);
}

11. Ahora incluyamos el pequeño cambio que se resalta en el formulario de contacto


(...\resources\views\contacto.blade.php) para ver el contenido de “info”, si es que
dicho elemento existe en el array asociativo que contiene las variables de sesión
disponibles para el script actual.

@extends(​'plantilla'​)

​ Contacto'​)
@section(​'titulo'​, '

@section(​'contenido'​)
<h1>Contacto</h1>

​@​if​(session()->has(​'info'​))
​<h3> {{ session(​'info'​) }} </h3>
​@​endif

<form method=​"post"​ action={{ route(​'contacto'​) }}>


@csrf
...
</form>

@endsection

12. Pruebe de nuevo el comportamiento de la aplicación (appxx.test/contacto).

13. Sin embargo en ..\vendor\laravel\framework\src\Illuminate\Foundation\helpers.php,


existe también un helper que retorna una respuesta de redireccionamiento a la
ubicación anterior:

​function​ ​back​($status = ​302​, $headers = [], $fallback = false)​ { ... }


Volvamos a modificar respuestaContacto para ver cómo facilita las cosas:

​public​ ​function​ ​respuestaContacto​(Request $request)​ {


$data = $request->all();
​return​ ​back​()->with(​'info'​, ​'Mensaje enviado correctamente'​);
}

Middlewares y filtrado de peticiones HTTP

Los Middleware proveen un mecanismo de “vigilancia” eficiente para el filtro de peticiones


HTTP que ingresan a una aplicación. Por ejemplo, Laravel incluye un middleware que
permite verificar si un usuario está autentificado cuando intenta acceder a la aplicación. Si el
usuario no lo estuviera, el middleware lo redireccionaría a la pantalla de login. Y por el
contrario, si lo estuviera, el middleware permitiría el acceso a la aplicación... (​ver más​).

Laravel tiene un middleware para la autenticacion, para quitar los espacios en blanco, para
verificar si tenemos el token csrf, para encriptar las cookies y otros más. Con esto nos
podemos dar cuenta de lo importante que puede ser usar meiddlewares… (​ver más​...)

Para mayor claridad antes de continuar, es muy importante lea la información de los
vínculos que se le proporcionan en los enlaces de los párrafos anteriores y que además
observe cómo están implementados los middlewares que se proporcionan en la carpeta
..\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\.

Como pudo ver casi todos los Middlewares tiene un método llamado handle. Observe por
ejemplo el de la clase VerifyCsrfToken que es el encargado de verificar el token csrf
agregado al formulario de contacto mediante la directiva @csrf.

En los siguientes pasos veremos cómo crear y usar un middleware ejemplo:

1. Estando en la carpeta del proyecto ejecute el comando:

php artisan make:​middleware​ Ejemplo

Este comando creará el middleware..\app\Http\Middleware\​Ejemplo.php​.

2. Modifique el método handle del middleware creado en el paso 1, para que quede
así:

​public​ f​ unction​ ​handle​($request, Closure $next)​ {


​if​ (​false​) { ​// siempre fallará la validación
​return​ $next($request);
}
​return​ response(​'No tiene acceso'​, ​404​);
}
3. Abra el archivo ..\app\Http\Kernel.php que es donde se definen los grupos de
middlewares y observe los comentarios que preceden a cada propiedad, indicando
dónde se aplica cada grupo de middlewares. También recuerde ​este enlace​ si llega
a tener inquietudes con el orden de prioridad de ejecución de éstos.

​ l siguiente elemento que referencia al


4. Agregue al grupo ​routeMiddleware e
middleware creado en el paso 1:

'ejemplo' ​=> App\Http\Middleware\​Ejemplo​::class

5. Una de las formas de usar un middleware es en los controladores, así que abra el
archivo ..\app\Http\Controllers\​OtrasPruebasController.php​ y agregue al inicio de la
clase el siguiente constructor con la instrucción necesaria para ejecutar el
middleware:

​public​ ​function​ ​__construct​()​ {


​$this​->middleware(​'ejemplo'​);
}

6. Pruebe nuevamente a enviar datos desde el formulario (appxx.test/contacto) y note


que como la validación del punto 2 siempre falla, el sistema no le dará acceso.
Cambié a ​true​ el argumento del condicional para que la aplicación vuelva a funcional
normalmente.

Tenga en cuenta que de esta manera, el middleware “ejemplo” se aplica a todos los
métodos del controlador OtrasPruebasController.php.

7. Para aplicar el middleware a métodos específicos, proceda a modificar el constructor


como se indica en el ejemplo siguiente para afectar sólo a los métodos indicados en
el array “only”:

​public​ ​function​ ​__construct​()​ {


​$this​->middleware(​'ejemplo'​, [​'only'​ => [​'respuestaContacto'​]]);
}

También se puede indicar que se excluyan algunos casos. Ejemplo:

​$this​->middleware(​'ejemplo'​, [​'except'​ => [​'inicio'​]]);

Internacionalización de aplicaciones (Localization)

Los mensajes de validación que se desplegaban en inglés en la práctica anterior, están


definidos en la carpeta ​en​ que se encuentra dentro de ../resources/lang/. Puede verificar
ahora el contenido de dichos archivos, pero no es buena idea hacer cambios en dichos
archivos, mejor siga los siguientes pasos para obtener mensajes en español:

1. Abra el archivo ​../config/app.php​, que contiene la configuración de la aplicación

2. En el array asociativo que contiene este archivo, ubique el elemento ​‘locale’​ y


cámbielo el valor por ​'es'​. Con esto se indica que los mensajes de error se
buscarán en una carpeta ​../resources/lang/es​ y con el elemento ​'fallback_locale'
=> 'en'​ que se encuentra más abajo se indica que si no se encuentra la carpeta con
los archivos que definen el idioma español, entonces se utilice el idioma inglés que
viene por defecto.

3. Cree la carpeta ​../resources/lang/​es​, y vaya a este r​ epositio de idiomas de


GitHub para Laravel​, seleccione cada archivo, pulse clic sobre el botón ​Raw​ y
guarde cada archivo dentro de ​../resources/lang/​es​, teniedo especial cuidado en
que los archivos se guarden con la extensión PHP.

4. Si prueba ahora, verá que los mensajes de error se muestran en español.

5. Al final del archivo ​../resources/lang/es\validation.php​, como puede ver, se


encuentra un array asociativo con equivalencias de nombres de atributos en los que
se pueden definir nuestros propios atributos. Por ejemplo, podría agregarse un
atributo ​‘datetime’ ​con el equivalente ​‘fecha y hora’​. Esto sería útil en el supuesto
caso de dar al atributo name de un campo el nombre datetime.

6. También es posible personalizar los mensajes de un formulario específico. Para ello


basta con agregar un segundo argumento de tipo array con los mensajes que se
desean personalizar. Ejemplo para ../app/Http/Controllers/​MensajesController​.php:

request()->validate([
​'nombre'​ => ​'required'​,
​'correo'​ => ​'required|email'​, ​// también soporta format array
​'asunto'​ => ​'required',
​'contenido'​ => [​'required'​, ​'min:3'​],
], [
​'nombre.required'​ => ​'No acepto anónimos'​,
​'contenido.min'​ => ​'Sea breve en los mensajes pero no tanto'​,
]);

7. También puede establecer el idioma dinámicamente en ​../routes/web.php:

<?php

App::setLocale(​'es'​);

Route::view(​'/'​, ​'inicio'​, [​'nombre'​ => ​'Carlos'​])->name(​'inicio'​);


Route::view(​'/acercade'​, ​'acercade'​)->name(​'acercade'​);
Route::get(​'/catalogo'​, ​'CatalogoController@index'​)->name(​'catalogo'​);
// <-- OJO
Route::view(​'/contacto'​, ​'contacto'​)->name(​'contacto'​);

Route::post(​'contacto'​, ​'MensajesController@store'​);

8. En el repositorio recomendado en el paso 3, también existe un ​archivo de


traducciones internas​ que debe ir guardado como ../resources\lang\​es.json​. En
dicho archivo también es permitido agregar nuestras propias traducciones para los
mensajes.

9. Por último se recomienda consultar la ​documentación oficial​ sobre el tema.

Envío de correos

En esta sección se utilizará el formulario de contacto para hacer que nos llegue un correo
cada vez que el usuario envíe los datos del formulario.

1. Iniciamos con la creación de una clase Mailable, un tipo de clase que se utiliza para
representar cada tipo de correo enviado:

Ejecute el comando ​php artisan​ y observe que dentro del ​namespace​ ​make,​ está
el comando ​make:mail (Create a new email class)​. Utilice dicho comando
para crear una clase para representar cada tipo de email enviado por la aplicación.
Ejemplo:

php artisan ​make:mail MensajesRecibidos

El comando anterior creará la clase ../app/​Mail\MensajesRecibidos.php​,

2. En la carpeta ​emails​, cree también una vista para el contenido del correo, llamada
mensajes-recibidos​.

Para este paso debe crear una carpeta ​../resources\views\​emails​ y dentro de ella
crear el archivo ​mensajes-recibidos.blade.php​ con cualquier contenido de prueba.

3. En la clase ../app/Mail/​MensajesRecibidos.php​ modifique el método ​build​ para


indicarle la vista que se debe utilizar para visualizar los correos:

​public​ ​function​ ​build​()​ {


​return​ ​$this​->view(​'​emails.mensajes-recibidos​'​);
}

4. Modifique el controlador ../app/Http/Controllers/​MensajesController.php​ para


implementar el envío de correos:

<?php
namespace​ ​App​\​Http​\​Controllers​;

​ pp​\​Mail​\​MensajesRecibidos​; ​// OJO


use​ A
use​ I​ lluminate​\​Support​\​Facades​\​Mail​;

class​ ​MensajesController​ ​extends​ ​Controller​ {


public​ ​function​ ​store​()​ {

request()->validate([
​'nombre'​ => ​'required'​,
​'correo'​ => ​'required|email'​,
​'asunto'​ => ​'required',
​'contenido'​ => [​'required'​, ​'min:3'​],
], [
​'nombre.required'​ => ​'No acepto anónimos'​,
​'contenido.min'​ => ​'Sea breve pero no tanto'​,
]);
Mail::​to​(​'cc.1591570​@ucaldas.edu.co​'​)

->send(​new​ MensajesRecibidos); ​// OJO


​return​ ​'Mensaje enviado'​; ​// OJO
}
}

5. Lo siguiente es configurar el envío de correos, por ahora probaremos en un archivo


log. Las configuraciones posibles las puede ver en config/​mail.php​, pero no se
recomienda alterar este archivo. En su defecto, abra el archivo ​../.env​ y cambie la
entrada ​MAIL_DRIVER=smtp​ por M ​ AIL_DRIVER=log​.

6. Como en el paso anterior se indica que el driver es log, el correo en vez de enviarse
se guardará en ​../storage/logs/laravel-xxxx.log​.

Pruebe de nuevo a recargar la página de contacto y a pulsar el botón ​enviar​ con


datos correctos. Si todo sale bien, obtendrá en el navegar “mensaje enviado” y en un
archivo log, la estructura del correo recibido. Ejemplo:

[​2018-02-15​ ​12​:​13​:​35​] local.DEBUG:


Message-ID: <cadcce41ba0a29d776cb4f6f7c723919@appxx.test>
Subject: Mensajes Recibidos
From: Example <hello@example.com>
To: carlos.cuesta@ucaldas.edu.co
MIME-Version: ​1.0
Content-Type: text/html; charset=utf​-8
Content-Transfer-Encoding: quoted-printable
7. Para cambiar la cuenta origen resaltada en el punto 6 existen dos formas, primero,
puede modificar los datos del array ​from​ en ​../config/mail.php​. Ejemplo:

​'from'​ => [
​'address'​ => env(​'MAIL_FROM_ADDRESS​'​, ​'​cc@misitio.com​'​),
​'name'​ => env(​'​MAIL_FROM_NAME​'​, ​'Carlos Cuesta'​),
],

O también puede agregar las llaves resaltadas en el archivo ​./.env​, preferiblemente,


justo en la parte que se resalta de la porción de configuración del ejemplo:

MAIL_DRIVER=log
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=cc@misitio.com
MAIL_FROM_NAME='Carlos Cuesta I.'

8. Para cambiar el asunto de los mensajes, simplemente agregue el atributo público


subject​ en la clase ./app/Mail/​MensajesRecibidos.php​. Ejemplo:

<?php

...

class​ ​MensajesRecibidos​ ​extends​ ​Mailable​ {


​use​ ​Queueable​, ​SerializesModels​;

​public​ $subject = ​'Mensaje de contacto'​;

​public​ ​function​ ​__construct​()​ {


​//
}

​public​ ​function​ ​build​()​ {


​return​ ​$this​->view(​'emails.mensajes-recibidos'​);
}
}

Pruebe de nuevo y observe el cambio en el log.

9. Para acceder a los mensajes, hay que realizar algunas adiciones. Empiece por los
cambios que se resaltan en ../Http/Controllers/​MensajesController.php​:
class​ ​MensajesController​ ​extends​ ​Controller​ {

​public​ ​function​ ​store​()​ {

​$​mensaje​ = request()​->validate([
​'nombre'​ => ​'required'​,
​'correo'​ => ​'required|email'​,
​'asunto'​ => ​'required',
​'contenido'​ => [​'required'​, ​'min:3'​],
], [
​'nombre.required'​ => ​'No acepto anónimos'​,
​'contenido.min'​ => ​'Sea breve pero no tanto'​,
]);

Mail::to(​'carlos.cuesta@ucaldas.edu.co'​)->​send​(
new​ MensajesRecibidos(​$mensaje​));

​return​ ​'Mensaje enviado'​;


}

Importante​: se recomienda que en cambio de usar ​send ​para el envío de mensajes,


se use ​queue ​para que los mensajes se procesen en segundo plano y evitar así que
el usuario tenga que esperar a que el proceso termine. El método ​queue,​ requiere
una configuración adicional pero puede hacer de una vez el cambio, ya que si la
configuración no está disponible, ​queue ​utiliza internamente ​send.​

10. Según los cambios del punto 9, también hay que realizar cambios en la clase
../app/Mail/​MensajesRecibidos.php​, para que el constructor de esta clase reciba el
argumento que se está enviando desde el controlador:

11. También la plantilla ../resources\views\emails\​mensajes-recibidos.blade.php


requiere algunos cambios, empecemos por estructurarla así:

<!DOCTYPE html>
<html lang=​"es"​>
<head>
<meta charset=​"UTF-8"​>
<meta name=​"viewport"
content=​"width=device-width, initial-scale=1.0"​>
<meta http-equiv=​"X-UA-Compatible"​ content=​"ie=edge"​>
<title>Mensajes de contactos</title>
</head>
<body>
Contenido
{{ var_dump($​mensaje​) }}
</body>
</html>

12. Si prueba de nuevo, ahora el ../storage\logs\laravelxxxxx.​log​ contendrá el mensaje


enviado por el usuario. Por supuesto se puede dar formato al contenido en la
plantilla ../resources\views\emails\​mensajes-recibidos.blade.php​, teniendo en
cuenta la estructura del array $​mensaje​. Ejemplo:

<!DOCTYPE html>
<html lang=​"es"​>
<head> ... </head>
<body>
<p>Recibiste un mensaje de:
​ nombre'​] }} -
{{ $​mensaje​['
{{ $​mensaje​['​ correo'​] }}
</p>
<p><strong>Asunto:</strong> {{ $​mensaje​[​'asunto'​] }} </p>
<p><strong>Contenido:</strong> {{ $​mensaje​[​'contenido'​] }} </p>
</body>
</html>

13. Una de las formas para visualizar el correo en el navegador consiste en modificar
../app/Http/Controllers/​MensajesController.php​ para que el método ​store()​ retorne
la instancia de la clase MensajesRecibidos. Ejemplo:
​public​ ​function​ ​store​()​ {

$mensaje = request()->validate([
​'nombre'​ => ​'required'​,
​'correo'​ => ​'required|email'​,
​'asunto'​ => ​'required'​,
​'contenido'​ => [​'required'​, ​'min:3'​],
], [
​'nombre.required'​ => ​'No acepto anónimos'​,
​'contenido.min'​ => ​'Sea breve pero no tanto'​,
]);

$​mensajeRecibido​ = ​new​ MensajesRecibidos($mensaje);


Mail::to(​'cc@ucaldas.edu.co'​)
->​queue​($​mensajeRecibido​);
​return​ $​mensajeRecibido​;
}
Probar envíos mediante MailTrap

MailTrap​ proporciona otra forma de probar envíos de correos sin llenar nuestras cuentas:

1. Si no tiene una cuenta en este sitio, créela ahora y realice en dicho sitio, la
configuración básica que se le solicita.

2. En el archivo ​./.env​, actualice las entradas que se resaltan, teniendo en cuenta los
valores proporcionados por ​MailTrap​:

MAIL_DRIVER​=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME​=​06aaaaaaaaaafd
MAIL_PASSWORD​=​a6bbbbbbbbbbb4
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=cc@misitio.com
MAIL_FROM_NAME='Carlos Cuesta I.'

3. Recargue el formulario, realice un nuevo envío y luego de unos segundos, revise su


cuenta de ​MailTrap​ para ver los resultados.

4. Si desea realizar otras pruebas, por ejemplo con una cuenta de Gmail (como su
cuenta de correo de estudiante de la U. de Caldas), puede seguir las indicaciones
que se dan ​aquí​.

5. Antes de continuar adelante se recomienda consultar sobre entornos de desarrollo o


locales vs. entornos de producción y sobre ​variables de entorno​.

Lo esencial de la gestión de base de datos mediante migraciones

Las ​migraciones​ son un mecanismo proporcionado por Laravel que permite el control de
versiones sobre los cambios en la estructura de nuestra base de datos, así las acciones de
crear, modificar o eliminar la estructura de una base de datos será una tarea más sencilla
que si se utiliza un gestor de bases de datos. Miremos de qué se trata:

1. En ../config/database.php, indique el tipo de conexión que se utilizará::

'default' => env('DB_CONNECTION', ​'pgsql'​),

2. La anterior entrada hace que Laravel busque en ​../appxx/.env​ las credenciales de


acceso a base de datos.

DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=tiendadb
DB_USERNAME=postgres
DB_PASSWORD=XXXXXXXXX
Importante​: cada nuevo proyecto, por defecto tiene un archivo ​.env​ con los datos de
configuración necesarios para el mismo, cuando utilizamos un sistema de control de
versiones como GIT, por seguridad, este archivo se excluye del repositorio.

3. Antes de crear la base de datos de pruebas, asegúrese de tener en las variables de


entorno del sistema, específicamente en la variable PATH, la ruta de los ejecutables
de Postgres. En Windows, algo así como:

C:\Program Files\PostgreSQL\11\bin

Sin embargo verifique la ruta exacta en su sistema.

4. Ingrese en la ventana de comandos del sistema la instrucción para crear la base de


datos. Ejemplo (todo en una misma línea):

psql -c "CREATE DATABASE ​nombredb​" "user=postgres dbname=postgres


password=​xxxxxxxxxx​"

Siendo nombredb, el nombre de la nueva base de datos. Obviamente el nombre del


usuario puede variar.

Nota: también puede usar pgAdmin o cualquier otro gestor para llevar a cabo este
paso.

5. Por defecto, Laravel trae dos clases para el manejo de ​migraciones​, una para crear
la tabla de los usuarios y otra para el reinicio de contraseñas.

Toda ​migración​ ​tiene los métodos ​up() y​ ​down().​ Revise la estructura de estas
clases alojadas en ​../database/migrations/ y​ aproveche para familiarizarse con los
métodos que definen los ​tipos de datos​ utlizados, que en realidad son ​alias,​ lo cual
puede comprobar dando control Ctrl+Clic para ver la definición de los mismos.

El método ​app()​ se utiliza para agregar tablas, columnas o índices a la base de


datos. Mientras que el método ​down() ​se utiliza para revertir las acciones realizadas
con ​up()​.

6. Una vez creadas las migraciones hay que ir a la ventana de comandos y sobre la
carpeta del proyecto ejecutar el comando:

php artisan ​migrate

Deberá obtener un mensaje indicando que las tablas de migración se crearon


exitosamente, lo cual puede corrobarar sobre un gestor de bases de datos.

Adicional a las tablas creadas, también se crea una tabla ​migrations​ que guarda el
registro de las migraciones ejecutadas.

7. Para ejecutar el método ​down()​ se ejecuta el comando:


​php artisan migrate:​rollback

Al ejecutar este comando puede verificar en la tabla ​migrations​ que las filas
correspondientes a la creación realizada en el paso 6, fueron eliminadas.

8. Se recomienda que experimente con las variantes documentadas en el sitio oficial


para ​revertir las migraciones​. Por ejemplo, en dicha documentación se indica que la
forma más rápida de quitar todas las tablas y volver a ejecutar todas las migraciones
es utilizando el comando:

php artisan ​migrate:fresh

Tenga en cuenta que este comando eliminará la información de la base de datos.

Aquí​ encuentra un buen resumen de este tema.

Actualizar la estructura de una tabla sin afectar la información

Suponga que la tabla de usuarios ya tiene datos y que se requiere agregar el teléfono a la
misma. Para ello es necesario crear una migración adicional que permita hacer el cambio:

1. Ejecute el comando:

php artisan ​make:migration​ add_phone​_to_​users_table

2. Realice los siguientes cambios en el archivo que se crea en ../database/migrations/,


concretamente en los siguientes métodos, para incluir las instrucciones de adición o
eliminación del nuevo campo:

​public​ ​function​ ​up​()​ {


Schema::table(​'users'​, ​function​ ​(Blueprint $table)​ {
​// $table->string('phone')->default('0'); // valor por defecto
​// $table->string('phone')->nullable(); // permitiendo nulos
$table->string(​'phone'​)->after(​'email'​)->nullable();
// after no funciona para Posgres
});
}

​public​ ​function​ ​down​()​ {


Schema::table(​'users'​, ​function​ ​(Blueprint $table)​ {
$table->dropColumn(​'phone'​); ​// eliminar la columna
});
}

3. Ejecute de nuevo:
php artisan migrate

De esta manera, se agregará un nuevo campo y la información de la tabla se


preservará.

Si se desea que el campo se agregue luego de una columna específica (sólo MySQL
por lo que veo) se puede utilizar la sintaxis del siguiente ejemplo:

$table->string('phone')->​after​('email')->nullable();

4. Efectúe un rollback para eliminar el campo recién creado:

php artisan migrate:rollback

Veriifque la estructura de la tabla y si lo desea vuelva a ejecutar la migración, esta


vez indicando la posición donde debe agregarse el campo.

5. Cree una nueva migración para agregar la tabla “productos”. Ejemplo:

php artisan make:migration create_​productos​_table

6. Seleccione de ​../database/migrations/​ la migración recién creada e indique los


campos que debe tener la tabla:

​public​ ​function​ ​up​()​ {


Schema::create(​'productos'​, ​function​ ​(Blueprint $table)​ {
$table->bigIncrements(​'id'​);
$table->string(​'nombre'​)->unique();
$table->decimal(​'precio'​)->nullable();
$table->decimal(​'iva'​)->default(0);
$table->integer(​'cantidad_disponible'​);
$table->integer(​'cantidad_minima'​);
$table->integer(​'cantidad_maxima'​);
$table->timestamps();
});
}

7. Ahora ejecute el comando que actualiza la estructura de la base de datos


completamente:

php artisan migrate:fresh

Tenga en cuenta que este comando re-crea toda la estructura de la base de datos.

8. Por último, use un gestor de base de datos para observar la estructura creada.

Importante​:
● Al utilizar el comando ​php artisan make:migration nombre_archivo​, asegúrese
de elegir un nombre representativo que luego le ayude a recordar lo que hace. Esto
facilitará cualquier consulta que requiera sobre la tabla ​migrations​ que se crea en las
bases de datos con el fin de gestionar las migraciones.

● Si el nombre dado a una migración tiene la palabra “​create_​” al principio y al final la


palabra opcional ​“_table”​, Laravel crea una migración con el código para crear la
tabla.

● Cuando se incluye el patrón ​“​_to_​”​ en el nombre de una migración, Laravel detecta


que lo que se quiere es modificar una tabla existente y se crea una clase cuyo
nombre incluye la fecha de creación y el nombre dado. Ejemplo:

​../database/migrations/2018_03_27_040953_add_phone​_to_​users_table.php

Observe que la clase para modificar, difiere en su implementación de las utilizadas


para crear tablas. Otra opción para crear tablas es :

php artisan make:migration nombre_migracion ​--create=nombre_tabla

● Y para modificar tablas es:

php artisan make:migration nombre_migracion ​--table=nombre_tabla

Un ejemplo más de actualizar la estructura de una tabla sin afectar la información

Suponga que la tabla de ​users​ ya tiene datos y que se requiere agregar la dirección a dicha
tabla.

1. Ejecute el siguiente comando para crear una clase que permita agregar o quitar
campos de una tabla:

php artisan make:migration add_address​_to_​users_table ​--table=users

Note que siempre se intenta dar un nombre lo más descriptivo posible.

2. Al archivo ​aaaa_mm_dd_##_add_address​_to_u ​ sers_table.php​ que se crea en


../database/migrations/, modifíquele los métodos up() y down() como se muestra
enseguida:

​public​ ​function​ ​up​()​ {


Schema::table(​'users'​, ​function​ ​(Blueprint $table)​ {
$table->string(​'address'​)->nullable();
});
}

public​ ​function​ ​down​()​ {


Schema::table(​'users'​, ​function​ ​(Blueprint $table)​ {


$table->dropColumn(​'address'​);
});
}

3. Ejecute el comando:

php artisan migrate

Si consulta la tabla ​users ​notará que las modificaciones en la estructura se realizan


sin afectar los datos.

4. También compruebe que el campo se elimina cuando se hace un rollback

php artisan migrate:rollback

Para mayor claridad, la gráfica siguiente muestra la estructura con la que debe
quedar la tabla ​users:

En la gráfica se aprecia el nuevo campo ​phone​ y se resalta el campo


remember_token,​ ​este último relacionado con el tema de ​sesiones​, puesto que,
como podemos ver en la ​documentación oficial de Laravel​, nos servirá para saber si
queremos que el sistema recuerde nuestros datos y mantenga la sesión abierta.

Nota​: se pueden crear campos de llaves primarias de otros tipos, con la sintaxis:

$table->tipo('nombre_campo', ...)->primary();​ ejemplo:

$table->string('code', 30)->primary();

Este y otros temas como las ​llaves foráneas​ y​ los ​índices compuestos​, están ampliamente
ilustrados en la ​sección de migraciones de la documentación oficial​ y sería conveniente que
practicara sobre dichos conceptos. La sugerencia es la siguiente:

● Tome como referente este ​modelo de base de datos​ para practicar migraciones.
● Empiece por las tablas de color verde, continúe luego con las de color amarillo,
hasta llegar a las de color naranja.

● Revise en pgAdmin o en cualquier otro gestor de Postgres los resultados que vaya
obteniendo.

La primera prueba de acceso a los datos de una tabla

Antes de continuar adelante estudiando aspectos del ORM de Laravel, se realizarán


algunas pruebas para verificar que las extensiones para manipulación de datos desde PHP
en Postgres, están disponibles.

Para cumplir esta fase de prueba, siga los siguientes pasos:

1. Aunque no es absolutamente necesario, debería trabajar sobre una copia del


proyecto que se viene implementando. Esto tiene dos ventajas: a) en caso de fallos
graves puede recurrir a la versión anterior y b) mantiene un historial del curso.

2. Desde el gestor de Postgres, agregue manualmente unos tres registros a la tabla de


productos:

INSERT​ ​INTO​ productos(


nombre, precio, iva, cantidad_disponible, cantidad_minima,
cantidad_maxima, created_at, updated_at) ​VALUES
(​'Doritos'​, ​1000​, ​0.16​, ​10​, ​3​, ​20​, ​'2019-06-06 13:01:25'​, ​'2019-06-06 13:01:25'​),
(​'De Todito'​, ​1100​, ​0.16​, ​15​, ​3​, ​20​, ​'2019-06-06 13:01:26'​, ​'2019-06-06 13:01:26'​),
(​'Green Tea'​, ​1200​, ​0.16​, ​12​, ​3​, ​20​, ​'2019-06-06 13:01:27'​, ​'2019-06-06 13:01:27'​);

3. Verifque que PHP tiene asociadas las extensiones para trabajar con Postgres:
4. Actualice el archivo ../app/Http/Controllers/CatalogoController.php para que quede
con un único método index() implementado de la siguiente manera:

​public​ ​function​ ​index​()​ {


$catalogo = ​\DB​::table(​'productos'​)->​get​();
​return​ view(​'catalogo'​, compact(​'catalogo'​));
}

En el ejemplo se utiliza la instrucción ​DB::tabla​ indicando el nombre de la tabla


utilizada para realizar la consulta y, seguidamente se llama al método ​get()​ para
obtener todas las filas de la tabla en formato de objeto stdClass y no en formato de
array como se venía probando hasta ahora. Más información al respecto ​aquí​.

Importante​: recuerde que la notación ​\DB​ indica que se está importando la clase DB
y que esto es equivalente a la instrucción ​use DB;​ agregada antes de la cabecera
de la clase que se está implementando.

5. Puesto que las filas de la tabla se obtienen como objetos de tipo stdClass, hay que
hacer un cambio en ​../resources/views/catalogo.blade.php​ para acceder a las
propiedades de los objetos. En el código que se incluye enseguida se resalta el
cambio realizado. Basicamente hay que tener en cuenta que para acceder a los
elementos de un array asociativo se utiliza la notación ​$nombreArray['item'] ​y
que para acceder a las propiedades de un objeto se usa la notación
$nombreObjeto->propiedad​:

@extends(​'plantilla'​)

@section(​'titulo'​)
Catálogo
@endsection

@section(​'contenido'​)
<h1>Catálogo de productos</h1>

<ul>
@​isset​ ($catalogo)
@forelse($catalogo ​as​ $itemCatalogo)
<li> {{ ​$itemCatalogo->nombre​ }} </li>
@​empty
<li>No hay productos para mostrar</li>
@endforelse
@​else
<li>Catálogo no definido</li>
@endisset

</ul>

@endsection
Por supuesto también puede visualizar los múltiples campos de cada fila:

{{ $itemCatalogo->id }}-{{ $itemCatalogo->nombre }}

Antes de continuar adelante con otros talleres, se sugiere que consulte sobre ​arquitectura
REST​. También es importante que consulte sobre las fortalezas y debilidades de la
implementación REST con Query Builder y con Eloquent. En particular, recomiendo ​esta
discusión​ al respecto.

Compruebe que ha quedado claro el tema de las migraciones:

1. Cree una migración que permita crear el esquema para la tabla “mensajes”.

2. Permita que la migración recién creada, al ejecutarse, agregue a la base de datos


una tabla “mensajes” con los campos que Laravel incluye por defecto más los
siguientes, en su orden:

Nombre del campo Tipo


nombre string
email string
asunto string
contenido text

Como puede ver, dichos campos, corresponden a los del formulario que se utilizó
para el envío de correos en un taller anterior.

3. Ejecute la migración y verifique que obtiene una estructura como la siguiente.

Nota​: para continuar con el siguiente taller, se requiere que haya llevado a cabo estos
pasos.

Implementacion de arquitectura REST con Query Builder

Se conoce como ​RESTful​ a los servicios web que ejecutan la arquitectura REST. Para
empezar, como siempre, la recomendación es:

a. Crear una copia del proyecto anterior y trabajar sobre la copia.


b. Tener claro qué es un CRUD,
c. Saber cuáles son las cuatro formas posibles de enviar peticiones a un servidor y
d. Comprender cuáles y que papel desempeñan las siete acciones que se deben crear
para el funcionamiento correcto de un controlador que implemente REST.

Si le asalta alguna duda sobre estos tres asuntos, lo mejor es que consulte ​este articulo
antes de dar el primer paso.

1. Si existe el archivo ..app\Http\Controllers\MensajesController.php, elimínelo, para


refrescar algunas cositas…

2. Ejecute el comando:

php artisan make:controller ​MensajesController


Como puede ver, se crea un archivo con una plantilla de clase que extiende a la
clase Controller. En una plantilla como esta, sería posible implementar manualmente
los siete métodos necesarios para la implementacion REST, sin embargo existe el
comando Artisan que hace dicho trabajo, así que vuelva a eliminar el controlador y
continúe con el siguiente paso.

3. Consulte la ayuda referente al comando make:controller para conocer las opciones


disponibles:

php artisan -h make:controller

4. De la lista proporcionada por la ayuda, nos sirve la opción ​-r​ o ​--resource​ que
genera un clase controladora RESTful con los siete métodos necesarios para la
implementación de un recurso, entendiendo recurso como la “información” (página,
sección, archivo, tabla, ...) a la que se quiere acceder. Así que ejecute el comando:

php artisan make:controller MensajesController --resource

Comprueba que efectivamente se crea de nuevo el controlador, ahora con los siete
métodos RESTful, debidamente parametrizados y con las dependencias necesarias.

En las columnas 3 y 4 de la siguiente tabla se resume lo que se pretende realizar


con dichos métodos:

RESTful
HTTP URL/Ruta Acción del controlador Respuesta

GET /mensajes MensajesController@​index Todos los mensajes

Formulario de
GET /mensajes/crear MensajesController@​create
creación
Guarda mensajes y
POST /mensajes MensajesController@​store
redirecciona
Un mensaje
GET /mensajes/{id} MensajesController@​show
específico

GET /mensajes/{id}/editar MensajesController@​edit Formulario de edición

PUT/
Actualiza mensajes y
PATC /mensajes/{id} MensajesController@​update
redirecciona
H
Elimina mensajes y
DEL /mensajes/{id} MensajesController@​destroy
redirecciona
5. En el archivo ..\routes\web.php, cree una ruta con nombre que responda a la URL
/mensajes/create, de la segunda fila de la tabla:

<?php

Route::view('/', 'inicio', ['nombre' => 'Carlos'])->name('inicio');


Route::view('/acercade', 'acercade')->name('acercade');
Route::get('/catalogo', 'CatalogoController@index')->name('catalogo');

Route::​get​(​'​mensajes/crear​'​, ​'​MensajesController@​create​'​)
->​name​(​'​crear-mensaje​'​);

6. Cree la carpeta ..resources\views\​mensajes​\, arrastre a ella la vista


contacto.blade.php​ y cambie el nombre de la vista por ​crear.blade.php​.

7. Implemente el método create de ..\app\Http\Controllers\MensajesController.php para


que retorne la vista crear.blade.php:

​public​ ​function​ ​create​()​ {


​return​ view(​'mensajes.crear'​);
}

8. Actualice en ..\resources\views\partials\nav.blade.php el enlace que abre el


formulario de contacto, para que quede apuntando a la ruta del paso 5.

<nav>
<li class="{{ seleccionado('inicio') }}">
<a href="/">Inicio</a>
</li>

<li class="{{ seleccionado('acercade') }}">


<a href="acercade">Acerca de...</a>
</li>

<li class="{{ seleccionado('catalogo') }}">


<a href="catalogo">Catálogo</a>
</li>

​ ​ ​<li class=​"{{ seleccionado('crear-mensaje') }}"​>


<a href=​"{{ route('crear-mensaje') }}"​>​Contacto​</a>
</li>
</nav>
Observe que se utiliza el nombre dado a la ruta en el paso 5 y no la URL, esto por
razones ya documentadas.

Con esto ya ha sido creado el método REST correspondiente a la segunda fila de la


tabla. Compruebe que el ​formulario de creación​ se abre correctamente.

Continuamos con la tercera fila de la tabla del paso 4, es decir, guardar y


redireccionar.

9. Se requiere la dependencias para el manejo de bases de datos y una dependencia


para el manejo de fechas en la clase ..\app\Http\Controllers\MensajesController.php,
entonces agregue al principio la instrucción:

use DB;
use Carbon\Carbon;

10. Agregue una nueva ruta en ..\routes\web.php que corresponda al guardado y la


redirección:

Route::post(​'mensajes'​, ​'MensajesController@store'​)
->name(​'guardar-mensaje'​);

11. Hay que actualizar la acción del formulario ..\resources\views\crear.blade.php, con la


nueva ruta creada, así que proceda como se indica con el resaltado:

​<form method=​"post"​ action=​"{{​ ​route('guardar-mensaje')​ }}​"​>


@csrf
​...
​<button>​Enviar​</button>
​</form>

12. Implemente el método ​store​ para guarde en la tabla “mensajes” los datos del
formulario y por ahora, simule una redirección:

​public​ ​function​ ​store​(Request $request)​ {


​// Guardar el mensaje
DB::table(​'mensajes'​)->insert([
​'nombre'​ => $request->input(​'nombre'​),
​'email'​ => $request->input(​'correo'​),
​'asunto'​ => $request->input(​'asunto'​),
​'contenido'​ => $request->input(​'contenido'​),
​'created_at'​ => Carbon::now(),
​'updated_at'​ => Carbon::now(),
]);
​// redireccionar. Más adelante se le pedirá que cambie esto
​return​ ​"Hecho"​;
}

Pruebe la dirección ​appxx.test/mensajes/crear​ que debería mostrar un formulario de


contacto, con la posibilidad de guardar los datos introducidos al pulsar clic sobre el
botón “enviar”.

Continuamos con la implementación de la funcionalidad para mostrar todos los


mensajes de acuerdo a lo especificado en la primera fila de la tabla del punto 4.

13. Cree una nueva ruta en ..\routes\web.php para mostrar todos los mensajes:

Route::get(​'mensajes'​, ​'MensajesController@index'​)
->name(​'ver-mensajes'​);

14. Cambie la instrucción ​return​ en el método ​store​ del controlador por la versión que se
incluye enseguida, que redirecciona a la ruta creada en el punto anterior:

return​ redirect()->route(​'ver-mensajes'​);

15. Dentro de la carpeta ​..resources\views\​mensajes\​ ,​ cree la vista ​index.blade.php​, que


se encargará de mostrar todos los registros de la tabla.

16. Para mostrar todos los registros de la tabla, el método index del constructor que se
viene implementando, debe acceder a los registros de la tabla “mensajes” y pasarlos
a la vista index. Así que implemente dicho método como se muestra enseguida:

​public​ ​function​ ​index​()​ {


$mensajes = DB::table(​'mensajes'​)->get();
​return​ view(​'mensajes.index'​, compact(​'mensajes'​));
}

17. Ahora ya la vista index tiene acceso a los registros de la tabla “mensajes”, entonces
proceda a crear en dicha vista, una tabla que muestre los registros enviados por el
controlador:

@extends(​'plantilla'​)

@section(​'titulo'​, ​'Todos los mensajes'​)


@section(​'contenido'​)

<h1>Todos los mensajes</h1>

<table width=​"100%"​ border=​"1"​>


<thead>
<tr>
<th>Nombre</th>
<th>Correo</th>
<th>Asunto</th>
<th>Contenido</th>
<th>Acciones</th>
</tr>
</thead>

<tbody>
@​foreach​ ($mensajes ​as​ $mensaje)
<tr>
<td>{{ $mensaje->nombre}}</td>
<td>{{ $mensaje->email}}</td>
<td>{{ $mensaje->asunto}}</td>

<td>{{ $mensaje->contenido}}</td>
<td>Editar Eliminar</td>
</tr>
@​endforeach
</tbody>
</table>

@endsection

Pruebe el funcionamiento ​(appxx.test/mensajes)​ y continúe con el siguiente paso


que implementa la fila 4 de la tabla RESTful, para mostrar un mensaje específico.

18. Agregue a ..\routes\web.php la ruta correspondiente a la fila 4:

Route::get(​'mensajes/{id}'​, ​'MensajesController@show'​)
->name(​'buscar-mensaje'​);

19. Dentro de la carpeta ​..resources\views\​mensajes\​ ,​ cree la vista ​show.blade.php,​ que


se encargará de mostrar un registro.
20. En el controlador implemente el método show, como se muestra enseguida:

public​ ​function​ ​show​($id)​ {


$mensaje = DB::table(​'mensajes'​)->where(​'id'​,
$id)->first();
​return​ view(​'mensajes.index'​, compact(​'mensaje'​));
}

21. Implemente la vista ..\resources\views\mensajes\show.blade.php con lo básico para


mostrar la información de un registro:

@extends(​'plantilla'​)
@section(​'titulo'​, ​'Mensaje'​)
@section(​'contenido'​)
<h1>Mensajes</h1>
<p> Enviado por {{ $mensaje->nombre }} -
{{ $mensaje->email }}</p>
<p> {{ $mensaje->contenido }} </p>

@endsection

22. Pruebe la implementación, dando como argumento de la ruta un id de un registro


existente. Ejemplo: appxx.test/mensajes/​3​).

23. En ..\resources\views\mensajes\index.blade.php, realice un pequeño ajuste para


convertir las celdas con nombres en enlaces que lleven a la vista del paso 21.

@​foreach​ ($mensajes ​as​ $mensaje)


<tr>
<td>
<a href=​"{{ route('buscar-mensaje', $mensaje->id) }}"​>
{{ $mensaje->nombre}}
</a>
</td>
<td>{{ $mensaje->email}}</td>
<td>{{ $mensaje->asunto}}</td>
<td>{{ $mensaje->contenido}}</td>
</tr>
@​endforeach

Verifique la funcionalidad y continúe con la fila 5 de la implementación RESTful que


hace referencia a editar un mensaje.
24. Cree en ..\routes\web.php una nueva ruta que lleve a la opción de edición:

Route::get(​'mensajes/{id}/​editar​'​, ​'MensajesController@​edit​'​)
->name(​'editar-mensaje'​);

25. Implemente en el controlador el método encargado de la edición:

​public​ ​function​ ​edit​($id)​ {


$mensaje = DB::table(​'mensajes'​)->where(​'id'​, $id)->first();
​return​ view(​'mensajes.editar'​, compact(​'mensaje'​));
}

26. Cree la vista ..\resources\views\mensajes\​editar​.blade.php, que corresponde a la


vista que retorna el método ​edit​ implementado en el punto anterior:

@extends(​'plantilla'​)

​ Mensaje'​)
@section(​'titulo'​, '

@section(​'contenido'​)
<h1>Mensaje</h1>

<form method=​"post"​ action=​"​{{ ​route(​'actualizar-mensaje'​)​ }}​"​>


@csrf
<input name=​"nombre"​ placeholder=​"Nombre..."
value=​"{{ $mensaje->nombre }}"​><br>
{!! $errors->first(​'nombre'​, ​'<small>:message</small>'​) !!} <br>

<input type=​"email"​ name=​"correo"​ placeholder=​"Correo..."


value=​"{{ $mensaje->email }}"​><br>
{!! $errors->first(​'correo'​, ​'<small>:message</small>'​) !!} <br>

<input name=​"asunto"​ placeholder=​"Asunto..."


value=​"{{ $mensaje->asunto }}"​><br>
{!! $errors->first(​'asunto'​, ​'<small>:message</small>'​) !!} <br>

<textarea name=​"contenido"​ placeholder=​"Contenido..."​>


{{ $mensaje->contenido }}
</textarea><br>
{!! $errors->first(​'contenido'​, ​'<small>:message</small>'​) !!}
<br>
<button>Enviar</button>
</form>
@endsection
Como se resalta, el formulario de edición envía los datos por el método PUT y el
controlador los procesa mediante el método update. Puesto que los navegadores no
implementan el método PUT, para que Laravel sepa que los datos se envían por
PUT, se utilizan en el formulario el helper method_field con el argumento “PUT”.

Con esto termina la implementación de la fila 5 de la tabla que documenta el


RESTful y puede pasar a implementar lo correspondiente a la fila 6 de dicha tabla.

27. La fila 6 de la tabla RESTful, “actualizar mensajes y redireccionar” es una acción que
se implementa en el método update del controlador, de la siguiente manera:

​public​ ​function​ ​update​(Request $request, $id)​ {


DB::table(​'mensajes'​)->where(​'id'​, $id)->update([
​'nombre'​ => $request->input(​'nombre'​),
​'email'​ => $request->input(​'correo'​),
​'asunto'​ => $request->input(​'asunto'​),
​'mensaje'​ => $request->input(​'contenido'​),
​'updated_at'​ => Carbon::now()
]);
​return​ redirect()->route(​'ver-mensajes'​);
}

Ya puede comprobar la implementación de la edición/actualización. Ejemplo:

appxx.test/mensajes/​6​/editar

Ahora sólo queda implementar la última fila de la tabla, que es lo correspondiente a


eliminar mensajes.

28. En ..\routes\web.php agregue la ruta para eliminar mensajes:

Route::delete(​'mensajes/{id}'​, ​'MensajesController@destroy'​)
->name(​'eliminar-mensaje'​);
29. Implemente en el controlador el método para eliminar mensajes:

​public​ ​function​ ​destroy​($id)​ {


DB::table(​'mensajes'​)->where(​'id'​, $id)->delete();
​return​ redirect()->route(​'ver-mensajes'​);
}

30. Por último actualice ..\resources\views\mensajes\index.blade.php para que en la


última columna de la tabla:
a. El texto “Editar” se convierta en un enlace que despliega el formulario de
edición para el registro en curso.
b. El texto “eliminar” se convierta en botón que permita eliminar el registro en
curso.

<td>
<a href=​"{{ route('editar-mensaje', $mensaje->id) }}"​>
Editar</a>

<form style=​"display:inline"
method=​"POST"
action=​"{{ route('eliminar-mensaje',
$mensaje->id) }}"​>
@csrf
{!! method_field(​'DELETE'​) !!}
<button type=​"submit"​>Eliminar</button>
</form>
</td>

 
Eloquent - El ORM de Laravel

Mediante ​Eloquent​, Laravel puede mapear los datos que se encuentran en la base de
datos a objetos de PHP y viceversa (Mapeo Objeto-Relacional - ORM). Esto permite
elaborar un código portable en el que no hace falta usar SQL dentro de las clases de PHP.

Como todo el Laravel, Eloquent usa ​namespaces​ y puede usar la especificación ​PSR-4​ para
facilitar la carga de dependencias, aspecto que cobra mucha importancia de aquí en
adelante, dado que ​Eloquent funciona a partir de modelos​ que facilitan el trabajo con tablas.

En los siguientes pasos se estudian algunos aspectos de Eloquent (app19):

1. Inicialmente, dele un vistazo a las opciones del comando make:model:

php artisan make:model -h

2. Seguidamente, cree una clase, un modelo que represente la tabla productos:

php artisan make:model Producto -m

Aunque ya se tiene la migración creada, se puede ganar tiempo utilizando el


parámetro -m que también crea un archivo de migración para el modelo a crearse.
Para el caso del ejemplo el archivo de migraciones creado, es algo así como:
../database/migrations/​2018_03_04_151647_create_productos_table.php

Y el modelo creado ​(../app/Producto.php)​ es una simple clase con el nombre dado:

<?php
namespace​ ​App​;
use​ ​Illuminate​\​Database​\​Eloquent​\​Model​;

class​ ​Producto​ ​extends​ ​Model​ {


​//
}

Es de anotar que con esta clase, Eloquet referencia automáticamente a una tabla
cuyo nombre se corresponde con el nombre del modelo, pero en minúsculas y en
plural, en nuestro caso: “productos”.

En caso de que la convención para el nombre de la tabla no funcione, defina un


atributo $table para indicar el nombre de ésta. Ejemplo:

protected $table = 'nombre_tabla';

Es importante tener en cuenta que los nombres de los modelos no utilizan guiones y
que en su defecto se usa la notación CamelCase.

3. Lo siguiente es adecuar el controlador ​../app/Http/Controllers/CatalogoController.php


para que quede respondiendo de acuerdo a los cambios.

<?php

namespace​ ​App​\​Http​\​Controllers​;
use​ ​App​\​Producto​;
class​ ​CatalogoController​ ​extends​ ​Controller​ {
​public​ ​function​ ​index​()​ {
$catalogo = ​Producto::​get​();
​return​ view(​'catalogo'​, compact(​'catalogo'​));
}
}

4. Si prueba ahora. Todo deberá funcionar como antes.

5. Prueba ahora a obtener las filas en el orden inverso en que fueron insertadas:

$catalogo = Producto::​orderBy​(​'created_at'​, ​'DESC'​)->get();


O si lo prefiere de una forma análoga:

$catalogo = Producto::​latest​()->get();

El método ​latest()​ recibe como argumento el nombre de la columna que se quiere


utilizar para listar en forma descendente y si no se pasa ninguno, utiliza ​create_at​.

6. Pruebe con otras columnas si efectivamente el método ​latest​ trabaja correctamente


con los argumentos que se le envían.

7. Sobre las ​fechas​ es importante anotar que estas son recibidas como instancias de
Carbon​, una librería especializada en manejo de fechas. Pruebe por ejemplo a
modificar el archivo ​../resources/views/catalogo.blade.php,​ para que los elementos
de lista muestren la fecha de creación de las filas:

<li>
{{ $itemCatalogo->id }}-{{ $itemCatalogo->nombre }} <br>
{{ $itemCatalogo->​created_at​ }}
</li>

La librería ​Carbon​ ofrece posibilidades de formatos como:

$itemCatalogo->created_at->​format​('Y')

$itemCatalogo->created_at->​format​('m')

$itemCatalogo->created_at->​format​('Y-m-d')

$itemCatalogo->created_at->​diffForHumans​()

8. En el archivo ​../app/Http/Controllers/CatalogoController.php​ se puede indicar la


paginación​ de resultados que se obtiene utilizando el método ​paginate():​

​public​ ​function​ ​index​()​ {


$catalogo = Producto::latest()->​paginate​(); ​// 15 por página
​return​ view(​'catalogo'​, compact(​'catalogo'​));
}

También es posible incluir un argumento que indique el número de registros por


página que se requiere. Ensaye por ejemplo a listar 2 por página:

$catalogo = Producto::latest()->paginate(​#​); // ​#​ de filas por página

9. El complemento indispensable a la paginación, son los enlaces que permiten


desplazarse por las páginas de registros, esta funcionalidad se puede agregar en el
archivo ​../resources/views/catalogo.blade.php​, mediante el uso del método ​línk():​
@​isset​ ($catalogo)
@forelse($catalogo ​as​ $itemCatalogo)
<li>
{{ $itemCatalogo->id }}-{{ $itemCatalogo->nombre }} <br>
</li>
@​empty
<li>No hay productos para mostrar</li>
@endforelse
{{ ​$catalogo->​links​()​ }}
@​else
<li>Catálogo no definido</li>
@endisset

Como podrá notar son links funcionales pero carentes de un estilo que puede dar
mediante CSS puro o mediante un Framework de Front-End. Más adelante nos
ocuparemos de esa parte.

10. Veamos una forma equivalente de determinar la funcionar del controlador y de la


plantilla, escribiendo de manera resumida el código.

El método index() de​../app/Http/Controllers/CatalogoController.php​ quedaría así:

​public​ ​function​ ​index​()​ {


​// la vista referenciada: 'catalogo.blade.php'
​return​ view(​'catalogo'​, [
​// array asociativo procesado en 'catalogo.blade.php'
​'productos'​ => Producto::latest()->paginate()
]);
}

La plantilla ​../resources/views/catalogo.blade.php,​ sufriría un ligero cambio para


adaparse a los cambios de nombres:

@​isset​ ($​productos​)
@forelse($​productos​ a​ s​ $​producto​)
<li>
{{ $​producto-​>id }}-{{ $​producto-​>nombre }} <br>
</li>
@​empty
<li>No hay productos para mostrar</li>
@endforelse
{{ $​productos-​>links() }}
@​else
<li>Catálogo no definido</li>
@endisset
Consulta de datos específicos con Eloquent

Se recomienda que para continuar tenga instalada en su navegador una extensión para
formatear archivos JSON. Para Chrome, puede usar JSON Formatter, para Mozilla
JSONView. Ah y no olvide que es bueno trabjar sobre una copia del proyecto...

En este taller se mostrará como consultar los datos específicos de cada producto, dando
clic sobre los elementos del listado de productos y haciendo la búsqueda según su
identificador.

1. Modifique en ../resources/views/catalogo.blade.php los elementos de lista para


convertirlos en enlaces:

​@forelse​($productos as $producto)
<li>
<a href=​"{{ ​route​('producto.show', $producto) }}"​>
{{ $producto->nombre }}</a>
</li>
​@empty
<li>No hay productos para mostrar</li>
​@endforelse

Como se puede ver, el enlace llevará a una ruta inexistente aún, llamada
producto.show​. El segundo argumento de la función route() recibe como argumento
la instancia de la iteración actual, correspondiente al producto que se quiere mostrar.

Esto hará que Laravel automáticamente obtenga el ID del producto correspondiente


a dicha instancia y automáticamente genere con dicho ID un enlace para cada
producto. En otras palabras es como si se usara esta instrucción:

$producto->​getRouteKey​()

Si ejecutara la aplicación en este momento se generería un error, indicando que la


ruta aún no ha sido definida:

Route [​producto.show​] not defined. (View: ..\resources\views\catalogo.blade.php)

2. Ahora hay que modificar ../routes/web.php para indicar las rutas especificadas en el
paso 1:

<?php

App::setLocale(​'es'​);
Route::view(​'/'​, ​'inicio'​)->name(​'inicio'​);
Route::view(​'/acercade'​, ​'acercade'​)->name(​'acercade'​);

Route::get(​'/catalogo'​,
'CatalogoController@index'​)->name(​'catalogo.index'​);
Route::get(​'/catalogo/​{id}​'​,
'CatalogoController@​show​'​)->name(​'producto.show'​);

Route::view(​'/contacto'​, ​'contacto'​)->name(​'contacto'​);
Route::post(​'contacto'​, ​'MensajesController@store'​);

Observe que se ha incluido una nueva ruta que recibe un parámetro ​{id}​, dicho
parámetro corresponde al segundo argumento utilizado en el llamado que se hizo en
el paso 1 a la función route().

Tenga en cuenta que el nombre del parámetro no interesa.

El controlador pasado como segundo argumento del método get() sigue siendo
CatalogoController y el método que se va a implementar para mostrar los datos del
producto, es show().

Finalmente, se asigna el nombre ‘producto.show’ a la ruta.

Si prueba la aplicación ahora, deberían generarse los enlaces para cada elemento
del listado de productos, pero al pulsar clic sobre un enlace se generará un error
indicando que el método show() no existe en el controlador:

Method App\Http\Controllers\CatalogoController::​show​ does not exist.

3. Agregue a ../app/Http/Controllers/CatalogoController.php una primera versión del


método que hace falta:

​public​ ​function​ ​show​($id)​ {


​return​ $id;
}

Si prueba ahora a pulsar clic sobre los enlaces, ya al menos se muestra el ID de


cada producto, tanto en la barra de direcciones como en el contenido del navegador.

4. Para ver en formato JSON los datos del producto que corresponden al ID, se puede
llamar all método find() de la clase Producto (recuerde que dicha clase está definida
en ../app/Producto.php). Por lo tanto el método show cambiaría a:

​public​ ​function​ ​show​($id)​ {


​return​ Producto::​find​($id);
}
Y el resultado obtenido al dar clic sobre un enlace, sería algo así como esto:

id​: ​3​,
nombre​: ​"Lipton Green Tea"​,
precio​: ​"1200.00"​,
iva​: ​"0.16"​,
cantidad_disponible​: ​12​,
cantidad_minima​: ​3​,
cantidad_maxima​: ​20​,
created_at​: ​"2018-05-01 13:01:27"​,
updated_at​: ​"2018-05-01 13:01:27"
}

5. La versión siguiente del método show() consiste en utilizar una vista


(show.blade.php) que estará contenida en la subcarpeta
../resources/views/productos/:

​public​ ​function​ ​show​($id)​ {


​return​ view(​'productos.show'​, [
​'producto'​ => Producto::find($id),
]);
}
Observe que lo que se hace en esta versión, es pasar a la vista show.blade.php
(inexistente aún), los datos del producto retornado por el método find().

6. Cree dentro de ../resources/views/ una nueva carpeta llamada “productos”

7. Dentro de ../resources/views/productos/ cree el archivo show.blade.php con una


implementación que permita ver los datos del producto seleccionado. Algo así como
esto:

@extends(​'plantilla'​)

@section(​'titulo'​, ​"Catálogo | $producto->nombre"​)

@section(​'contenido'​)
<h1> {{ $producto->nombre }} </h1>
Precio del producto $ {{ $producto->precio }}
<br>
Cantidad disponible: {{ $producto->cantidad_disponible }}
<br>
Cantidad mínima: {{ $producto->cantidad_minima }}
<br>
Cantidad máxima: {{ $producto->cantidad_maxima }}
<br><br>
@endsection
Pruebe para ver los resultados.

8. Puesto que estamos accediendo por GET, intente visualizar un producto inexistente,
por ejemplo http://appxxxx.test/catalogo/​999

Notará que se produce un error como este:

Trying to get property ​'nombre' ​of non-object (View:


..\resources\views\productos\show.blade.php)

9. La versión final del método show() en ../app/Http/Controllers/CatalogoController.php


queda de la siguiente manera, para indicar una manera de tratar con el error del
paso anterior:

​public​ ​function​ ​show​($id)​ { ​// nombre de método igual a nombre de vista


​return​ view(​'​productos.show​'​, [
​'producto'​ => Producto::​findOrFail​($id),
]);
}

Nota​: si desea modificar la vista que muestra el error 404, proceda de la siguiente
manera:

a. Cree la carpeta ../resources/views/​errors

b. Dentro de /errors, agregue la vista ​404.blade.php

c. Personalice la vista a su gusto. Por ejemplo:

<h1>​No se ha encontrado el recurso solicitado​</h1>


<a href=​"/"​>​Volver a la página inicial​</a>

Reestructurando el proyecto para dar consistencia

Varias veces de aquí en adelante será necesario, reestructurar el proyecto. Si desea


conservar el histórico de cambios recuerde hacer una copia del proyecto y trabajar sobre
ésta porque no hay garantía de que el proyecto quede funcionando, dados todos los
cambios que vienen…

1. En ..\app\Http\Controllers\CatalogoController.php, para dar consistencia a los


nombres lleve a cabo el siguiente cambio en el método index():

​public​ ​function​ ​index​()​ { ​// nombre de método igual a nombre de vista


​return​ view(​'​productos.index​'​, [
​ ​'productos'​ => Producto::latest()->paginate(),
]);
}

Observe que ahora tanto en el método show() como en el método index(), el nombre
del método es análogo al nombre de la vista que devuelve. Esto sin duda alguna
facilita el mantenimiento de la aplicación.

2. Cree el archivo ​..\resources\views\productos\i​ ndex.blade.php.​

3. Copie el código del archivo ​..\resources\views\catalogo.blade.php​ en index.blade.php


y elimine la vista catalogo.blade.php.

Todo debería continuar funcionando como hasta ahora.

4. El otro detalle es que ..\app\Http\Controllers\​CatalogoController.php​ es un


controlador cuyo nombre no es consistente con el nombre de la clase del modelo
..\app\​Producto.php​, así que cambie el nombre de archivo del controlador y también
el nombre de la clase por ​ProductoController​ para que haya relación entre el
nombre de las clases de las capas del modelo y del controlador.

5. Otra recomendación que debe tenerse en cuenta es que los nombre de las clases
deben ser en singular, así que también cambie el nombre del archivo y de la clase
..\app\Http\Controllers\​MensajesController​.php, por ​MensajeController​.

6. En ..\resources\views\productos\​index.blade.php​ actualice la ruta dada en la


referencia:

​<li>
​<a href=​"{{ route('​productos.show​', $producto) }}"​>
{{ $producto->nombre }}
</a>
​</li>

​ eb.php,​ de acuerdo a los


7. Ahora hay que actualizar las referencias en ​..\routes\w
cambios realizados:

<?php

App::setLocale(​'es'​);

Route::view(​'/'​, ​'inicio'​)->name(​'inicio'​);
Route::view(​'/acercade'​, ​'acercade'​)->name(​'acercade'​);

Route::get(​'/catalogo'​,
'​ProductoController​@index​'​)->name(​'productos​.index​'​);
Route::get(​'/catalogo/{id}'​,
'​ProductoController​@show​'​)->name(​'productos​.show​'​);

Route::view(​'/contacto'​, ​'contacto'​)->name(​'contacto'​);
Route::post(​'contacto'​, ​'MensajeController​@store'​)->name(​'mensajes.store'​);

8. Otra inconsistencia que hay hasta el momento es que en el archivo de navegación


no se están utilizando las rutas con nombre, así que es hora de realizar esa
corrección en el archivo ​..\resources\views\partials\​nav.blade.php:​

<nav>
​<li class=​"{{ seleccionado('inicio') }}"​>
<a href=​"{{ ​route('inicio')​ }}"​>​Inicio​</a>
</li>
​<li class=​"{{ seleccionado('acercade') }}"​>
<a href=​"{{ ​route('acercade')​ }}"​>​Acerca de...​</a>
</li>
​<li class=​"{{ seleccionado(​'​productos.index​'​) }}"​>
<a href=​"{{ ​route('productos.index')​ }}"​>​Productos​</a>
</li>
​<li class=​"{{ seleccionado('contacto') }}"​>
<a href=​"{{ ​route('contacto')​ }}"​>​Contacto​</a>
</li>
</nav>

Recuerde que hacer referencia a los nombre de las rutas y no a las URL, permite
cambiar las URL sin afectar la navegabilidad.

​ ontacto.blade.php,​
9. Uno de los cambios del punto 7, afecta a ​..\resources\views\c
donde simplementa hay que actualizar la ruta:

​<form method=​"post"​ action=​{{​ r


​ oute('mensajes.store')​ }}>
@csrf
...
​</form>

Pruebe que todo siga funcionando y pruebe también a cambiar la forma como se
selecciona la siguiente ruta en el punto 8. En este caso se usa un comodín (*) para
indicar que “cualquier subruta” debe mostrarse activa.

​<li class=​"{{ seleccionado(​'​productos.*​) }}"​>

<a href=​"{{ ​route('productos.index')​ }}"​>​Catálogo​</a>

</li>

Enlace de modelos a rutas en Laravel


Laravel permite obtener modelos usando directamente los parámetros de las funciones, sin
necesidad del llamado explícito a métodos de Eloquent como​ find()​ o ​findOrFail(),​ mediante
una técnica conocida como “​Route Model Dinding​”. Veamos como:

1. En ​..\app\Http\Controllers\​ProductoController.php​, modifique el argumento del


método show para que en vez de un ID de producto, reciba una instancia de
Producto, es decir la fila de datos completa.

​public​ ​function​ ​show​(Producto ​$producto​)​ {


​return​ view(​'productos.show'​, [
​'producto'​ => ​$producto
]);
}

Observe que ya no se requiere buscar el producto como se hizo en el punto 1 de la


práctica anterior, sino que simplemente se retorna la vista con la instancia recibida
como argumento.

​ eb.php,​ el argumento, que ya no es


2. Por supuesto, hay que modificar en ​..\routes\w
“id”​ sino que es ​“producto”​. Veamos:

<?php

App::setLocale(​'es'​);
Route::view(​'/'​, ​'inicio'​)->name(​'inicio'​);
Route::view(​'/acercade'​, ​'acercade'​)->name(​'acercade'​);

Route::get(​'/catalogo'​,
'ProductoController​@index​'​)->name(​'productos​.index​'​);
Route::get(​'/catalogo/​{​producto​}​'​,
'ProductoController​@show​'​)->name(​'productos​.show​'​);

...

Si prueba los cambios, todo debería seguir funcioado correctamente dado que mediante
esta técnica se búsca tambien por el ID.

Búsquedas utilizando campos distintos a la llave primaria

Recuerde que la clase ​..\app\​Producto.php​ es una derivación de la clase “Model”. Esta


clase contiene un método llamado ​getRouteKeyName(), ​que por defecto busca por la llave
primaria. Lo que hay que hacer entonces es sobreescribir dicho método en la subclase
Producto:
1. Agregue el método ​getRouteKeyName() a​ la clase Producto, implementado de
manera tal que permita búsquedas por el nombre del producto:

​public​ ​function​ ​getRouteKeyName​()​ {


​return​ ​'nombre'​;
}

2. En la barra de direcciones del navegador, pruebe a ingresar como parámetro un


nombre de producto. Ejemplo:

appxxx.test/catalogo/​Doritos

Lo anterior debería dar como resultado que se muestren en pantalla los datos del
producto dado como parámetro. Hasta la versión anterior, tal búsqueda sólo era
posible si se daba el ID del producto.

3. Si tiene migraciones para la creación de la tabla productos, que ​no tengan​ una
implementación similar a la del punto 4, elimínelas. Sólo debe quedar una como la
del punto 4.

4. Abra ​..\database\migrations\AAAA_MM_DD_122110_create_productos_table.php,​ la
migración que contiene el método que crea la tabla Productos y modifique dicho
método para agregar la nueva columna que se resalta en el código siguiente:

​public​ ​function​ ​up​()​ {


Schema::create(​'productos'​, ​function​ ​(Blueprint $table)​ {
$table->bigIncrements(​'id'​);
$table->string(​'nombre'​);
$table->decimal(​'precio'​);
$table->decimal(​'iva'​);
$table->integer(​'cantidad_disponible'​);
$table->integer(​'cantidad_minima'​);
$table->integer(​'cantidad_maxima'​);
​$table->string(​'url'​)->unique();
$table->timestamps();
});
}

5. Ejecute de nuevo las migraciones:

php artisan migrate:fresh

6. Desde el gestor de Postgres, agregue manualmente varios registros a la tabla de


productos, incluyendo los datos de la columna URL sin espacios:
Nota: en el siguiente taller se indica cómo agregar nuevos productos, tema ya visto,
pero con el fin de introducir conceptos adicionales.

7. Actualice de nuevo ell método ​getRouteKeyName() ​para que permita buscar por la
URL:

​public​ ​function​ ​getRouteKeyName​()​ {


​return​ ​'url'​;
}

8. Pruebe a una búsquede desde la barra de direcciones, basada ésta en una URL.
Ejemplo:

http://appxxx.test/catalogo/de-todito

Todo debería funcionar sin problemas, incluso se muestra correctamente la


informacion de cada producto, al dar clic sobre el enlace de cada uno, aunque antes
las referencias se basaban en el ID del proyecto y ahora se basan en el campo URL.
Esto es posible por la sobreescritura que se hizo del método ​getRouteKeyName()​ y
porque en ​..\resources\views\productos\index.blade.php​, al pasar el objeto completo,
Laravel utiliza el nombre del campo indicado en la sobreescritura del método
getRouteKeyName().​

Tangencialmente esta práctica ha demostrado como crear ​URL amigables​.

Inserción de registros

En este taller se mostrará cómo insertar registros utilizando Eloquent y ​lo más importante​,
utilizando el ​diseño de arquitectura de software REST​.

1. Lo primero es mostrar una vista con el formulario de creación de un proyecto, así


que cree la vista ..\resources\views\productos\​create​.blade.php​. Como hasta ahora,
nos enfocamos en la funcionalidad y más adelante nos enfocaremos en la
presentación:

@extends(​'plantilla'​)
@section(​'titulo'​, ​'Agregar producto'​)
@section(​'contenido'​)

<h1>Agregar nuevo producto</h1>


<form method=​"​POST​"​ action=​"{{ route('​productos.store​') }}"​>


​@csrf
<label>
Nombre del producto <br>
<input type=​"text"​ name=​"nombre"​>
</label>
<br>

<label>
Precio <br>
<input type=​"text"​ name=​"precio"​>
</label>
<br>
<label>
IVA <br>
<input type=​"text"​ name=​"iva"​>
</label>
<br>
<label>
Cantidad disponible <br>
<input type=​"text"​ name=​"cantidad_disponible"​>
</label>
<br>
<label>
Cantidad mínima <br>
<input type=​"text"​ name=​"cantidad_minima"​>
</label>
<br>
<label>
Cantidad máxima <br>
<input type=​"text"​ name=​"cantidad_maxima"​>
</label>
<br>
<label>
URL <br>
<input type=​"text"​ name=​"url"​>
</label>
<br><br>
<button>Guardar</button>
</form>

@endsection
2. Ahora hay que agregar en ..\routes\web.php, una ruta que devuelva la vista creada:

<?php

App::setLocale(​'es'​);

Route::view(​'/'​, ​'inicio'​)->name(​'inicio'​);

Route::view(​'/acercade'​, ​'acercade'​)->name(​'acercade'​);

Route::get​(​'/catalogo'​,
​'ProductoController@index'​)->name(​'productos.index'​);

Route::get​(​'/catalogo/crear'​, ​// ruta para mostrar la vista


'​ProductoController@​create​'​)->name(​'​productos.​create​'​);

Route::post​(​'/catalogo/'​, ​// ruta que gestiona el envío por POST


​'​ProductoController@​store​'​)->name(​'​productos.​store​'​);

Route::get(​'/catalogo/{producto}'​,
​'ProductoController@show'​)->name(​'productos.show'​);

Route::view(​'/contacto'​, ​'contacto'​)->name(​'contacto'​);

Route::post(​'contacto'​,
​'MensajeController@store'​)->name(​'mensajes.store'​);

Importante​: el orden en que se han colocado las rutas resaltadas es fundamental. Si


el orden estuviera invertido, Laravel intentaría encontrar un producto llamado “crear”
y sucedería el error 404.Esto lo puede comprobar invirtiendo las rutas y probando la
aplicación, pero realizada la prueba, vuelva a dejar el código en el orden que se
indica.

3. Lo siguiente es crear en el controlador, el método que se indica en la nueva ruta, así


que realice la adición del método en ..\app\Http\Controllers\ProductoController.php:

​public​ ​function​ ​create​()​ {


​return​ view(​'​producto.create​'​);
}

4. Pruebe que al dar en la barra de dirección, la URL ​appxx.test/catalogo/crear​, el


formulario se despliega correctamente. En cambio si intenta utilizar el botón guardar,
se presentará un error porque el controlador aún no tiene un método ​store()
implementado:

Method App\Http\Controllers\​ProductoController::store
does not exist.
Al inspeccionar los elementos del formulario recordamos que la directiva @csrf,
agrega un campo oculto con un token de validación. Veamos:

5. Agregue en ..\app\Http\Controllers\ProductoController.php el método que responde a


la solicitud POST:

​public​ ​function​ ​store​()​ {


​return​ request(); ​// inspeccionar la petición y retornarla
}

Si prueba nuevamente y pulsa clic sobre el botón “Guardar”, debería mostrarse en el


navegador un JSON con los datos de la solicitud. Ejemplo:

_token​: ​"OswCpnwzbfyAs6Rn0S9TbQrOnftyvhA0FGd0rp5X"​,
nombre​: ​null​,
precio​: ​null​,
iva​: ​null​,
cantidad_disponible​: ​null​,
cantidad_minima​: ​null​,
cantidad_maxima​: ​null​,
url​: ​null
}

En la función store() también se puede indicar como argumento de la función


request() el campo a retornar. Ejemplo:

public​ ​function​ ​store​()​ {


​return​ ​request(​'nombre'​);
}

O si requiere retornarlo como un array:

public​ ​function​ ​store​()​ {


​return​ ​request([​'nombre']​);
}
También, si importa la clase Request:

use​ ​Illuminate​\​Http​\​Request​;

Puede escribir el método request() de esta manera:

​public​ ​function​ ​store​(Request $​request​)​ {


​return​ $​request​;
}

O de esta manera para retornar un campo específico:

​public​ ​function​ ​store​(Request $request)​ {


​return​ ​$request->get(​'nombre'​);
}

En mi caso me parece más fácil el uso de la función request(). Ahora sí miremos


como guardar estos datos en la base de datos.

6. Modifique el método store() para que guarde un registro de productos:

​public​ ​function​ ​store​()​ {


​return ​Producto::​create​([
​'nombre'​ => request(​'nombre'​),
​'precio'​ => request(​'precio'​),
​'iva'​ => request(​'iva'​),
​'cantidad_disponible'​ => request(​'cantidad_disponible'​),
​'cantidad_minima'​ => request(​'cantidad_minima'​),
​'cantidad_maxima'​ => request(​'cantidad_maxima'​),
​'url'​ => request(​'url'​)
]);
}

Al intentar ejecutar la aplicación (URL: appxx.test/catalogo/​crear​), notará que se


produce el siguiente error:

Add [nombre] to fillable property to allow mass assignment on


[App\Producto].

Cuando como en este caso se solicite que agregue un campo a la propiedad


rellenable para permitir la asignación masiva, lo que quiere indicar es que se debe
​ roducto.php​ la propiedad ​fillable​ como un array que
agregar en la clase ​..\app\P
contiene los campos que se puedan insertar masivamente. De acuerdo a esto, la
implementación de la clase queda así:
<?php

namespace​ ​App​;

use​ ​Illuminate​\​Database​\​Eloquent​\​Model​;

class​ ​Producto​ ​extends​ ​Model​ {

​protected​ $​fillable​ = [​'nombre'​, ​'precio'​, ​'iva'​,


​'cantidad_disponible'​,
'cantidad_minima'​, ​'cantidad_maxima'​, ​'url'
];

​public​ ​function​ ​getRouteKeyName​()​ {


​return​ '
​ url'​;
}

Si prueba prueba nuevamente (URL: appxx.test/catalogo/​crear​), se insertará el


nuevo registro en la base de datos.

En este caso la instrucción ​return​ del método ​store()​ es opcional y se usa aquí, sólo
para poder verificar en el navegador, los datos que fueron insertados en la tabla.
Veamos un ejemplo:

{
​"nombre"​: ​"Jet Wafer"​,
​"precio"​: ​"5000"​,
​"iva"​: ​"0.19"​,
​"cantidad_disponible"​: ​"15"​,
​"cantidad_minima"​: ​"5"​,
​"cantidad_maxima"​: ​"20"​,
​"url"​: ​"jet-wafer"​,
​"​updated_at​"​: ​"2019-06-14 15:55:59"​,
​"​created_at​"​: ​"2019-06-14 15:55:59"​,
​"id"​: ​7
}

Como puede ver los datos ​updated_at​ y ​created_at​ se generan automáticamente.

Verifique que efectivamente en el catálogo de productos, se visualicen las nuevas


inserciones (URL: appxx.test/catalogo).
Enseguida se hará una pequeña mejora funcional que permita agregar productos
desde el catálogo y que agregado un producto inmediatamente se pueda consultar
en el catálogo.

7. Añada un enlace en appxx.test/catalogo que permita agregar productos. En


..\resources\views\productos\index.blade.php, realice la adición del enlace necesario
y deje lo demás tal y como estaba:

@extends(​'plantilla'​)

@section(​'titulo'​) Catálogo @endsection

@section(​'contenido'​)

<h1>Catálogo de productos</h1>

​<a href=​"{{ ​route('productos.create')​ }}"​>Agregar producto</a>

<ul>
...
</ul>

@endsection

8. Modifique en ..\app\Http\Controllers\ProductoController.php el método ​store()​ para


que al crear un producto, se redireccio al catálogo:

​public​ ​function​ ​store​()​ {


​Producto::create​([
​'nombre'​ => request(​'nombre'​),
​'precio'​ => request(​'precio'​),
​'iva'​ => request(​'iva'​),
​'cantidad_disponible'​ => request(​'cantidad_disponible'​),
​'cantidad_minima'​ => request(​'cantidad_minima'​),
​'cantidad_maxima'​ => request(​'cantidad_maxima'​),
​'url'​ => request(​'url'​)
]);

​return​ redirect()->route(​'productos.index'​);
}

Importante​: cuando los datos que llegan al controlador tienen los mismos nombres
que las columnas de la tabla, puede evitarse la asignación uno a uno como se hizo
en esta versión y en su lugar utilizar el método all() del objeto request para hacer la
asignación masiva​. Ejemplo:
​public​ ​function​ ​store​()​ {
Producto::create(​request()->​all​()​);
​return​ redirect()->route(​'productos.index'​);
}

Recuerde entonces que la ​asignación masiva​ consiste en enviar una matriz a la creación del
modelo, básicamente estableciendo un grupo de campos en el modelo de una sola vez, en
lugar de uno por uno.

Aspectos complementarios de la asignación masiva

Como se dijo en el taller anterior, el atributo ​fillable​ permite seleccionar el conjunto de


columnas que se utilizan en la inserción de registros. Según esto, no importa el número de
campos que se envíen desde la capa de presentación, sólamente se utilizan los definidos
en dicha propiedad.

Esto evita que un usuario pueda modificar el formulario y enviar un campo adicional que no
se quiere editar. Esto lo puede hacer un usuario accediendo al código fuente de la página y
editando el HTML.

Además de la propiedad ​fillable​ existe la propiedad ​guarded​ que hace lo opuesto de ​fillable,​
es decir que en el array que se asigne se pueden agregar los campos que no se desean
utilizar masivamente. Ejemplo:

$protected $​guarded​ = ['id', 'created_at', 'updated_at'];

Es decir que cualquier campo inexistente en este array, podrá ser utilizado en la inserción.

La protección dada por la acción masiva se puede deshabilitar si se hace una asignación
como esta: ​$protected $​guarded​ = [];​ siempre y cuando se implemente otra protección.

Puesto que el problema de protección se da cuando se pasa todo el ​request:


request()->​all​()​, una solución sería pasar sólo los campos requeridos. Ejemplo:

Producto::create(request()->only('campo1', 'campo2', .. 'campoN'));

Otra forma de solución muy óptima, ya vista por cierto, es utilizar la ​validación​ que
automáticamente pasa sólo el array de campos validados. Ejemplo:

public​ ​function​ ​store​()​ {


$campos = request()->validate([
​'nombre'​ => ​'required'​,
​'precio'​ => ​'required'​,
​'cantidad_disponible'​ => ​'required'
...
]);
Producto::create($campos);
​return​ redirect()->route(​'productos.index'​);
}
Otra de las ventajas de esta técnica es que se evita el tener que actualizar la propiedad
fillable ​o ​guarded​ en las clases del modelo (..\app\ClaseX.php).

Conclusión​: se puede deshabilitar la protección que viene por defecto en Laravel, siempre
y cuando no se use ​request()->all()​.

El Ejemplo de Gestión de Mensajes con Eloquent

Este taller retoma los conceptos de REST y RESTful, tratados en el taller “​Implementacion
de arquitectura REST con Query Builder”, pero vistos desde la óptica de lo ya dicho de
Eloquent, por tanto, se recomienda que trabaje sobre una copia de dicho proyecto.

1. Verifque que su proyecto tenga las siguientes rutas definidas, que corresponden a la
arquitectura REST.

Puede tener más, pero lo importante es que incluya las 7 rutas que muestra la figura.

2. Comente o elimine dichas rutas en el archivo ..\routes\web.php y en su lugar incluya:

Route::resource(​'mensajes'​, ​'MensajesController'​);

Observará que obtiene un listado de rutas muy similar al anterior, salvo las rutas con
nombres y otras dos pequeñeces.

3. Utilice el comando “buscar y reemplazar en archivos” (en VSC ^Shift+H) para


reemplazar las incidencias relacionadas con rutas, dado el cambio en el paso 2:

Buscar Reemplazar por


ver-mensajes mensajes.index
guardar-mensaje mensajes.store
crear-mensaje mensajes.create
buscar-mensaje mensajes.show
actualizar-mensaje mensajes.update
eliminar-mensaje mensajes.destroy
editar-mensaje mensajes.edit

Pruebe que realizada la limpieza, la aplicación sigue funcionando correctamente.

4. Cree un clase-modelo para la tabla Mensajes. Recuerde seguir la convención de


definir los modelos en singular y las tablas en plural:

php artisan make:model ​Mensaje

Comprueba la creación del archivo ..\app\Mensaje.php correspondiente.

5. En ..\app\Http\Controllers\MensajesController.php importe la clase Mensaje creada


en el punto anterior:

use App\Mensaje;

6. Actualice el método index como se indica enseguida:

​public​ ​function​ ​index​()​ {


$mensajes = Mensaje::all();
​return​ view(​'mensajes.index'​, compact(​'mensajes'​));
}

Compruebe que la appxx.test/mensajes continúa mostrando la tabla con mensajes


correctamente.

7. Agregue en la clase ..\app\Mensaje.php, la propiedad ​fillable​ para indicar los campos


que pueden ser editados:

protected $fillable = ['nombre', 'email', 'asunto', 'contenido'];

8. El método store del controlador puede implementarlo de una de dos maneras. La


primera:

​public​ ​function​ ​store​(Request $request)​ {


Mensaje::create([
​'nombre'​ => $request->input(​'nombre'​),
​'email'​ => $request->input(​'correo'​),
​'asunto'​ => $request->input(​'asunto'​),
​'contenido'​ => $request->input(​'contenido'​)
]);
return​ redirect()->route(​'mensajes.index'​);

}
Como puede ver, se pasan uno a uno los campos y no requiere pasar las fechas de
creación y actualización.
La segunda forma es asumiendo que desde el formulario se envían todos los datos
necesarios para crear el registro:

Mensaje::create($request->all());

Perilita​: existe una función ​dd()​ (“Dump and Die” / “volcar y morir”)​ en Laravel que le
puede ayudar en la depuración. Llame esta función en una línea entre los métodos
create() y redirect(), para inspeccionar el contenido del argumento $request:

dd($request->all());

Observe los resultados en el navegador al pulsar clic en el botón “Enviar”.

9. Continuamos con el cambio en el método show del controlador:

​public​ ​function​ ​show​($id)​ {


$mensaje = Mensaje::​findOrFail​($id);
​return​ view(​'mensajes.show'​, compact(​'mensaje'​));
}

Importante​: recuerde que para que el método findOrFail funcione correctamente,


debe personalizar la visualización de errores en una vista cuyo nombre corresponde
al número del error que se quiere tratar. Por lo tanto debe contar con un archivo
..\resources\views\errors\404.blade.php, con el mensaje de error requerido.

Pruebe que todo funciona correctamente.

10. Para el método edit del controlador el asunto es sencillo y similar al punto anterior:

​public​ ​function​ ​edit​($id)​ {


$mensaje = Mensaje::findOrFail($id);
​return​ view(​'mensajes.editar'​, compact(​'mensaje'​));
}

Pruebe ahora la edición.

11. La actualización la puede implementar de la siguiente manera:

​public​ ​function​ ​update​(Request $request, $id)​ {


Mensaje::findOrFail($id)->update($request->all());
​return​ redirect()->route(​'mensajes.index'​);
}

Pruebe de nuevo que todo siga funcionando correctamente.

12. Por último, el método destroy del controlador, queda así:

​public​ ​function​ ​destroy​($id)​ {


Mensaje::findOrFail($id)->delete();
​return​ redirect()->route(​'mensajes.index'​);
}

Ya puede probar toda la implementación RESTful con la tabla mensajes.

Gestión básica de usuarios

En esta sección y de manera incremental se irán incluyendo aspectos de la gestión básica


de usuarios. Más adelante se volverá sobre el tema:

1. Inicie haciendo un pequeño cambio en el método up() de la migración


CreateUsersTable​, provista por Laravel, esto para incluir el perfil del usuario:

Schema::create(​'users'​, ​function​ ​(Blueprint $table)​ {


$table->bigIncrements(​'id'​);
$table->string(​'name'​);
$table->string(​'email'​)->unique();
$table->timestamp(​'email_verified_at'​)->nullable();
$table->string(​'password'​);
​$table->string(​'role'​)->nullable();
$table->rememberToken();
$table->timestamps();
});

2. Para establecer los cambios, ejecute el comando ​php artisan migrate:refresh


(recuerde que es una combinación de reset y migrate).

3. Por ahora, agregue manualmente un usuario al que le debe asignar la contraseña


'$2y$10$H.j77qRgZ6gm7ua7vOciLOSr3JQiG3g7fa3RLxPcYv2HNObCn673y'​, una
contraseña encriptada que corresponde a “123”. Más adelante veremos cómo
automatizar la inserción de datos de prueba.

4. Actualice el archivo ..\resources\views\partials\nav.blade.php para que queden los


siguientes enlaces:

<nav>
​<li class=​"{{ seleccionado('inicio') }}"​>
<a href=​"{{ ​route​('inicio') }}"​>​Inicio​</a>
</li>
​<li class=​"{{ seleccionado('acercade') }}"​>
<a href=​"{{ ​route​('acercade') }}"​>​Acerca de...​</a>
</li>
​<li class=​"{{ seleccionado('catalogo') }}"​>
<a href=​"{{ ​route​('catalogo') }}"​>​Catálogo​</a>
</li>
​<li class=​"{{ seleccionado('mensajes.create') }}"​>
<a href=​"{{ ​route​('mensajes.create') }}"​>​Contacto​</a>
</li>

​ !-- si existe un usuario autenticado actualmente -->


<
@if (​auth​()->​check​())
​<li class=​"{{ seleccionado('mensajes.index') }}"​>
<a href=​"{{ ​route​('mensajes.index') }}"​>​Mensajes​</a>
</li>
​<li class=​"{{ seleccionado('/logout') }}"​>
<a href=​"{{ ​route​('logout') }}"​>
Cerrar sesion de {{ ​auth​()->​user​()->​name​ }}
</a>
</li>
@endif

​ !-- sólo si es un usuario invitado -->


<
@if (​auth​()->​guest​())
​<li class=​"{{ seleccionado('login') }}"​>
<a href=​"{{ ​route​('login') }}"​>​Autenticarse​</a>
</li>
@endif
</nav>

Las referencias se han cambiado todas a rutas con nombres y se han incluido dos
condiciones, la primera para verificar si hay un usuario autenticado, en cuyo caso se
muestra el enlace a los mensajes de los clientes y se permite cerrar la sesión, y la
segunda para verificar si es un usuario invitado, en cuyo caso se muestra el enlace
para autenticarse, pero no el enlace de los mensajes.

5. También actualice el archivo ..\routes\web.php para que queden las siguientes rutas:

<?php

Route::view(​'/'​, ​'inicio'​, [​'nombre'​ => ​'NN'​])->name(​'inicio'​);


Route::view(​'acercade'​, ​'acercade'​)->name(​'acercade'​);
Route::get(​'catalogo'​, ​'CatalogoController@index'​)->name(​'catalogo'​);

Route::resource(​'mensajes'​, ​'MensajesController'​);

Route::get(​'login'​, ​'Auth\LoginController@​showLoginForm'​)
->name(​'login'​);
Route::post(​'login'​, ​'Auth\LoginController@​login'​);
Route::get(​'logout'​, ​'Auth\LoginController@​logout'​)->name(​'logout'​);
Observe que aparecen tres nuevas rutas provistas por un controlador que ha pasado
desapercibido hasta ahora: ..\app\Http\Controllers\Auth\LoginController.php:

● La primera para mostrar el formulario de autenticación,

● La segunda para el procesamiento de los datos de autenticación y

● La última para cerrar sesión.

6. También se requiere la clase CatalogoController. Si su proyecto actual no la tiene


aproveche y repase:

a. php artisan make​:​controller CatalogoController

b. Esta es toda la implementacion que se requiere:

<?php
namespace​ ​App​\​Http​\​Controllers​;
class​ ​CatalogoController​ ​extends​ ​Controller​ {

​public​ ​function​ ​index​()​ {


$catalogo = \DB::table(​'productos'​)->get();
​return​ view(​'catalogo'​, compact(​'catalogo'​));
}
}

7. Cree la carpeta y la vista ..\resources\views\​auth​\​login.blade.php​ e implemente una


vista básica como la que se muestra enseguida:

@extends('plantilla')

@section('titulo')
Login
@endsection

@section('contenido')
​<h1>​Página de inicio​</h1>
​<form method=​"POST"​ action=​"login"​>
@csrf
​<input type=​"email"​ name=​"email"​ placeholder=​"Correo"​>
​<input type​=​"password"​ ​name​=​"password"​ ​placeholder​=​"Contraseña"​>
​<input type=​"submit"​ value=​"Ingresar"​>
​</form>
​<br>
@endsection
8. Actualice el atributo $redirectTo de ..\app\Http\Controllers\Auth\LoginController.php
para establecer la ruta a la que debe ir luego de la autenticación:

protected $redirectTo = 'mensajes';

Es de anotar que permitir visualizar mensajes es sólo para usuarios autenticados.

9. Implemente en ..\app\Http\Controllers\MensajesController.php, un constructor para


agregar un ​middleware​ que nos permita brindar acceso a la opción de “contacto” sin
necesidad de estar autenticado en el sistema:

​function​ ​__construct​()​ {
​$this​->middleware(​'auth'​, [​'except'​ => [​'create'​, ​'store'​]]);
}

Las demás opciones de la clase MensajesController estarán bloqueadas, mientras el


usuario no se autentique, aasí que pruebe autenticándose y como invitado.

Un poco de Front-End

Voy a darles un respiro con la programación del lado del back-end para mirar algunos
aspectos de la presentación.

Si procedió como se recomendó al principio y utiliza Laragon para este curso, notará que a
nivel de la carpeta de la aplicación hay un archivo llamado package.json que es donde se
definen las dependencias o módulos de ​Node.JS​, que también se instaló junto a Laravel.

Así como Laravel viene con un manejador de paquetes llamado Composer, Node.JS viene
con uno llamado NPM, que se utilizará para instalar algunas dependencias necesarias.

Si no está utilizando Laragon y no tiene disponible ​Node.JS​, es necesario que ingrese a la


página de descarga​ y siga los pasos de instalación.

Para comprobar que tanto Node como su gestor de paquetes está instalado, puede hacerlo
desde la terminal, como se indica en la siguiente gráfica:

Ahora sí empecemos:

1. Instale las dependencias listadas en el archivo ..\package.json:

npm ​install
Compruebe que terminada la instalación se ha creado una carpeta
..\node_modules\. Esta carpeta es análoga a la carpeta vendor de Composer.

2. Con el lanzamiento de la versión 5.4 de Laravel, su sistema de compilado de assets


(CSS y JS) sufrió varios cambios y mejoras sustanciales en cuanto a su
funcionamiento. ​Laravel Mix​ es el sucesor de ​Laravel Elixir​ y, al contrario que su
antecesor, está basado en ​Webpack​ y no en ​Gulp​.

Laravel Mix​ provee al usuario de una potente y versátil API que le permitirá definir de
forma rápida y sencilla el procesado de CSS y JavaScript, entre otras cosas.

Revise el archivo ..\webpack.mix.js que es donde se definen todas las tareas que se
llevarán a cabo cuando Laravel Mix se ejecute. Este archivo es un archivo de
configuración para definir cómo y en qué orden se irán ejecutando las tareas
definidas. ​Ver más...

La primera línea del archivo hace referencia a Laravel mix que es el encargado de
proporciona todo lo necesario para hacer uso del compilador Webpack.

La primera tarea referenciada es un archivo ​SASS​ que debe ser compilado y que se
encuentra en ..\resources\sass\

Enseguida vamos a realizar unas pruebas con CSS para entender cómo se lleva a
cabo este proceso, no para continuar proporcionando estilos de esa manera...lo que
se haga ahora se descartará más adelante.

3. Comente, ​por ahora,​ la importación que se hace de Bootstrap en el archivo


..\resources\sass\app.scss.

4. Elimine del archivo ..\resources\views\plantilla.blade.php el o los estilos que pueda


tener. Para efectos de pruebas, sólo para pruebas, se requiere que el archivo
..\resources\sass\app.scss, quede implementado así:

// ​Fonts
@​import​ url(​'https://fonts.googleapis.com/css?family=Nunito'​);

// ​Variables
@​import​ ​'variables'​;

​ ootstrap
// B
// @​import​ ​'~bootstrap/scss/bootstrap'​;

$​color​: ​#0E6655​;

nav​ {
​list-style-type​: none;
​width​: ​70%​;
​display​: flex;
}

nav​>​li​ {
​flex​: ​1​;
}

.activo​ ​a​ {
​color​: $color;
​text-decoration​: none;
}

.error​ {
​color​: red;
​font-size​: ​12px
}

5. Ejecute el comando:

npm run dev

Espere un momento...es posible que la primera vez la compilación tarde un poco


antes de aparecer el mensaje “Compiled successfully”.

6. Dele un vistazo al nuevo archivo ..\public\css\app.css que se ha generado.

7. Incluya en ..\resources\views\plantilla.blade.php el enlace que lleve a los estilos:

<!DOCTYPE html>
<html lang=​"es"​>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>@yield('titulo', 'Tienda')</title>
​<link rel=​"stylesheet"​ href=​"/css/app.css"​>
</head>

<body>
...
</body>
</html>

Los enlaces deberían resaltarse con los ligeros cambios correspondientes a los
estilos del paso 4.
Nota​: use ​npm run watch​ para supervisar los archivos y ejecutar todas las tareas
definidas en el archivo ..\webpack.mix.js.

Cuando desee apagar el supervisor, pulse Ctrl+C.

Esto le evitará estar ejecutando el comando ​npm run dev​, por cada cambio.

8. Suponga, sólo suponga, que desea cambiar de ​SASS​ a ​less​. Por definición, los
archivos less deben ir en una carpeta con el mismo nombre, así que se crearía este
nuevo estilo como ..\resources\​less​\app.less, con un contenido muy similar al del
archivo sass:

@​color​: #​0​E6655;
nav​ {
​list-style-type​: none;
​...
}
...

Por supuesto, luego se compilaría (​npm run dev​) y se utilizaría.

Ahora sí un poco de Boostrap. Lo primero será darle un poco de estilo a la barra de


navegación, no necesariamente como la que se muestra enseguida, usted puede
elegir otras opciones.

9. Encienda el supervisor (​npm run watch​)

10. Modifique el archivo ..\resources\sass\app.scss para que sólo quede con estas
líneas:

// F​ onts
@​import​ url(​'https://fonts.googleapis.com/css?family=Nunito'​);

// V​ ariables
@​import​ ​'variables'​;

// B​ ootstrap
@​import​ ​'~bootstrap/scss/bootstrap'​;
Es todo lo que necesitaremos por ahora.

11. Actualice también el archivo ..\app\helpers.php para que la única función que tiene,
quede retornando el nombre de una clase de Bootstrap que cumpla con la misma
función:

< ?php>

function​ ​seleccionado​($nombreEnlace)​ {
​return​ request()->routeIs($nombreEnlace) ?
'nav-item active'​ : ​'nav-item'​;
}

12. Modifique la vista ..\resources\views\partials\nav.blade.php para incorporar una barra


de navegación con estilos de Bootstrap, similar a la que se muestra en el punto 8.

<nav class=​"navbar navbar-expand-sm navbar-dark bg-primary"​>


​<a class=​"navbar-brand"​ href=​"#"​>​Menú​</a>

​<div class=​"collapse navbar-collapse"​ id=​"collapsibleNavId"​>


​<ul class=​"navbar-nav mr-auto mt-2 mt-lg-0"​>
​<li class=​"{{ seleccionado('inicio') }}"​>​ … ​</li>

​</ul>

​<ul class=​"navbar-nav navbar-right"​>



​</ul>
​</div>
</nav>

Descargue de ​aquí​ el código completo del archivo ​nav.blade.php​ o si lo prefiere


personalice los estilos de la barra de navegación a su agrado.

13. Un snippet para VSC como el de ​Bootstrap 4 de Ashok Koyi​, realmente ayuda a
ganar tiempo cuando de la capa de presentación se trata.

14. Ahora se quiere que el formulario de contacto también se vea mejorado. Ejemplo:
Esta versión del formulario la puede descargar de ​aquí​.

15. También hay disponible ​aquí​ un prototipo de formulario de autenticación.

16. Una pequeña mejora a la tabla que muestra los mensajes:


El código fuente de esta tabla lo puede descargar de ​aquí​.

17. Ahora sí, mejore las demás vistas que queden faltando y cuando termine de mejorar
el estilo de la aplicación, pare el supervisor (^C) y ejecute el comando:

npm run production

De esta manera optimizarán los archivos y se mejorará la carga de la página. Puede


observar en la carpeta ../public/css cómo el archivo app.css queda comprimido
después de llevar a cabo esta acción.

Tips a tener en cuenta

Combinar archivos​. Suponga que tiene la carpeta ..\resources\componentes, que a su vez


contiene varias hojas de estilos scss, para el caso del ejemplo: navbarligth.scss y
frm-dialog.scss. Se sabe además que se tiene el archivo ..\resources\sass\app.scss donde
se unifican todos los estilos de la aplicación, entonces es posible importar componentes
scss como se muestra enseguida (no se requieren las extensiones):

@import url('https://fonts.googleapis.com/css?family=Nunito');
@import 'variables';
@import '~bootstrap/scss/bootstrap';
@​import​ ​'/resources/componentes/navbarligth'​;
@​import​ ​'/resources/componentes/frm-dialog​;

Si ejecuta el comando ​npm run dev​ (combinar todo) o el comando o ​npm run production
(combinar todo y minimizar archivos) y luego revisa el contenido del archivo compilado
..\public\css\app.css, notará que las importaciones aparecen al final de dicho archivo.

En teoría,​ si hace las importaciones directamente en el archivo ..\webpack.mix.js, también


debe funcionar.
mix.js(​'resources/js/app.js'​, ​'public/js'​)
.sass(​'resources/sass/app.scss'​, ​'public/css'​);

Sin embargo no es una buena idea, llenar de parámetros, a la función sass o a cualquier
otra del objeto mix. Mejor dele una mirada a ​Laravel Mix​ y a la ​sección de front-end​ de
Laravel donde se proporciona información importante sobre el tema.

Bootstrap, Javascript y jQuery​. Algunas funcionalidades de Bootstrap están definidas en


el script app.js ya referenciado en el archivo ..\webpack.mix.js y disponible en
..\public\js\app.js. Todo lo que tiene que hacer es incluirlo en su aplicación, de esta manera
también quedará disponible ​jQuery​. Para el trabajo que sigue, el archivo app.js debe
incluirse en la cabecera del archivo ..\resources\views\plantilla.blade.php:

<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
...
​<link rel=​"stylesheet"​ href=​"/css/app.css"​>
​<!-- en lo posible use async o defer para hacer carga diferida -->
​<script ​async ​src=​"​/js/app.js​"​></script>
</head>

Compruebe que efectivamente puede utilizar jQuery (mientras lo erradican de Laravel y de


Bootstrap):

​<script>
​let​ arr = [​'Uno'​, ​'Dos'​, ​'Tres'​, ​'Cuatro'​, ​'...'​];
$.each(arr, ​function​ (​index, value​) {
​console​.log(value);
});
​</script>

Nuestros propios script JS​. Otra prueba que es importante realizar con Mix es la
siguiente:

1. Crear el archivo ..\resources\js\components\prueba.js con un simple


console.log(​'bla bla bla'​),

2. Referenciar dicho archivo en ..\webpack.mix.js:

mix.​js('resources/js/app.js', 'public/js')
.​js​(​'resources/js/components/​prueba.js​'​, ​'public/js'​)
​.sass('resources/sass/app.scss', 'public/css');

3. Incluirlo en ..\resources\js\app.js
require​(​'./bootstrap'​);
require​(​'./components/prueba'​);

window​.Vue = ​require​(​'vue'​);
// …

4. Compilar (​npm run dev​) y probar que el script hace lo que tiene que hacer.

Browsersync​. esta herramienta crea un canal de comunicación entre las carpetas de


nuestro proyecto y el navegador para observar los cambios en los archivos que están dentro
de estas carpetas. Al momento de detectar una modificación se envía un evento que
recarga el navegador o lo navegadores abiertos, de forma automática. ​Ver más...

Para tenerla disponible, todo lo que tiene que hacer es lo siguiente:

1. Inclúyala en Mix e indique en proxy la ruta de su proyecto:

mix.​js('resources/js/app.js', 'public/js')
.​sass('resources/sass/app.scss', 'public/css')
​.browserSync({
proxy: ​'http://appxx.test/'​, ​// ruta de su proyecto
})​;

2. Ejecute ​npm run watch

3. Realice algún cambio en la aplicación y guarde. Si todo salió bien, los cambios se
reflejarán automáticamente en el navegador.

4. Observe que por consola se muestra información sobre las URLs de acceso. Si tiene
dispositivos conectados a la misma red, también puede monitorear la aplicación por
la URL externa que se proporciona.

Nota​: al implementar el CRUD para usuarios tenga que es antiético e inseguro guardar las
contraseñas sin encriptar, por lo tanto debe utilizar la función bcrypt() para encriptarlas.

FIN DE LA PRIMERA PARTE

ANEXOS
Una noticia que deben leer los que me preguntan por qué
prefiero a Postgres
https://platzi.com/blog/el-apagon-de-platzi-migramos-de-mysql-a-postgresql/

Recomendados
En esta sección se irán agregando las referencias que los compañeros de curso
recomiendan. Se les agradece mucho estas contribuciones que, sin duda, permitirán
ampliar el campo de conocimientos:

● Un ​videotutorial​ recomendado por Ricardo Andrés Bolaños V.


● Curso de ​MDBoostrap
● Sistema de pedidos en línea​.

Potrebbero piacerti anche