Integración de APIs REST con Fetch API, PHP y MySQL

Compartir

[caption id="attachment_3047" align="aligncenter" width="2245"]Integración de APIs REST con Fetch API, PHP y MySQL Integración de APIs REST con Fetch API, PHP y MySQL[/caption]

En este tutorial, crearemos una aplicación web completa que utiliza APIs REST, Fetch API, PHP, y MySQL para implementar una tienda en línea. Aprenderemos cómo estructurar el backend con PHP para proporcionar datos a través de una API REST, así como el frontend para consumir y mostrar esos datos utilizando Fetch API.

1. Configuración inicial

Requisitos previos

  • Servidor local configurado (por ejemplo, XAMPP).
  • Base de datos MySQL.
  • Conocimientos básicos de PHP, HTML, CSS, y JavaScript.

Estructura del proyecto

Crea un proyecto con la siguiente estructura:

/api-tienda
|-- /images            # Carpeta para imágenes de los productos
|-- index.html         # Archivo principal del frontend
|-- fetch-products.php # Archivo que contiene la API REST
|-- database.php       # Archivo para la conexión a MySQL

2. Crear la base de datos y tabla

Abre tu gestor de bases de datos como phpMyAdmin y usa la siguiente consulta SQL para crear la base de datos y la tabla:

CREATE DATABASE tienda;

USE tienda;

CREATE TABLE productos ( id INT AUTO_INCREMENT PRIMARY KEY, nombre VARCHAR(100) NOT NULL, fabricante VARCHAR(100), precio DECIMAL(10, 2) NOT NULL, imagen VARCHAR(100) NOT NULL );

-- Insertar datos de ejemplo INSERT INTO productos (nombre, fabricante, precio, imagen) VALUES ('Laptop', 'Dell', 750.00, 'laptop.jpg'), ('Smartphone', 'Samsung', 500.00, 'smartphone.jpg'), ('Tablet', 'Apple', 650.00, 'tablet.jpg'), ('Auriculares', 'Sony', 100.00, 'auriculares.jpg');

3. Crear la conexión a la base de datos (database.php)

<?php
$host = 'localhost';
$dbname = 'tienda';
$username = 'root'; // Cambiar si usas otro usuario
$password = ''; // Cambiar si usas contraseña

try { $pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { die("Error de conexión: " . $e->getMessage()); } ?>

4. Crear la API REST (fetch-products.php)

<?php
require 'database.php';

// Configurar cabeceras header('Access-Control-Allow-Origin: *'); header('Content-Type: application/json');

try { // Parámetros enviados por el frontend $page = isset($_GET['page']) ? (int)$_GET['page'] : 1; $limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 12; $search = isset($_GET['search']) ? trim($_GET['search']) : ''; $priceOrder = isset($_GET['priceOrder']) ? strtolower(trim($_GET['priceOrder'])) : ''; $offset = ($page - 1) * $limit;

// Base de la consulta $baseQuery = "FROM productos WHERE 1";

// Agregar búsqueda por nombre si se proporciona if (!empty($search)) { $baseQuery .= " AND nombre LIKE :search"; }

// Contar el total de productos $totalQuery = "SELECT COUNT(*) AS total " . $baseQuery; $totalStmt = $pdo->prepare($totalQuery);

if (!empty($search)) { $totalStmt->bindValue(':search', "%$search%", PDO::PARAM_STR); }

$totalStmt->execute(); $totalProductos = $totalStmt->fetch(PDO::FETCH_ASSOC)['total'];

// Obtener productos con búsqueda, filtro y paginación $productsQuery = "SELECT nombre, fabricante, CAST(precio AS DECIMAL(10, 2)) AS precio, imagen " . $baseQuery;

// Agregar orden por precio si se especifica if ($priceOrder === 'asc') { $productsQuery .= " ORDER BY precio ASC"; } elseif ($priceOrder === 'desc') { $productsQuery .= " ORDER BY precio DESC"; } else { $productsQuery .= " ORDER BY nombre ASC"; // Orden predeterminado }

// Agregar límites para la paginación $productsQuery .= " LIMIT :limit OFFSET :offset"; $stmt = $pdo->prepare($productsQuery);

if (!empty($search)) { $stmt->bindValue(':search', "%$search%", PDO::PARAM_STR); } $stmt->bindValue(':limit', $limit, PDO::PARAM_INT); $stmt->bindValue(':offset', $offset, PDO::PARAM_INT); $stmt->execute();

$productos = $stmt->fetchAll(PDO::FETCH_ASSOC);

// Preparar respuesta echo json_encode([ 'total' => $totalProductos, 'page' => $page, 'limit' => $limit, 'productos' => $productos, ]); } catch (PDOException $e) { echo json_encode([ 'status' => 'error', 'message' => 'Error al obtener productos: ' . $e->getMessage(), ]); } ?>

5. Crear el frontend (index.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Productos - API REST</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container mt-5">
        <h1 class="text-center mb-4">Productos Disponibles</h1>

<!-- Barra de búsqueda y filtros --> <div class="row mb-4"> <div class="col-md-6"> <input type="text" id="searchInput" class="form-control" placeholder="Buscar producto por nombre..." oninput="fetchProducts()"> </div> <div class="col-md-6"> <select id="priceFilter" class="form-select" onchange="fetchProducts()"> <option value="">Ordenar por precio</option> <option value="asc">Menor precio</option> <option value="desc">Mayor precio</option> </select> </div> </div>

<!-- Contenedor de productos --> <div id="productGrid" class="row row-cols-1 row-cols-md-4 g-4"></div>

<!-- Controles de paginación --> <nav class="mt-4"> <ul class="pagination justify-content-center" id="paginationControls"></ul> </nav> </div>

<script> let currentPage = 1; const limit = 12; const merchantEmail = "tu-correo@comerciante.com"; // Reemplaza con tu correo de PayPal

async function fetchProducts(page = 1) { try { const search = document.getElementById('searchInput').value.trim(); const priceOrder = document.getElementById('priceFilter').value;

const queryParams = new URLSearchParams({ page, limit, search, priceOrder });

const response = await fetch(`fetch-products.php?${queryParams}`); if (!response.ok) throw new Error(`Error: ${response.status}`);

const data = await response.json(); const { productos, total } = data;

const productGrid = document.getElementById('productGrid'); const paginationControls = document.getElementById('paginationControls');

// Limpiar contenido previo productGrid.innerHTML = ''; paginationControls.innerHTML = '';

// Renderizar productos if (productos.length === 0) { productGrid.innerHTML = ` <div class="col-12"> <p class="text-center">No se encontraron productos.</p> </div> `; } else { productos.forEach(product => { const precio = parseFloat(product.precio); // Convertir a número productGrid.innerHTML += ` <div class="col"> <div class="card h-100"> <img src="images/${product.imagen}" class="card-img-top" alt="${product.nombre}"> <div class="card-body"> <h5 class="card-title">${product.nombre}</h5> <p class="card-text">Fabricante: ${product.fabricante}</p> <p class="card-text">Precio: $${precio.toFixed(2)}</p> <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank"> <input type="hidden" name="cmd" value="_xclick"> <input type="hidden" name="business" value="${merchantEmail}"> <input type="hidden" name="item_name" value="${product.nombre}"> <input type="hidden" name="amount" value="${precio.toFixed(2)}"> <input type="hidden" name="currency_code" value="USD"> <button type="submit" class="btn btn-primary w-100">Comprar</button> </form> </div> </div> </div> `; }); }

// Generar controles de paginación const totalPages = Math.ceil(total / limit); for (let i = 1; i <= totalPages; i++) { paginationControls.innerHTML += ` <li class="page-item ${i === page ? 'active' : ''}"> <button class="page-link" onclick="changePage(${i})">${i}</button> </li> `; } } catch (error) { console.error('Error al obtener productos:', error); } }

function changePage(page) { currentPage = page; fetchProducts(page); }

// Cargar la primera página al iniciar document.addEventListener('DOMContentLoaded', () => fetchProducts(currentPage)); </script> </body> </html>

Habilidades Adquiridas en este Tutorial

A lo largo de este tutorial, desarrollaste una aplicación completa que incluye tanto el backend como el frontend para una tienda en línea. En el proceso, adquiriste las siguientes habilidades clave:

  1. Diseño y configuración de una base de datos relacional:
    • Creaste una base de datos MySQL para almacenar productos, aprendiendo a definir estructuras básicas de tablas, tipos de datos, y relaciones entre datos.
  2. Desarrollo de una API REST con PHP:
    • Implementaste una API REST que puede manejar múltiples funcionalidades como la paginación, búsqueda y filtrado por precio.
    • Aprendiste a utilizar métodos seguros para realizar consultas a la base de datos, utilizando sentencias preparadas con PDO para evitar ataques de inyección SQL.
  3. Gestión de parámetros dinámicos:
    • Integramos parámetros como page, limit, search, y priceOrder para proporcionar flexibilidad al cliente y mejorar la experiencia del usuario.
  4. Paginación dinámica en la API y frontend:
    • Diseñaste un sistema de paginación para dividir los resultados en páginas manejables.
    • Implementaste controles de paginación en el frontend que permiten navegar entre páginas con botones dinámicos.
  5. Buscador funcional:
    • Agregaste un campo de búsqueda que interactúa con la API para filtrar productos en tiempo real según el nombre.
  6. Ordenación de datos:
    • Creaste un sistema para ordenar los productos por precio de forma ascendente o descendente, proporcionando una experiencia de compra más intuitiva.
  7. Consumo de APIs REST con Fetch API:
    • Utilizaste Fetch API en JavaScript para consumir la API creada, aprendiendo a manejar solicitudes asíncronas y procesar respuestas en formato JSON.
  8. Diseño responsivo con Bootstrap:
    • Organizaste los productos en una cuadrícula que se adapta automáticamente al tamaño de la pantalla, mostrando cuatro productos por fila en dispositivos más grandes.
  9. Integración de un botón de compra con PayPal:
    • Implementaste un botón que permite a los usuarios realizar pagos utilizando PayPal, facilitando la transacción sin necesidad de configurar una cuenta empresarial o utilizar la API oficial.
  10. Gestión de errores:
    • Configuraste manejadores de errores tanto en el backend como en el frontend para mostrar mensajes útiles al usuario en caso de fallos.
  11. Preparación para la producción:
    • Diseñaste el sistema para ser escalable, modular y compatible con futuras integraciones, como la adición de nuevos métodos de pago o la gestión de usuarios.

Con este conocimiento, ahora puedes abordar proyectos más complejos que involucren integración de APIs, desarrollo de frontend interactivo, y manejo de bases de datos robustas. Si tienes preguntas o deseas expandir esta aplicación (por ejemplo, agregar un carrito de compras, un sistema de usuarios o mejorar la seguridad), ¡puedes seguir aprendiendo y mejorando!

Ver demostración Comprar archivos

 

Obed Alvarado

Sobre el autor

Artículo redactado por Obed Alvarado. Comparto experiencias prácticas sobre PHP, MySQL, aplicaciones web, instalación en hosting y herramientas para negocios.

Comentarios