logotipo prestashop

Prestashop pone a disposición de los desarrolladores un formulario que genera los archivos necesarios para la creación de un módulo, incluyendo opciones como para compatibilidad de versiones, hooks a los que se anclará el módulo, uso de posible tabla propia en la base de datos…

La siguiente dirección nos permitirá generar los archivos necesarios para comenzar con la creación de cualquier módulo:

https://validator.prestashop.com/generator

A partir de las instrucciones de la documentación de Prestashop, hemos seguido un «tutorial» para crea un módulo muy básico, disponible ya en mi repositorio a través de esta dirección:

https://github.com/JorgeGolo/first-upload-module

Este es el enlace a la documentación oficial:

Voy a listar unas notas que me han parecido bastante esclarecedoras sobre la forma que tiene Prestashop de estructurar sus módulos, y de cómo seguir sus estándares. Algunas ya las conocía de otras versiones:

  • Todo módulo generado con el formulario que hemos comentado, llamado «Nombredelmodulo», tiene un archivo principal llamado nombredelmodulo.php, con una estructura de carpetas similar a todos los demás módulos
  • Si tiene una sección de «Configuración» en el BackOffice tiene una función llamada getContent() en el archivo nombredelmodulo.php
  • Tools::isSubmit() es un método específico de Prestashop que comprueba si un formulario ha sido ejecutado.
  • Tools::getValue() es otro método específico que lee el parámetro desde POST o GET. En el ejemplo, Tools::getValue(‘MYMODULE_CONFIG’), donde MYMODULE_CONFIG es el name que le hemos dado al input del formulario.
  • updateValue(‘MYMODULE_CONFIG’, $nombrevariable) actualizará el texto del campo del formulario.
  • El objeto Validate contiene muchos métodos de validación, uno de ellos es isGenericName(), que ayuda a validar una string con «formato Prestashop válido», es decir, sin caracteres especiales.
  • displayError() muestra un mensaje de error
  • Hay algunos otros métodos explicados en el ejemplo que son fáciles de seguir.

En el tutorial también se habla de HelperForm. En el contexto de programación web y sistemas de gestión de contenido como PrestaShop, HelperForm es una función o clase utilizada para simplificar la creación de formularios en el back office de PrestaShop. Estos formularios suelen ser parte de módulos o extensiones que se integran en el panel de administración de PrestaShop. Aquí tienes una lista de ventajas del uso de los estándares con HelperForm:

  1. Facilita el Desarrollo:
    • Un helperform podría proporcionar métodos y funciones listas para usar que simplifican la creación de elementos comunes de formularios, como campos de entrada, botones, selectores, etc.
    • La estandarización facilita el desarrollo, ya que los desarrolladores pueden utilizar la misma interfaz y estructura para crear formularios en diferentes partes del back office.
  2. Consistencia Visual:
    • Al utilizar una clase o función común para la creación de formularios, se promueve la consistencia visual en la interfaz de administración de PrestaShop.
    • Los elementos del formulario generados por el helperform seguirían un estilo y diseño coherentes, lo que mejora la experiencia del usuario y la apariencia general del back office.
  3. Reutilización de Código:
    • La función o clase «helperform» podría contener métodos que encapsulen la lógica comúnmente utilizada en la manipulación y validación de datos de formularios.
    • Esto permite a los desarrolladores reutilizar código, evitando la duplicación y reduciendo la probabilidad de errores.
  4. Adaptabilidad a Futuras Actualizaciones:
    • Al seguir una interfaz estandarizada proporcionada por el helperform, los módulos y extensiones desarrollados con esta clase pueden ser más adaptables a futuras actualizaciones de PrestaShop.
    • Cambios en la implementación interna de PrestaShop podrían ser manejados dentro de la clase helperform, minimizando el impacto en los módulos existentes.
  5. Documentación Clara:
    • Una función o clase dedicada simplifica la documentación para los desarrolladores. Pueden entender rápidamente cómo crear formularios y qué métodos están disponibles, facilitando la adopción y el mantenimiento.
  6. Aumento de la Productividad:
    • Al reducir la complejidad y la cantidad de código necesario para crear formularios, los desarrolladores pueden ser más productivos y centrarse en la lógica específica de sus módulos en lugar de la implementación de formularios.
  • Para generar un formulario, usamos un array multinivel y luego se lo pasamos como parámetro a la clase HelperForm
  • Se crea una instancia de la clase y se le pasan parámetros a sus métodos.
  • Luego se usa el método generateForm() pasándole como variable el array que hemos creado anteriormente.
  • Todo esto dentro de una función displayForm() que tendrá este return:        
return $helper->generateForm([$form]);
  • La función displayForm la pasamos a su vez al return de getContent(), junto con otras variables (si queremos):      
return $output . $this->displayForm();

Prestashop pone a disposición de los desarrolladores otros helpers como HelperList. En esta entrada se hace uso de HelperList:

https://www.prestashop.com/forums/topic/334228-solved-helperlist-not-generating-the-list/

logo mysql

Antes de ponernos manos a la obra, leamos la definición de Wikipedia de trigger:

Los Triggers o Disparadores son objetos que se asocian con tablas y se almacenan en la base de datos. Su nombre se deriva por el comportamiento que presentan en su funcionamiento, ya que se ejecutan cuando sucede algún evento sobre las tablas a las que se encuentra asociado. Los eventos que hacen que se ejecute un trigger son las operaciones de inserción (INSERT), borrado (DELETE) o actualización (UPDATE), ya que modifican los datos de una tabla.

Wikipedia

En esta entrada mostramos dos maneras de crear un trigger en una tabla de una base de datos MySQL.

1- Usando el formulario que phpMyAdmin nos facilita. Ponerle un nombre, elegir la tabla, decidir si se hará antes o después del evento, seleccionar uno de los tres eventos (AFTER, DELETE, UPDATE). He dejado el campo del definidor vacío, y se ha autorellenado con el usuario de la base de datos (gracias phpMyAdmin, no lo recordaba de memoria).

2- Escribiendo el código directamente en el phpMyAdmin. Es decir, escribiendo algo similar a lo siguiente:

CREATE TRIGGER nombre_trigger
{BEFORE | AFTER} {INSERT | UPDATE | DELETE}
ON nombre_tabla
FOR EACH ROW
BEGIN
  -- Cuerpo del trigger
END;
  • BEFORE o AFTER: Indica si el trigger debe ejecutarse antes o después del evento especificado.
  • INSERT, UPDATE, o DELETE: Especifica el tipo de evento que activará el trigger.
  • nombre_tabla: Indica la tabla asociada al trigger.
  • FOR EACH ROW: Especifica que el trigger se ejecutará una vez por cada fila afectada por el evento.

Tipos de Triggers:

  1. BEFORE Triggers:
    • Se ejecutan antes de que se realice la acción asociada al trigger (por ejemplo, antes de la inserción, actualización o eliminación).
  2. AFTER Triggers:
    • Se ejecutan después de que se haya realizado la acción asociada al trigger.

Acceso a las Columnas de la Fila:

Dentro del cuerpo del trigger, puedes acceder a las columnas de la fila afectada usando la sintaxis NEW y OLD:

  • NEW.columna: Accede al valor de la columna después del evento (solo en triggers AFTER).
  • OLD.columna: Accede al valor de la columna antes del evento (solo en triggers BEFORE).

Ejemplo de Trigger

Este trigger ajusta automáticamente el valor de columna1 a ‘Valor predeterminado’ si se intenta insertar un registro con un valor nulo en esa columna.

DELIMITER //
CREATE TRIGGER before_insert_example
BEFORE INSERT
ON ejemplo_tabla
FOR EACH ROW
BEGIN
  IF NEW.columna1 IS NULL THEN
    SET NEW.columna1 = 'Valor predeterminado';
  END IF;
END;
//
DELIMITER ;

Eliminación de Triggers

Para eliminar un trigger, se utiliza la siguiente sintaxis:

DROP TRIGGER [IF EXISTS] nombre_trigger;

Consideraciones Importantes

  1. Cascada de Triggers:
    • Los triggers pueden activar otros triggers, lo que lleva a una cascada de acciones.
  2. Evitar Ciclos Infinitos:
    • Es importante evitar la creación de triggers que puedan entrar en un ciclo infinito.
  3. Desempeño:
    • El uso excesivo de triggers puede afectar el rendimiento, así que se deben utilizar con cuidado.
  4. Permisos:
    • Se necesitan los permisos adecuados para crear y ejecutar triggers.

Un resumen sobre cómo se personaliza la plantilla que la aplicación Prestashop incluye por defecto.

Para Versión 1.6:
https://likonet.es/likonet/modificar-una-plantilla-de-prestashop-16/

logo-prestashop
logo-prestashop

La carpeta themes/nombre_de_la_plantilla tiene esta estructura:

  • /css
  • /img
  • /js
  • /lang
  • 404.tpl
  • address.tpl
  • addresses.tpl
  • authentication.tpl
  • (…)
  • preview.jpg
  • index.php
  • es.php



//
Resumen el contenido: 4 carpetas, varios archivos de texto de extensión .tpl, un archivo index.php, un archivo es.php, y una imagen llamada preview.jpg.

La programación con smarty es una forma de separar el código HTML del código PHP con el fin de agilizar y mejorar la organización de una web de comercio electrónico. Así, en una plantilla encontraremos variables y funciones de la clase de PHP smarty y código HTML, que en el caso de Prestashop es XHMTL (tratamos algo sobre este estándar en este articulo sobre XHTML).

Para modificar el HTML, se modifican los archivos .tpl. Éstos son archivos de texto que contienen casi exclusivamente HTML y smarty. Recuerda que el parámetro $smarty->force_compile del archivo /config/smarty.config.inc.php debe estar en «true» para que los cambios se reflejen. (ver articulo sobre la velocidad de carga de la página para un poco más de información). Actualización: La versión 1.4 y posteriores incorporan un formulario en el Back Office para hacer esto menos tedioso a la hora de editar, se accede desde la pestaña Preferencias > Rendimiento.

Para cambiar el CSS haremos muchas modificaciones sobre el mismo archivo: /css/global.css. Es conveniente hacer copias de seguridad de este archivo y validarlo siempre que se pueda. Algunos módulos pueden incluir su propio archivo CSS, y tendremos que tener en cuenta su ruta para no cometer errores en el código.

Sobre los navegadores y la compatibilidad: Al personalizar el archivo global.css podemos encontrar incompatibilidades con navegadores y versiones de navegadores distintas. Esto se puede corregir de diversas maneras, una de ellas es incluir hojas de estilo distintas para cada navegador.

Imágenes: Las imágenes de la plantilla (fondos, botones…) están en la carpeta /img. Si queremos cambiar una imagen por otra que hayamos creado nosotros, debemos tener en cuenta sus características. algunos módulos puedes incluir su propia carpeta /img.

La imagen preview.jpg es la miniatura de la plantilla que se muestra en el Back-Office, en la lista de themes de nuestra tienda.

Sobre los módulos: Los archivos tpl de los módulos se encuentran en la carpeta /modules/nombre_del_módulo/nombre_del_módulo.tpl, para modificar el contenido y la estructura de los mismos hay que salir de la carpeta /themes y entrar en la carpeta de cada módulo; aunque el CSS se sigue cambiando desde global.css, a no ser que el módulo tenga un .css dentro de su carpeta.

El archivo es.php es generado por Prestashop, se usa para las traducciones y no es usual modificarlo manualmente.

– Se puede crear fácilmente una copia de la plantilla por defecto incluyendo su carpeta dentro de /themes. Cambiaremos de plantilla desde el Back Officee, Preferencias > Aspecto.
– Se recomienda tener siempre la plantilla original de la aplicación dentro de /themes, a salvo de los errores que podamos cometer.
– Puede que las modificaciones de una plantilla generen incompatibilidades después de una actualización de la versión de Prestashop (por ejemplo, de 1.3 a 1.4).

 

Parte 1: Obtener la lista de elementos de etiqueta ‘p’ y mostrarlos en un alert de JavaScript.

// lista de párrafos en un array
var pes = document.getElementsByTagName('p');

// recorremos el array con un for


var texto = "";
for (i = 0 ; i < pes.length ; i++) {

  //Aquí lo mostramos en consola
  console.log(pes[i]);


  // sin el método innerHTML no podríamos ver el contenido
  texto = texto + pes[i].innerHTML; 
}
alert(texto);

Parte 2: Añadir un nuevo elemento p con el texto “Texto del nuevo párrafo” y que sea hijo del body.

// Creamos el párrafo y su texto
let p = document.createElement("p");
let pTexto = document.createTextNode("Ejemplo");

// Añadimos el texto al párrafo
p.appendChild(pTexto);

// Añadimos el párrafo como hijo del body
document.body.appendChild(p);

https://www.w3schools.com/jsref/dom_obj_htmlcollection.asp

Una de las características clave de WordPress es su capacidad para interactuar con otras aplicaciones a través de una API REST. Pero, ¿qué es una API REST? En pocas palabras, es un conjunto de reglas y protocolos que permiten a las aplicaciones comunicarse entre sí de manera eficiente.

Conceptos: rutas y endpoints

Además de comprender qué es una API REST, es importante entender las rutas y los endpoints. Las rutas son las direcciones que se utilizan para acceder a los recursos de una aplicación, mientras que los endpoints son los puntos finales de una ruta que ofrecen acceso a una función o conjunto de funciones. En el caso de WordPress, los endpoints de la API REST permiten acceder y manipular diferentes tipos de datos de WordPress, como publicaciones, páginas, usuarios y comentarios.

Código para functions.php

Para aprovechar al máximo la API REST de WordPress, es útil conocer una lista de funciones para functions.php que permiten añadir rutas y endpoints personalizados. Estas funciones permiten a los desarrolladores personalizar la API REST de WordPress para que se adapte a sus necesidades específicas, como crear rutas y endpoints personalizados para tipos de contenido personalizados o integrar con otras aplicaciones de terceros. Con estas funciones, las posibilidades son infinitas en cuanto a personalización de la API REST de WordPress.

Añadir endpoint personalizado

add_action( 'rest_api_init', function () {
  register_rest_route( 'myplugin/v1', '/custom-endpoint/', array(
    'methods' => 'GET',
    'callback' => 'my_custom_endpoint_callback',
  ) );
} );

function my_custom_endpoint_callback( $request ) {
  // Aquí puedes escribir tu lógica personalizada para el endpoint
  return '¡Hola! Esta es una respuesta desde mi endpoint personalizado.';
}

Mostrar rutas y endpoints

add_action( 'rest_api_init', function () {
function list_endpoints() {
  global $wp_routes;

  // Iterar por todas las rutas registradas
  foreach ( $wp_routes as $route => $handlers ) {
    // Iterar por todos los handlers registrados para esta ruta
    foreach ( $handlers as $handler ) {
      echo $route . ' - ' . $handler['callback'] . '<br>';
    }
  }
}

add_action( 'admin_menu', 'register_list_endpoints_page' );
function register_list_endpoints_page() {
  add_menu_page(
    'Listado de endpoints',
    'Listado de endpoints',
    'manage_options',
    'list-endpoints',
    'list_endpoints'
  );
}

Lista de rutas por defecto

Aquí una lista de algunas rutas que incluye un sitio de WordPress

  • Route: /
  • Route: /batch/v1
  • Route: /oembed/1.0
  • Route: /oembed/1.0/embed
  • Route: /oembed/1.0/proxy
  • Route: /wp/v2
  • Route: /wp/v2/posts
  • Route: /wp/v2/posts/(?P[\d]+)
  • Route: /wp/v2/posts/(?P[\d]+)/revisions
  • Route: /wp/v2/posts/(?P[\d]+)/revisions/(?P[\d]+)
  • Route: /wp/v2/posts/(?P[\d]+)/autosaves
  • Route: /wp/v2/posts/(?P[\d]+)/autosaves/(?P[\d]+)
  • Route: /wp/v2/pages
  • Route: /wp/v2/pages/(?P[\d]+)
  • Route: /wp/v2/pages/(?P[\d]+)/revisions
  • Route: /wp/v2/pages/(?P[\d]+)/revisions/(?P[\d]+)
  • Route: /wp/v2/pages/(?P[\d]+)/autosaves
  • Route: /wp/v2/pages/(?P[\d]+)/autosaves/(?P[\d]+)
  • Route: /wp/v2/media
  • Route: /wp/v2/media/(?P[\d]+)
  • Route: /wp/v2/media/(?P[\d]+)/post-process
  • Route: /wp/v2/media/(?P[\d]+)/edit
  • Route: /wp/v2/menu-items
  • Route: /wp/v2/menu-items/(?P[\d]+)
  • Route: /wp/v2/menu-items/(?P[\d]+)/autosaves
  • Route: /wp/v2/menu-items/(?P[\d]+)/autosaves/(?P[\d]+)
  • Route: /wp/v2/blocks
  • Route: /wp/v2/blocks/(?P[\d]+)
  • Route: /wp/v2/blocks/(?P[\d]+)/revisions
  • Route: /wp/v2/blocks/(?P[\d]+)/revisions/(?P[\d]+)
  • Route: /wp/v2/blocks/(?P[\d]+)/autosaves
  • Route: /wp/v2/blocks/(?P[\d]+)/autosaves/(?P[\d]+)
  • Route: /wp/v2/templates
  • Route: /wp/v2/templates/(?P([^\/:<>\*\?»\|]+(?:\/[^\/:<>\*\?»\|]+)?)[\/\w-]+)
  • Route: /wp/v2/templates/(?P[\d]+)/revisions
  • Route: /wp/v2/templates/(?P[\d]+)/revisions/(?P[\d]+)
  • Route: /wp/v2/templates/(?P[\d]+)/autosaves
  • Route: /wp/v2/templates/(?P[\d]+)/autosaves/(?P[\d]+)
  • Route: /wp/v2/template-parts
  • Route: /wp/v2/template-parts/(?P([^\/:<>\*\?»\|]+(?:\/[^\/:<>\*\?»\|]+)?)[\/\w-]+)
  • Route: /wp/v2/template-parts/(?P[\d]+)/revisions
  • Route: /wp/v2/template-parts/(?P[\d]+)/revisions/(?P[\d]+)
  • Route: /wp/v2/template-parts/(?P[\d]+)/autosaves
  • Route: /wp/v2/template-parts/(?P[\d]+)/autosaves/(?P[\d]+)
  • Route: /wp/v2/navigation
  • Route: /wp/v2/navigation/(?P[\d]+)
  • Route: /wp/v2/navigation/(?P[\d]+)/revisions
  • Route: /wp/v2/navigation/(?P[\d]+)/revisions/(?P[\d]+)
  • Route: /wp/v2/navigation/(?P[\d]+)/autosaves
  • Route: /wp/v2/navigation/(?P[\d]+)/autosaves/(?P[\d]+)
  • Route: /wp/v2/types
  • Route: /wp/v2/types/(?P[\w-]+)
  • Route: /wp/v2/statuses
  • Route: /wp/v2/statuses/(?P[\w-]+)
  • Route: /wp/v2/taxonomies
  • Route: /wp/v2/taxonomies/(?P[\w-]+)
  • Route: /wp/v2/categories
  • Route: /wp/v2/categories/(?P[\d]+)
  • Route: /wp/v2/tags
  • Route: /wp/v2/tags/(?P[\d]+)
  • Route: /wp/v2/menus
  • Route: /wp/v2/menus/(?P[\d]+)
  • Route: /wp/v2/users
  • Route: /wp/v2/users/(?P[\d]+)
  • Route: /wp/v2/users/me
  • Route: /wp/v2/users/(?P(?:[\d]+|me))/application-passwords
  • Route: /wp/v2/users/(?P(?:[\d]+|me))/application-passwords/introspect
  • Route: /wp/v2/users/(?P(?:[\d]+|me))/application-passwords/(?P[\w\-]+)
  • Route: /wp/v2/comments
  • Route: /wp/v2/comments/(?P[\d]+)
  • Route: /wp/v2/search
  • Route: /wp/v2/block-renderer/(?P[a-z0-9-]+/[a-z0-9-]+)
  • Route: /wp/v2/block-types
  • Route: /wp/v2/block-types/(?P[a-zA-Z0-9_-]+)
  • Route: /wp/v2/block-types/(?P[a-zA-Z0-9_-]+)/(?P[a-zA-Z0-9_-]+)
  • Route: /wp/v2/global-styles/themes/(?P[^\/:<>\*\?»\|]+(?:\/[^\/:<>\*\?»\|]+)?)
  • Route: /wp/v2/global-styles/(?P[\/\w-]+)
  • Route: /wp/v2/settings
  • Route: /wp/v2/themes
  • Route: /wp/v2/themes/(?P[^\/:<>\*\?»\|]+(?:\/[^\/:<>\*\?»\|]+)?)
  • Route: /wp/v2/plugins
  • Route: /wp/v2/plugins/(?P[^.\/]+(?:\/[^.\/]+)?)
  • Route: /wp/v2/sidebars
  • Route: /wp/v2/sidebars/(?P[\w-]+)
  • Route: /wp/v2/widget-types
  • Route: /wp/v2/widget-types/(?P[a-zA-Z0-9_-]+)
  • Route: /wp/v2/widget-types/(?P[a-zA-Z0-9_-]+)/encode
  • Route: /wp/v2/widget-types/(?P[a-zA-Z0-9_-]+)/render
  • Route: /wp/v2/widgets
  • Route: /wp/v2/widgets/(?P[\w\-]+)
  • Route: /wp/v2/block-directory/search
  • Route: /wp/v2/pattern-directory/patterns
  • Route: /wp-site-health/v1
  • Route: /wp-site-health/v1/tests/background-updates
  • Route: /wp-site-health/v1/tests/loopback-requests
  • Route: /wp-site-health/v1/tests/https-status
  • Route: /wp-site-health/v1/tests/dotorg-communication
  • Route: /wp-site-health/v1/tests/authorization-header
  • Route: /wp-site-health/v1/directory-sizes
  • Route: /wp-block-editor/v1
  • Route: /wp-block-editor/v1/url-details
  • Route: /wp/v2/menu-locations
  • Route: /wp/v2/menu-locations/(?P[\w-]+)
  • Route: /wp-block-editor/v1/export

Descripción de las funciones

  • add_action: Es una función del core de WordPress que permite registrar una función para que se ejecute en un punto específico del ciclo de vida de WordPress, como la carga de la página o la ejecución de una acción. Esta función es ampliamente utilizada en el desarrollo de temas y plugins de WordPress para personalizar y extender el funcionamiento de la plataforma.
  • register_rest_route: Es una función del core de WordPress que permite registrar una ruta personalizada para la REST API de WordPress. La REST API de WordPress permite a los desarrolladores interactuar con el contenido y los datos de WordPress a través de una interfaz API basada en HTTP. La función register_rest_route permite registrar una ruta personalizada, que luego puede ser utilizada para crear o actualizar contenido o realizar cualquier otra operación que esté permitida por la API.
  • callback: Es una función que se ejecuta cuando se solicita una ruta personalizada de la REST API de WordPress. Esta función recibe como parámetros la solicitud HTTP y la respuesta HTTP, y es responsable de procesar la solicitud y devolver una respuesta HTTP apropiada.
  • handler: Es una función que se ejecuta cuando se solicita un endpoint personalizado de WordPress. Esta función recibe como parámetros la solicitud HTTP y la respuesta HTTP, y es responsable de procesar la solicitud y devolver una respuesta HTTP apropiada.

Las funciones que se han proporcionado son:

  1. add_action('rest_api_init', 'register_custom_route'): Registra la función register_custom_route para que se ejecute en el evento rest_api_init. Esta función se encargará de registrar una ruta personalizada en la REST API de WordPress.
  2. function register_custom_route(): Define la función register_custom_route, que utiliza la función register_rest_route para registrar una ruta personalizada en la REST API de WordPress. Esta ruta personalizada se definirá utilizando el método GET y se llamará custom-endpoint.
  3. add_action('rest_api_init', function () use ($namespace) {...}: Registra una función anónima para que se ejecute en el evento rest_api_init. Esta función se encargará de registrar un endpoint personalizado en la REST API de WordPress. Se utiliza una función anónima en lugar de una función con nombre para mantener el espacio de nombres limpio.
  4. $namespace = 'my-namespace/v1';: Define una variable $namespace que se utilizará como el prefijo del namespace para los endpoints personalizados. Esto asegura que los endpoints personalizados no entren en conflicto con otros endpoints que puedan estar registrados en la REST API de WordPress.
  5. register_rest_route($namespace, '/custom-endpoint/(?P<id>\d+)', array(...));: Registra un endpoint personalizado en la REST API de WordPress utilizando la función register_rest_route. Este endpoint personalizado se llamará custom-endpoint y se definirá utilizando el método GET. La ruta también incluirá un parámetro dinámico id que aceptará un número entero.
  6. function my_custom_endpoint_callback($request) {...}: Define una función my_custom_endpoint_callback que se encargará de procesar la solicitud para el endpoint personalizado. Esta función recibe la solicitud HTTP como parámetro y devuelve una respuesta HTTP apropiada.

Hacer pruebas con Postman

Postman es una herramienta de colaboración y pruebas de API que permite a los desarrolladores crear, compartir, probar y documentar API de manera más eficiente. Con Postman, los usuarios pueden enviar solicitudes HTTP a una API y ver las respuestas en tiempo real, lo que facilita la depuración y el desarrollo de API. Además, Postman proporciona una interfaz de usuario fácil de usar y funciones avanzadas como la automatización de pruebas, la generación de documentación de API y la integración con sistemas de control de versiones.

Pequeña guía de Postman

Con estos sencillos pasos puedes empezar a utilizar Postman y practicar con la API REST de WordPress

  1. Descarga e instala Postman: Si aún no tienes Postman instalado en tu ordenador, lo primero que debes hacer es descargarlo e instalarlo desde su sitio web oficial.
  2. Crear una nueva solicitud en Postman: Abre Postman y haz clic en el botón «Nueva solicitud». A continuación, ingresa la URL de la API REST de WordPress que deseas probar. Por ejemplo, si deseas obtener una lista de publicaciones, ingresa la siguiente URL: http://tudominio.com/wp-json/wp/v2/posts.
  3. Seleccionar el método HTTP: Elige el método HTTP que deseas utilizar para la solicitud (GET, POST, PUT, DELETE, etc.) en la sección desplegable a la derecha de la URL.
  4. Añadir encabezados: Si es necesario, agrega encabezados a la solicitud. Por ejemplo, si deseas enviar datos de autenticación, debes agregar un encabezado de autorización. Puedes hacer esto haciendo clic en la pestaña «Encabezados» debajo de la URL y agregando los encabezados requeridos.
  5. Enviar la solicitud: Una vez que hayas configurado todo lo que necesitas, haz clic en el botón «Enviar» para enviar la solicitud. Verás la respuesta en la pestaña «Respuesta» debajo de la solicitud.
  6. Inspeccionar la respuesta: En la pestaña «Respuesta», verás la respuesta de la API REST de WordPress. Si se devuelve una respuesta JSON, Postman te la mostrará en un formato fácil de leer. Si hay algún error en la solicitud, también verás la información de error en esta pestaña.
  7. Prueba otras rutas: Para probar otras rutas de la API REST de WordPress, simplemente repite los pasos anteriores con la URL de la ruta que deseas probar.

Debido a las actualizaciones recientes (a fecha de esta entrada) necesitamos actualizar nuestra base de conocimientos. Es muy útil tener un glosario a modo de resumen de ciertas definiciones. Además, los pasos para crear un proyecto desde cero han variado ligeramente.

Crear un proyecto

Cuando creamos un proyecto nuevo de Laravel 9 con los comandos habituales, elegiremos en más de una ocasión usar Jetstream. En el momento de ejecutar el comando de instalación de Laravel con Jetstream, la consola de comandos nos preguntará si queremos usar Liveware o Inertia. Al elegir Liveware, se creará el proyecto con esas opciones. Se ejecutará el comando «npm install» (instalando todas las dependencias de npm dentro del proyecto) seguido de «npm run dev», que creará un nuevo servidor de Vite.

Cuando necesitemos subir el proyecto tendremos que hacer uso del comando «npm run build» para que se cree el manifiesto donde estará compilado todo nuestro js y css. El servidor de Vite ya no será necesario, pero el proyecto esperará tener los manifiestos.

Glosario

Jetstream

Jetstream es un kit de inicio de aplicaciones diseñado para Laravel y proporciona la implementación para el inicio de sesión, el registro, la verificación de correo electrónico, la autenticación de dos factores, la gestión de sesiones, la API a través de Laravel Sanctum y las funciones opcionales de gestión de equipos de su aplicación.

A la hora de trabajar con Jetstream hayq ue escoger entre dos interfaces: Livewire e Inertia.js. La elección depende del lenguaje de los templates.

Liveware

Laravel Livewire es una librería que simplifica las interfaces de Blade y las hace más modernas, reactivas y dinámicas.

https://laravel-livewire.com/

Al usar Livewire, se puede elegir qué partes de la aplicación serán un componente de Livewire, mientras que el resto de la aplicación se puede representar como las plantillas Blade tradicionales.

Vite

Vite es un compilador que se mantendrá a la escucha de cualquier cambio en nuestro código de javascript y css, y creará los assets que incluiremos finalmente en nuestra página.

Para poder trabajar en el entorno local y cargar los estilos de nuestra aplicación, necesitamos tener el servidor encendido. Para ello ejecurtaríamos el comando «npm run dev» y ya veríamos los estilos.

Laravel ahora usa Vite: https://www.youtube.com/watch?v=4gxxWTe3pVA&list=PLZ2ovOgdI-kWWS9aq8mfUDkJRfYib-SvF&index=32

Bootstrap

Cómo usar Bootstrap en Laravel 9 usando Vite: https://www.youtube.com/watch?reload=9&v=D_-i6ZiWHuQ

Bootstrap es un framework front-end utilizado para desarrollar aplicaciones web y sitios mobile first, o sea, con un layout que se adapta a la pantalla del dispositivo utilizado por el usuario. El propósito del framework es ofrecerle al usuario una experiencia más agradable cuando navega en un sitio.

Por esta razón, tiene varios recursos para configurar los estilos de los elementos de la página de una manera simple y eficiente, además de facilitar la construcción de páginas que, al mismo tiempo, están adaptadas para la web y para dispositivos móviles.

¿Cómo funciona Bootstrap?
Bootstrap está constituido por una serie de archivos CSS y JavaScript responsables de asignar características específicas a los elementos de la página.

¿Cómo configurar y usar Bootstrap?
Hay diferentes formas de configurarlo o estructurarlo para usarlo en una aplicación web. Sin embargo, para que funcione correctamente, es necesario agregar las bibliotecas JQuery y Popper.js, necesarias para la ejecución de algunos componentes de Bootstrap.

Para comenzar a usar Bootstrap en una página, es necesario agregar las referencias de los principales archivos del framework en la página principal de la aplicación.

Tailwind

Tailwind es un framework CSS. Es Brinda un conjunto único de clases de utilidad que hace que el proceso de desarrollo sea muy fácil y da como resultado un diseño único. El código y el diseño web se puede personalizar libremente.

Instalar el framework Tailwind no es tan simple como instalar un framework bootstrap, requiere un poco de configuración con laravel Mix.

Npm

https://docs.npmjs.com/cli/v6/commands/npm

npm es el administrador de paquetes para la plataforma Node JavaScript. Coloca los módulos en su lugar para que el nodo pueda encontrarlos y gestiona los conflictos de dependencia de manera inteligente.

Usualmente npm se necesita porque el proyecto requiere instalaciones.

Laravel Mix (antes de Vite)

https://laravel.com/docs/8.x/mix

Laravel Mix proporciona una API fluida para definir los pasos de compilación de Webpack utilizando varios procesadores de CSS y JavaScript comunes.

El objetivo es, entre otras cosas, procesar todo nuestro código css, minificarlo y combinarlo en un solo archivo. Lo mismo para el código javascript, minificar, ofuscar y combinar todo el código en un solo archivo. Con laravel mix nuestras páginas web son mas seguras y rápidas.

Laravel mix está optimizado para usar con Laravel, pero se puede utilizar en cualquier aplicación web, aunque no se use Laravel.

¿Qué es Jetstream?

Directamente de la documentación de Laravel:

Jetstream provides the implementation for your application’s login, registration, email verification, two-factor authentication, session management, API…

Instalación

Nuevo proyecto con Jetstream

Vamos a la consola de Gitbash, y dentro de nuestro directorio htdocs de XAMPP la línea de más abajo para un nuevo proyecto. A este lo llamaremos «bootstrap».

Tendremos que elegir entre liveware y inertia. Si estás familiarizado con blade, es más sencillo liveware. Con inertia es mejor si estás más familiarizado con javascript, por ejemplo Vue. Lo haremos con liveware.

laravel new bootstrap --jet

Base de datos

El siguiente paso es crear una base de datos llamada bootstrap (así la llamaremos) dentro de phpmyadmin.

Desde la misma consola de Gitbash, podemos ir a nuestro proyecto:

cd bootstrap

Y ejecutar las migraciones:

php artisan migrate

Iniciar el server

Inicializamos el servidor así:

php artisan serve

Se nos informará que podemos acceder mediante una url como: http://127.0.0.1:8000.

Instalar Bootstrap

Abrimos nuestro proyecto desde VSC. En la carpeta principal podemos encontrar el archivo de configuración de tailwind, pero nosotros queremos bootstrap, con lo que instalaremos un paquete llamado Jetstrap.

La siguiente línea la hemos encontrado en el repositorio de Github https://github.com/nascent-africa/jetstrap, en el readme.

composer require nascent-africa/jetstrap --dev

Ahora necesitamos ejecutar el siguiente comando para liveware:

php artisan jetstrap:swap livewire

Nos aparece el siguiente mensaje una vez terminado: Please execute the «npm install && npm run dev» command to build your assets para que los estilos se compilen. Ya estaremos viendo que el archivo de configuración de tailwind no existe, y que en la carpeta resource/sass están las variables y estilos de bootstrap, y que el archivo webpack.mix.js todo se va a compilar con sass y que buscará el archivo de la carpeta sass que se llama app.scss.

Ejecutamos por orden:

npm install
npm run dev

Al ejecutar el segundo comando nos hemos encontrado un error de la versión de node que se explica en el vídeo siguiente:

Descargamos el instalador del node más actualizado para nuestro sistema operativo y los instalamos. Con esto hemos podido ejecutar el comando, pero los estilos no se cargan y probamos a empezar todo de nuevo, esta vez con node actualizado a la última versión.

Y ahora nos encontramos con otro «problema», con una actualización Laravel ahora usa Vite, con lo que los pasos anteriores no funcionan como esperábamos.

IMPORTANTE: Para los estilos es indispensable conocer el funcionamiento de Vite, que es una mejora de Laravel con respecto a las versiones anteriores. Por lo tanto, el uso y la instalación de Tailwind o de Bootstrap va a estar condicionado por el funcionamiento de Vite.

Recursos:

Enlace de Youtube: https://www.youtuhttps://www.youtube.com/watch?v=v-R5FLGAB58&list=PLZ2ovOgdI-kWWS9aq8mfUDkJRfYib-SvF&index=30

Archivos:

  • resources/views/layouts/header.blade.php

Agregaremos al menú de navegación un link «contáctanos». Al pulsarlo, nos redirigirá a una vista que mostrará un formulario donde poner el nombre, el mail y un mensaje. Esa información nos será enviada en un correo electrónico.

Enlace en el menú

Abrimos la platilla de headers dentro de la carpeta partials, para añadir un link «contactanos» en nuestro menú de navegación.

            <ul>
                <li><a href="{{route('home')}}" class="{{request()->routeIs('home') ? 'active' : '' }}">Home</a>
                    {{-- @dump(request()->routeIs('home')) --}}
                </li>
                <li><a href="{{route('cursos.index')}}" class="{{request()->routeIs('cursos.*') ? 'active' : '' }}">Cursos</a>
                    {{-- @dump(request()->routeIs('cursos.index')) --}}
                </li>
                <li><a href="{{route('nosotros')}}" class="{{request()->routeIs('nosotros') ? 'active' : '' }}">Nosotros</a>
                    {{-- @dump(request()->routeIs('nosotros')) --}}
                </li>
                <li><a href="">Contactanos</a>
                </li>
</ul>

Ruta para almacenar mail

Abrimos web.php. Le damos a la ruta get que hemos creado para enviar mails el nombre «contactanos.index».

Route::get('contactanos', function() {
    $correo = new ContactanosMailable;
    Mail::to('weblikonet@gmail.com')->send($correo);
    return "mensaje enviado";
})->name('contactanos.index');

Ruta para mostrar el formulario

Hacemos que el nuevo enlace de contacto apunte a esa ruta.


<li><a href="{{route('contactanos.index')}}">Contactanos</a></li>

Controlador para la ruta y métodos

Creamos un controlador para que administre la ruta.

php make:controller ContactanosController 

Creamos dos métodos para este controlador: index() para mostrar el formulario y store() para procesarlo y enviar el mail.

class ContactanosController extends Controller
{
    //
    public function index(){
        
    }

    public function store(){
    
    }

}

Modificamos el controlador

Habíamos escrito esta función en la ruta:

        $correo = new ContactanosMailable;
        Mail::to('weblikonet@gmail.com')->send($correo);
        return "mensaje enviado";    

Lo ponemos esta vez dentro del método store().

class ContactanosController extends Controller
{
    //
    public function index(){
        
    }

    public function store(){
        $correo = new ContactanosMailable;
        Mail::to('weblikonet@gmail.com')->send($correo);
        return "mensaje enviado";    
    }

}

Como estamos creando aquí el objeto y haciendo uso de la clase, debemos importar las clases en el controlador:

use App\Mail\ContactanosMailable;
use Illuminate\Support\Facades\Mail;

Modificamos la ruta para el controlador

Modificamos la ruta para que llame al método index() del controlador ContactanosController:

Route::get('contactanos', [ContactanosController::class, 'index'])->name('contactanos.index');

Vista para el formulario

Hacemos que el método index() del controlador devuelva una vista llamada «contactanos.index»:

    public function index(){
        return view('contactanos.index');
    }

Creamos una carpeta dentro de views llamada «contactanos» y, dentro, la vista index.blade.php. Su contenido puede estar copiado de la vista home, y modificamos a nuestro antojo el title y el h1:

@extends('layouts.plantilla')

@section('title', 'Contactanos')

@section('content')
    <h1>Déjanos un mensaje</h1>
@endsection

Añadimos la clase para que el enlace quede activo cuando se visite:

<a href="{{route('contactanos.index')}}" class="{{request()->routeIs('contactanos.index') ? 'active' : '' }}">Contactanos</a>

Formulario de contacto

Creamos el formulario dentro de la vista:

<form action="">
    <label>
        Nombre:
        <br>
        <input type="text" name="name">
    </label>
    <br>


    <label>
        Correo:
        <br>
        <input type="mail" name="email">
    </label>
    <br>

    <label>
        Mensaje:
        <br>
        <textarea name="mensaje" rows="4"></textarea>    
        </label>
    <br>

    <button type="submit">Enviar mensaje</button>

</form>

Ruta post para el formulario

Creamos una ruta que procese la información del formulario. Va a ser de tipo post y llevará la url «contactanos». Le asignamos el método store() del controlador ContactanosController. Le damos el nombre «contactanos.store»:

Route::post('contactanos',[ContactanosController::class, 'store'])->name('contactanos.store');

Ponemos esta ruta en el action del formulario. Tenemos que especificar también el método del formulario, y el token csrf:

(...)
<form action="{{route('contactanos.store')}}" method="post">
    @csrf
(...)

Ejercicio

Hagamos este pequeño ejercicio antes de continuar. Si añadimos una propiedad a la clase del mailable que hemos creado para enviar el correo, como esta:

(...)
class ContactanosMailable extends Mailable
{
    use Queueable, SerializesModels;

    public $subject="Asunto del mail de contacto";

    public $propiedad="Esta es una propiedad del mailable"; 
(...)

Podemos rescatar esta propiedad dentro de la vista del mail de la siguiente forma:

<!DOCTYPE html>
(...)
<body>
    <h1>Correo Electrónico</h1>
    <p>Primer mail que mando por Laravel</p>
    {{$propiedad}}
</body>
</html>

Y comprobar que se envía correctamente

Recuperar la información del formulario

Al método store del controlador le pasamos el objeto request.

    public function store(Request $request){
        $correo = new ContactanosMailable;
        Mail::to('weblikonet@gmail.com')->send($correo);
        return "mensaje enviado";    
    }

Donde estoy creando la instancia de la clase ContactanosMailable le pasamos la información de request al constructor:

    public function store(Request $request){
        $correo = new ContactanosMailable($request->all());
        Mail::to('weblikonet@gmail.com')->send($correo);
        return "mensaje enviado";    
    }

Y recibimos la información en el constructor de la clase de la siguiente forma:

public $contacto;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($contacto)
    {
        $this->contacto = $contacto;
    }

Ahora podemos modificar la vista para imprimir la información así:

(...)
<body>
    <h1>Correo Electrónico</h1>
    <p>Primer mail que mando por Laravel</p>
    {{$propiedad}}

    <p><strong>Nombre:</strong> {{$contacto['nombre']}}</p>
    <p><strong>Nombre:</strong> {{$contacto['email']}}</p>
    <p><strong>Nombre:</strong> {{$contacto['mensaje']}}</p>
</body>
</html>

Validación

Completamos el método store() para validar el formulario. Todos los campos serán requeridos y el email debe ser un email:

 public function store(Request $request){

        $request->validate([
            'name'=>'required',
            'name'=>'required|email',
            'mensaje'=>'required',
        ]);

        $correo = new ContactanosMailable($request->all());
        Mail::to('weblikonet@gmail.com')->send($correo);
        return "mensaje enviado";    
    }

En el formulario ponemos los mensajes de error en cada campo. Abrimos la directiva error con el parámetro el nombre del campo que queramos validar, y en la directiva un párrafo con el posible error almacenado en la variable $message. Lo hacemos en cada campo:

 <form action="{{route('contactanos.store')}}" method="POST">
    @csrf
    <label>
        Nombre:
        <br>
        <input type="text" name="name">
    </label>
    <br>

    @error('name')
        <p><strong>{{$message}}</strong></p>
    @enderror

    <label>
        Correo:
        <br>
        <input type="mail" name="email">
    </label>
    <br>


    @error('email')
        <p><strong>{{$message}}</strong></p>
    @enderror

    <label>
        Mensaje:
        <br>
        <textarea name="mensaje" rows="4"></textarea>    
        </label>
    <br>
    
    @error('mensaje')
        <p><strong>{{$message}}</strong></p>
    @enderror

    <button type="submit">Enviar mensaje</button>
</form>

Redirección

Con esta redirección no mostraremos el mensaje «mensaje enviado», sino que volveremos a la vista en la que estábamos mostrando un mensaje. Le mandamos a la ruta un mensaje de sesión con el método with con dos parámetros, el nombre de la variable y su contenido.

    public function store(Request $request){

        $request->validate([
            'name'=>'required',
            'email'=>'required|email',
            'mensaje'=>'required',
        ]);

        $correo = new ContactanosMailable($request->all());
        Mail::to('weblikonet@gmail.com')->send($correo);

        return redirect()->route('contactanos.index')->with('info','mensaje enviado');    
    }

Usamos un condicional en la vista para ver si mostramos o no la variable de sesión en una alerta, en la vista del formulario.

(...)
    <button type="submit">Enviar mensaje</button>

</form>

@if (session('info'))
    <script>
        alert("{{session('info')}}")
    </script>
@endif
(...)

Recursos:

Enlace de Youtube: https://www.youtube.com/watch?v=Ei3RVhdcE0A&list=PLZ2ovOgdI-kWWS9aq8mfUDkJRfYib-SvF&index=29

Archivos:

  • .env
  • config/mail.php
  • resources/routes/web.php

Conectar con el proveedor de correo electrónico

Laravel está configurado para conectar con ciertos proveedores que usan APIs, aunque podemos conectarnos con cualquier otro siempre que instalemos los archivos necesarios. Desde config/mail.php podemos ver toda la configuración necesaria.

La primera decisión a tomar es elegir entre API y SMTP. En el archivo mail.php está definida la variable mail_mailer y su valor por defecto es SMTP, que es el protocolo que usaremos aquí. (SMTP: Protocolo para transferencia simple de correo).

    'default' => env('MAIL_MAILER', 'smtp'

Además necesitamos las credenciales del proveedor para conectarnos por SMTP. Desde el archovo mail.php:


    'mailers' => [
        'smtp' => [
            'transport' => 'smtp',
            'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
            'port' => env('MAIL_PORT', 587),
            'encryption' => env('MAIL_ENCRYPTION', 'tls'),
            'username' => env('MAIL_USERNAME'),
            'password' => env('MAIL_PASSWORD'),
            'timeout' => null,
            'local_domain' => env('MAIL_EHLO_DOMAIN'),
        ],

Como vemos en este fragmento, estas variables se toman del archivo .env:

MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"

Mailtrap

Mailtrap es un servidor falso SMTP para pruebas. Una vez creada la cuenta en https://mailtrap.io/ (que aquí hemos creado con las credenciales de una de nuestras cuentas de Gmail) podemos encontrar los parámetros en el primer correo de prueba que encontraremos en la bandeja de entrada.

Necesitaremos el host, el puerto, el usuario, la contraseña para el archivo .env.

En mail.php vamos a encontrar los nombres de las variables para el remitente:


    'from' => [
        'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
        'name' => env('MAIL_FROM_NAME', 'Example'),
    ],

En el archivo .env:

MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"

Creación de mailable

Desde la consola:

    php artisan make:mail ContactanosMailable

Dentro de /app se creará uan carpeta /Mail con el archivo que acabamos de crear.

Agregamos propiedades a la clase que se extiende de Mailable.

 class ContactanosMailable extends Mailable
{
    use Queueable, SerializesModels;

    public $subject="Asunto del mail de contacto";

(...)

Más abajo encontramos que la función build() llama a una vista que vamos a renombrar, y luego a crearla:

    public function build()
    {
        return $this->view('emails.contactanos');
    }

En /views creamos una carpeta /emails y dentro nuestra vista llamada contactanos.blade.php. Dentro de la vista creamos el cuerpo de un archivo HTML (recordamos: en VSC basta con escribir ! y tabular). Modificamos el HTML a nuestro antojo, e incluso agregamos un estilo:

<!DOCTYPE html>
<html lang="en">
<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>Document</title>
    <style>
        h1 {color:blue}
    </style>
</head>
<body>
    <h1>Correo Electrónico</h1>
    <p>Primer mail que mando por Laravel</p>
</body>
</html>

Ruta para el envío del mail

Vamos al archivo de rutas, en web.php, y creamos nueva ruta tipo get:

Route::get('contactanos', function() {
    //
})

E importamos dos clases a este archivo:


use App\Mail\ContactanosMailable;
use Illuminate\Support\Facades\Mail;

Vamos a generar una instancia de ContactanosMailable y acceder a la clase mail con el método to(), usando como parámetro un mail cualquiera y pasándole una clase send con el objeto creado:

Route::get('contactanos', function() {
    $correo = new ContactanosMailable;
    Mail::to("unmaildepruebaqueexista@gmail.com")->send($correo);
    return "mensaje enviado";
})

Al ir a la url de la ruta se enviará el mail de que hemos creado. Podemos ver los mails en nuestra bandeja de entrada de mailtrap.

Recursos:

Enlace de Youtube: https://www.youtube.com/watch?v=e0ynchA_sBA&list=PLZ2ovOgdI-kWWS9aq8mfUDkJRfYib-SvF&index=28

Archivos:

  • resources/views/layouts/plantilla.blade.php
  • routes/web.php
  • resources/views/layouts/partials

HTML del menú de navegación

Este es el prototipo del código del menú de navegación, que escribiremos en el archivo de plantilla plantilla.blade.php:

(...)

<body>
    <!-- header -->
    <!-- nav -->
    <header>
        <nav>
            <ul>
                <li><a href="">Home</a></li>
                <li><a href="">Cursos</a></li>
                <li><a href="">Nosotros</a></li>
            </ul>
        </nav>
    </header>

(...)

Nombraremos nuestra ruta principal como «Home». En el archivo de rutas:

Route::get('/', HomeController::class)->name('Home');

Vamos a crear una ruta para «Nosotros» con el siguiente código, que utiliza un método distinto: el método view. Este método sólo lo utilizaremos para mostrar contenido estático. Admite dos parámetros, la url y el nombre de la vista. También le pondremos un nombre, la llamaremos «nosotros»:

// Route::view('url','vista')->name('nombre');
Route::view('nosotros','nosotros')->name('nosotros');

Creamos el archivo nosotros.blade.php en la carpeta vistas, le añadimos el contenido html de la vista home y la modificamos así:

@extends('layouts.plantilla')

@section('title', 'Home')

@section('content')
    <h1>Nosotros</h1>
@endsection

Ya podemos rellenar las rutas del nav de la siguiente forma:

    <header>
        <nav>
            <ul>
                <li><a href="{{route('home')}}">Home</a></li>
                <li><a href="{{route('cursos.index')}}">Cursos</a></li>
                <li><a href="{{route('nosotros')}}">Nosotros</a></li>
            </ul>
        </nav>
    </header>

Uso de la función request

Usando la función request con el método routeIs, pasando como parámetro una ruta, obtendremos true si la ruta es la actual, y false si no lo es. Podemos comprobarlo as´í:

    <header>
        <nav>
            <ul>
                <li><a href="{{route('home')}}">Home</a>
                <?php
                    dump(request()->routeIs('home'));
                ?>
</li>
                <li><a href="{{route('cursos.index')}}">Cursos</a></li>
                <li><a href="{{route('nosotros')}}">Nosotros</a></li>
            </ul>
        </nav>
    </header>

Mejor usar una directiva de blade:

@dump(request()->routeIs('home'))

Podríamos usar la misma directiva en cada ruta:

        <nav>
            <ul>
                <li><a href="{{route('home')}}">Home</a>
                    @dump(request()->routeIs('home'))
                </li>
                <li><a href="{{route('cursos.index')}}">Cursos</a>
                    @dump(request()->routeIs('cursos.index'))
                </li>
                <li><a href="{{route('nosotros')}}">Nosotros</a>
                    @dump(request()->routeIs('nosotros'))
                </li>
            </ul>
        </nav>

Añadimos una clase a cada enlace, con un condicional usando operadores ternarios. Así añadimos la clase active al enlace activo, o una clase vacía si la condición no se cumple:

            <ul>
                <li><a href="{{route('home')}}" class="{{request()->routeIs('home') ? 'active' : '' }}">Home</a>
                </li>
                <li><a href="{{route('cursos.index')}}" class="{{request()->routeIs('cursos.index') ? 'active' : '' }}">Cursos</a>
                </li>
                <li><a href="{{route('nosotros')}}" class="{{request()->routeIs('nosotros') ? 'active' : '' }}">Nosotros</a>
               </li>
            </ul>

Para que cualquier ruta que comience con cursos quede como activa (si visitamos la url de cualquier curso) usamos un asterisco:

            <ul>
                <li><a href="{{route('home')}}" class="{{request()->routeIs('home') ? 'active' : '' }}">Home</a>
                </li>
                <li><a href="{{route('cursos.index')}}" class="{{request()->routeIs('cursos.*') ? 'active' : '' }}">Cursos</a>
                </li>
                <li><a href="{{route('nosotros')}}" class="{{request()->routeIs('nosotros') ? 'active' : '' }}">Nosotros</a>
               </li>
            </ul>

Optimizamos la plantilla

Creamos una carpeta llamada partials dentro de la carpeta layouts. Dentro, un archivo de plantilla llamado header.blade.php, con el siguiente contenido (que cortamos desde la plantilla.blade.php):

<header>
        <h1>Proyecto Laravel</h1>
        <nav>
            <ul>
                <li><a href="{{route('home')}}" class="{{request()->routeIs('home') ? 'active' : '' }}">Home</a>
                    {{-- @dump(request()->routeIs('home')) --}}
                </li>
                <li><a href="{{route('cursos.index')}}" class="{{request()->routeIs('cursos.*') ? 'active' : '' }}">Cursos</a>
                    {{-- @dump(request()->routeIs('cursos.index')) --}}
                </li>
                <li><a href="{{route('nosotros')}}" class="{{request()->routeIs('nosotros') ? 'active' : '' }}">Nosotros</a>
                    {{-- @dump(request()->routeIs('nosotros')) --}}
                </li>
            </ul>
        </nav>
    </header>

Y en el lugar donde estaba todo el código del header, una directiva de blade para incluirlo:

    @include('layouts/partials/header')

Ya que hemos hecho esto, añadamos un footer. Dentro de partials, creamos footer.blade.php, lo incluimos también en la plantilla con include. El contenido del footer podría ser:

<footer>
    Esto es un footer
</footer>

Para resumir, el contenido de la plantilla.blade.php quedaría así:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@yield('title')</title>
    <!-- favicon -->
    <!-- estilos -->
    <style>
        .active {
            font-weight: bold;
            color: red
        }
    </style>

</head>
<body>
    <!-- header -->
    <!-- nav -->

    @include('layouts/partials/header')

    @yield('content')

    <!-- footer -->

    @include('layouts/partials/footer')

    <!-- script -->
</body>
</html>

Recursos:

Enlace de Youtube: https://www.youtube.com/watch?v=VVY5RVdpFco&list=PLZ2ovOgdI-kWWS9aq8mfUDkJRfYib-SvF&index=27

Archivos:

  • database/migrations
  • database/factories

Crear el campo slug

Genramos un nuevo campo dentro de la migración de la tabla cursos, llamado «slug»

    public function up()
    {
        Schema::create('cursos', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('slug');
            $table->text('description');
            $table->text('categoria');
            $table->timestamps();
        });
    }

Editamos el factory de esta tabla:

    public function definition()
    {
        return [
            //
            'name'=>$this->faker->sentence(),
            'slug'=>Str::slug($this->faker->sentence(),'-'),
            'categoria'=>$this->faker->randomElement(['Desarrollo web','Diseño web']),
            'description'=>$this->faker->paragraph()
        ];

Hemos llamado a la clase Str y a su método slug. Cambiará todas las mayúsculas por minúsculas y sustituirá los espacios por guiones (porque hemos establecido un guión como segundo parámetro).

En este caso tendremos un slug distinto al título, y queremos que sea el mismo, con lo que lo podemos poner así:

public function definition()
    {
        $name =$this->faker->sentence();

        return [
            //
            'name'=>$name,
            'slug'=>Str::slug($name,'-'),
            'categoria'=>$this->faker->randomElement(['Desarrollo web','Diseño web']),
            'description'=>$this->faker->paragraph()
        ];
    }

No podemos olvidar llamar a la clase Str al principio del archivo, si no lo ha autorrellenado Intelephense:

use Illuminate\Support\Str;

Luego migramos para ver los cambios en la base de datos:

php artisan migrate:fresh --seed

Uso del campo slug

Abrimos la vista index de los cursos, comprobemos la url actual:

@foreach ($cursos as $curso)

        <li>
         <a href="{{route('cursos.show', $curso->id)}}">{{$curso->name}}</a> 
         </li>
          {{route('cursos.show', $curso->id))}}

        @endforeach

Recordamos que en Laravel podemos llamar a la id directamente de la siguiente manera, ya que el recurso de llamar a la id es muy utilizado y se puede hacer directamente así:

@foreach ($cursos as $curso)

        <li>
         <a href="{{route('cursos.show', $curso)}}">{{$curso->name}}</a> 
         </li>
          {{route('cursos.show', $curso))}}

        @endforeach

LA id se la pasamos al controlador mediante la variable curso. Necesitaríamos que cuando utilicemos el método route y le pasemos el objeto curso, en vez de la id, recupere el slug del objeto. Devolverá el registro cuyo slug coincida con lo que le pasemos por la url.

Vamos al modelo Curso, que vemos que se extiende de la clase Model.

Vemos que uno de los muchos métodos que tiene es getroutekeyname:

     public function getRouteKeyName()
    {
        return $this->getKeyName();
    }

Podríamos modificar este mnétodo tal que así, para lograr lo que queremos:

     public function getRouteKeyName()
    {
       // return $this->getKeyName();
 return 'slug';
    }

Pero lo mejor es hacerlo en el modelo Curso:

Recursos:

Enlace de Youtube: https://www.youtube.com/watch?v=VYuthGIhrS0&list=PLZ2ovOgdI-kWWS9aq8mfUDkJRfYib-SvF&index=25

Archivos:

  • routes/web.php

Uso de route resource

Desde la consola de Git Bash, navegamos hasta el directorio de nuestro proyecto y ejecutamos el siguiente comando de artisan: php artisan «route list»

php artisan r:l

Comentamos todas las rutas y vamos a definir la rutas del CRUD de la siguiente forma:

 Route::resource('cursos', CursoController::class);

Si volvemos a ejecutar el comando route list, veremos todas las 7 rutas del CRUD de nuevo en la consola.

El resultado es exactamente el mismo porque anteriormente creamos las rutas una a una siguiendo las convenciones de nombres.

Cambio de rutas creadas con route resource

En la documentación de Laravel se nos indica cómo hacerlo: https://www.oulub.com/docs/laravel/controllers#restful-localizing-resource-uris

Según se nos indica allí, vamos al archivo App/providers/appserviceprovider.php y poner en la parte superior del archivo:

use Illuminate\Support\Facades\Route;

Y dentro del método boot:

    public function boot()
    {
        Route::resourceVerbs([
            'create' => 'crear',
            'edit' => 'editar',
        ]);
    }

Y podemos hacer esto porque una cosa es la url, y otra el nombre de la ruta. Lo que estamos cambiando es la url, mientras que los formularios y lois enlaces siguen apuntando a las rutas por su nombre.

De hecho, podríamos modificar la palabra «cursos» del route resource por cualquier otra, por ejemplo, «asignaturas»; y todo seguiría funcionando como hasta ahora, pero tendríamos que usar un par de métodos más para que no falle todo:

Route::resource('asignaturas', CursoController::class)->parameters(['asignatura' => 'curso'])->names('cursos');

El método names permitirá que las rutas sigan funcionando como hasta ahora (pues están en todas las vistas, y si no, tendríamos que modificar cada vista) y el método parameters incluye un array para los nombres de las variables que usamos para generar las rutas (que, de nuevo, necesitarían ser cambiadas por «asignatura»).

Recursos:

Enlace de Youtube: https://www.youtube.com/watch?v=PT6BoDQdkXk&list=PLZ2ovOgdI-kWWS9aq8mfUDkJRfYib-SvF&index=24

Archivos:

  • routes/web.php
  • CursoController.php

Crearemos una ruta de tipo delete, que apunte a una url llamada curso.destroy.

Route::delete('cursos/{curso}',[CursoController::class, 'destroy'])->name('cursos.destroy');

Crearemos un método destroy() en nuestro controlador. Como se está mandando información por la url, lo rescatamos en el controlador.

    public function destroy(Curso $curso) {
        $curso->delete();
    }    

Añadiremos un botón eliminar en la vista show dentro de un formulario. El formulario tendrá la directiva method con el método del mismo (que no es post, sino delete) y el token de seguridad, además de que en el action pasaremos la nueva ruta creada con el parámetro curso. En la vista show:

<form action="{{route('cursos.destroy',$curso)}}" method="POST">
        @csrf
        @method('delete')
        <button type="submit">Eliminar</button>
    </form>

Tenemos que pedirle al método destroy que nos redirija para que el formulario no muestre una página en blanco,

    public function destroy(Curso $curso) {
        $curso->delete();
        return redirect()->route('cursos.index');
    }    

Recursos:

Enlace de Youtube: https://www.youtube.com/watch?v=3uUW1IviXns&t=3s