Creando un API REST para Estadísticas de Basket-ball con PHP, MySQL y PDO
- Obtener vínculo
- X
- Correo electrónico
- Otras apps
En esta entrada del blog vamos a detallar un tutorial de programación web, vamos a construir de la manera más fácil posible, un paso a paso para crear un API RESTful para gestionar estadísticas de partidos de baloncesto utilizando PHP, MySQL y PDO con programación orientada a objetos. Si quieren conocer lo básico de la arquitectura REST, pueden leer la entrada del blog al respecto. El API permitirá realizar operaciones CRUD (Crear, Leer, Actualizar y Eliminar) sobre las estadísticas de los jugadores. La siguiente imagen ejemplifica como funcionaría nuestro pequeño proyecto REST.
¿Qué necesitamos para comenzar?
- Servidor web (Apache, Nginx)
- PHP 7.4 o superior
- MySQL 5.7 o superior
- Conocimientos básicos de PHP y MySQL
/basketball-api /config Database.php /models PlayerStats.php /controllers StatsController.php index.php .htaccess
Como pueden observar, la estructura de trabajo es básicamente considerando el patrón Model-View-Controller, en la siguiente entrada del blog explico de que va esto. Ahora veremos paso a paso como crear nuestra API REST.
INICIAMOS
CREATE DATABASE basketball_stats; USE basketball_stats; CREATE TABLE player_stats ( id INT AUTO_INCREMENT PRIMARY KEY, player_name VARCHAR(100) NOT NULL, points_scored INT NOT NULL, three_points_made INT NOT NULL, fouls_committed INT NOT NULL, game_date DATETIME DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );
Paso 2. Configuración de la conexión PDO. Aquí crearemos el archivo config/Database.php. Este archivo es simplemente una clase para manejar la conexión a nuestra base de datos. Contendrá un método para la conexión, el cual colocaremos dentro de un try-catch, ahí realizaremos una conexión mediante PDO (PHP- Data Object). Considera que deberás ajustar las credenciales según tu configuración de conexión.
class Database { // Configuración de la base de datos private $host = 'localhost'; private $db_name = 'basketball_stats'; private $username = 'root'; private $password = ''; private $conn; // Método para conectar a la base de datos public function connect() { $this->conn = null; try { $this->conn = new PDO( 'mysql:host=' . $this->host . ';dbname=' . $this->db_name, $this->username, $this->password ); $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch(PDOException $e) { echo 'Connection Error: ' . $e->getMessage(); } return $this->conn; } }
class PlayerStats { // Conexión a la base de datos y nombre de la tabla private $conn; private $table_name = 'player_stats'; // Propiedades del objeto public $id; public $player_name; public $points_scored; public $three_points_made; public $fouls_committed; public $game_date; public $created_at; public $updated_at; // Constructor con conexión a la base de datos public function __construct($db) { $this->conn = $db; } // Crear una nueva estadística public function create() { // Consulta SQL para insertar un registro $query = 'INSERT INTO ' . $this->table_name . ' SET player_name = :player_name, points_scored = :points_scored, three_points_made = :three_points_made, fouls_committed = :fouls_committed, game_date = :game_date'; // Preparamos la consulta $stmt = $this->conn->prepare($query); // Limpiamos y vinculamos los parámetros $this->player_name = htmlspecialchars(strip_tags($this->player_name)); $this->points_scored = htmlspecialchars(strip_tags($this->points_scored)); $this->three_points_made = htmlspecialchars(strip_tags($this->three_points_made)); $this->fouls_committed = htmlspecialchars(strip_tags($this->fouls_committed)); $this->game_date = htmlspecialchars(strip_tags($this->game_date)); $stmt->bindParam(':player_name', $this->player_name); $stmt->bindParam(':points_scored', $this->points_scored); $stmt->bindParam(':three_points_made', $this->three_points_made); $stmt->bindParam(':fouls_committed', $this->fouls_committed); $stmt->bindParam(':game_date', $this->game_date); // Ejecutamos la consulta if ($stmt->execute()) { return true; } // Si hay error, mostramos el mensaje printf("Error: %s.\n", $stmt->error); return false; } // Leer todas las estadísticas public function read() { // Consulta SQL $query = 'SELECT * FROM ' . $this->table_name . ' ORDER BY game_date DESC'; // Preparamos la consulta $stmt = $this->conn->prepare($query); // Ejecutamos $stmt->execute(); return $stmt; } // Leer una sola estadística por ID public function readOne() { // Consulta SQL $query = 'SELECT * FROM ' . $this->table_name . ' WHERE id = ? LIMIT 0,1'; // Preparamos la consulta $stmt = $this->conn->prepare($query); // Vinculamos el parámetro ID $stmt->bindParam(1, $this->id); // Ejecutamos $stmt->execute(); // Obtenemos la fila $row = $stmt->fetch(PDO::FETCH_ASSOC); // Asignamos valores a las propiedades del objeto if ($row) { $this->player_name = $row['player_name']; $this->points_scored = $row['points_scored']; $this->three_points_made = $row['three_points_made']; $this->fouls_committed = $row['fouls_committed']; $this->game_date = $row['game_date']; } } // Actualizar una estadística public function update() { // Consulta SQL $query = 'UPDATE ' . $this->table_name . ' SET player_name = :player_name, points_scored = :points_scored, three_points_made = :three_points_made, fouls_committed = :fouls_committed, game_date = :game_date WHERE id = :id'; // Preparamos la consulta $stmt = $this->conn->prepare($query); // Limpiamos y vinculamos los parámetros $this->player_name = htmlspecialchars(strip_tags($this->player_name)); $this->points_scored = htmlspecialchars(strip_tags($this->points_scored)); $this->three_points_made = htmlspecialchars(strip_tags($this->three_points_made)); $this->fouls_committed = htmlspecialchars(strip_tags($this->fouls_committed)); $this->game_date = htmlspecialchars(strip_tags($this->game_date)); $this->id = htmlspecialchars(strip_tags($this->id)); $stmt->bindParam(':player_name', $this->player_name); $stmt->bindParam(':points_scored', $this->points_scored); $stmt->bindParam(':three_points_made', $this->three_points_made); $stmt->bindParam(':fouls_committed', $this->fouls_committed); $stmt->bindParam(':game_date', $this->game_date); $stmt->bindParam(':id', $this->id); // Ejecutamos if ($stmt->execute()) { return true; } return false; } // Eliminar una estadística public function delete() { // Consulta SQL $query = 'DELETE FROM ' . $this->table_name . ' WHERE id = :id'; // Preparamos la consulta $stmt = $this->conn->prepare($query); // Limpiamos y vinculamos el parámetro ID $this->id = htmlspecialchars(strip_tags($this->id)); $stmt->bindParam(':id', $this->id); // Ejecutamos if ($stmt->execute()) { return true; } return false; } }
El modelo PlayerStats es como un intermediario entre la base de datos y el controlador. Básicamente, tiene tres funciones principales, estas son:
Representar los datos de un jugador de baloncesto:player_name
(nombre del jugador)points_scored
(puntos anotados)three_points_made
(triples encestados)fouls_committed
(faltas cometidas)game_date
(fecha del partido)
create()
→ Guarda una nueva estadística.read()
→ Obtiene todas las estadísticas.readOne()
→ Busca una estadística por su ID.update()
→ Modifica una estadística existente.delete()
→ Elimina una estadística.
Paso 4. Crear el controlador StatsController. El controlador es la parte encargada de gestionar la interacción entre el modelo y la vista, actuando como un intermediario entre estos dos componentes. Su principal responsabilidad es procesar las entradas del usuario, tomar las decisiones adecuadas y luego actualizar tanto el modelo como la vista según sea necesario. El controlador manejará todas las solicitudes HTTP (GET, POST, PUT, DELETE) y las dirige a los métodos correspondientes del modelo. En controllers/StatsController.php colocaremos el siguiente código fuente:
header("Access-Control-Allow-Origin: *"); header("Content-Type: application/json; charset=UTF-8"); header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE"); header("Access-Control-Max-Age: 3600"); header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"); require_once '../config/Database.php'; require_once '../models/PlayerStats.php'; class StatsController { private $db; private $playerStats; public function __construct() { $database = new Database(); $this->db = $database->connect(); $this->playerStats = new PlayerStats($this->db); } // Procesar la solicitud public function processRequest() { $method = $_SERVER['REQUEST_METHOD']; $id = isset($_GET['id']) ? $_GET['id'] : null; switch ($method) { case 'GET': if ($id) { $this->getStat($id); } else { $this->getAllStats(); } break; case 'POST': $this->createStat(); break; case 'PUT': if ($id) { $this->updateStat($id); } break; case 'DELETE': if ($id) { $this->deleteStat($id); } break; default: http_response_code(405); echo json_encode(["message" => "Método no permitido"]); break; } } // Obtener todas las estadísticas private function getAllStats() { $stmt = $this->playerStats->read(); $num = $stmt->rowCount(); if ($num > 0) { $stats_arr = array(); $stats_arr["records"] = array(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { extract($row); $stat_item = array( "id" => $id, "player_name" => $player_name, "points_scored" => $points_scored, "three_points_made" => $three_points_made, "fouls_committed" => $fouls_committed, "game_date" => $game_date, "created_at" => $created_at, "updated_at" => $updated_at ); array_push($stats_arr["records"], $stat_item); } http_response_code(200); echo json_encode($stats_arr); } else { http_response_code(404); echo json_encode(["message" => "No se encontraron estadísticas."]); } } // Obtener una sola estadística private function getStat($id) { $this->playerStats->id = $id; $this->playerStats->readOne(); if ($this->playerStats->player_name != null) { $stat_arr = array( "id" => $this->playerStats->id, "player_name" => $this->playerStats->player_name, "points_scored" => $this->playerStats->points_scored, "three_points_made" => $this->playerStats->three_points_made, "fouls_committed" => $this->playerStats->fouls_committed, "game_date" => $this->playerStats->game_date, "created_at" => $this->playerStats->created_at, "updated_at" => $this->playerStats->updated_at ); http_response_code(200); echo json_encode($stat_arr); } else { http_response_code(404); echo json_encode(["message" => "Estadística no encontrada."]); } } // Crear nueva estadística private function createStat() { $data = json_decode(file_get_contents("php://input")); if ( !empty($data->player_name) && isset($data->points_scored) && isset($data->three_points_made) && isset($data->fouls_committed) ) { $this->playerStats->player_name = $data->player_name; $this->playerStats->points_scored = $data->points_scored; $this->playerStats->three_points_made = $data->three_points_made; $this->playerStats->fouls_committed = $data->fouls_committed; $this->playerStats->game_date = isset($data->game_date) ? $data->game_date : date('Y-m-d H:i:s'); if ($this->playerStats->create()) { http_response_code(201); echo json_encode(["message" => "Estadística creada correctamente."]); } else { http_response_code(503); echo json_encode(["message" => "No se pudo crear la estadística."]); } } else { http_response_code(400); echo json_encode(["message" => "Datos incompletos."]); } } // Actualizar estadística private function updateStat($id) { $data = json_decode(file_get_contents("php://input")); $this->playerStats->id = $id; $this->playerStats->readOne(); if ($this->playerStats->player_name == null) { http_response_code(404); echo json_encode(["message" => "Estadística no encontrada."]); return; } $this->playerStats->player_name = isset($data->player_name) ? $data->player_name : $this->playerStats->player_name; $this->playerStats->points_scored = isset($data->points_scored) ? $data->points_scored : $this->playerStats->points_scored; $this->playerStats->three_points_made = isset($data->three_points_made) ? $data->three_points_made : $this->playerStats->three_points_made; $this->playerStats->fouls_committed = isset($data->fouls_committed) ? $data->fouls_committed : $this->playerStats->fouls_committed; $this->playerStats->game_date = isset($data->game_date) ? $data->game_date : $this->playerStats->game_date; if ($this->playerStats->update()) { http_response_code(200); echo json_encode(["message" => "Estadística actualizada correctamente."]); } else { http_response_code(503); echo json_encode(["message" => "No se pudo actualizar la estadística."]); } } // Eliminar estadística private function deleteStat($id) { $this->playerStats->id = $id; $this->playerStats->readOne(); if ($this->playerStats->player_name == null) { http_response_code(404); echo json_encode(["message" => "Estadística no encontrada."]); return; } if ($this->playerStats->delete()) { http_response_code(200); echo json_encode(["message" => "Estadística eliminada correctamente."]); } else { http_response_code(503); echo json_encode(["message" => "No se pudo eliminar la estadística."]); } } }
El controlador en este ejemplo (StatsController) actúa como el intermediario entre las solicitudes HTTP (que llegan al API) y el modelo (PlayerStats) que interactúa con la base de datos. Su función principal es procesar las solicitudes entrantes, para lo cual detecta el método HTTP (GET, POST, PUT, DELETE) y extrae parámetros como el ID (si está presente en la URL) para poder decidir qué acción ejecutar según el tipo de solicitud.
Cada método del controlador maneja una operación específica:
getAllStats()
(GET sin ID): Llama al métodoread()
del modelo. Devuelve un JSON con todas las estadísticas almacenadas.getStat($id)
(GET con ID). Busca una sola estadística usando el ID. Si no existe, devuelve un error 404 (Not Found).createStat()
(POST). Lee los datos del cuerpo de la solicitud (php://input
). Valida que los campos obligatorios estén presentes. Si todo es correcto, guarda los datos en la base de datos y devuelve 201 (Created).updateStat($id)
(PUT). Actualiza solo los campos proporcionados (el resto se mantienen igual). Si el ID no existe, devuelve 404 (Not Found).deleteStat($id)
(DELETE). Elimina una estadística por su ID. Si no existe, devuelve 404 (Not Found).
require_once 'controllers/StatsController.php'; $controller = new StatsController(); $controller->processRequest();
Paso 6. Configurar .htaccess para URL amigables. Este archivo redirige todas las solicitudes a index.php para un enrutamiento limpio.
RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L]
Ahora obtendremos todas las estadísticas (GET).URL: http://localhost/basketball-api/ Método: POST Body (raw JSON): { "player_name": "LeBron James", "points_scored": 28, "three_points_made": 4, "fouls_committed": 2 }
URL: http://localhost/basketball-api/ Método: GET
Obtener una estadística específica (GET), agregamos el id con el número de estadística que queremos obtener.{ "records": [ { "id": 1, "player_name": "LeBron James", "points_scored": 28, "three_points_made": 4, "fouls_committed": 2, "game_date": "2023-11-15 14:30:00", "created_at": "2023-11-15 15:45:23", "updated_at": "2023-11-15 15:45:23" }, { "id": 2, "player_name": "Stephen Curry", "points_scored": 35, "three_points_made": 7, "fouls_committed": 1, "game_date": "2023-11-16 20:15:00", "created_at": "2023-11-16 21:30:10", "updated_at": "2023-11-16 21:30:10" } ] }
URL: http://localhost/basketball-api/?id=1
Método: GET
Respuesta al obtener una estadística específica (GET /?id=1)Actualizar una estadística (PUT).{ "id": 1, "player_name": "LeBron James", "points_scored": 28, "three_points_made": 4, "fouls_committed": 2, "game_date": "2023-11-15 14:30:00", "created_at": "2023-11-15 15:45:23", "updated_at": "2023-11-15 15:45:23" }
Eliminar una estadística (DELETE).URL: http://localhost/basketball-api/?id=1 Método: PUT Body (raw JSON): { "points_scored": 32, "three_points_made": 5 }
URL: http://localhost/basketball-api/?id=1
Método: DELETE
Respuesta al eliminar una estadística.
{
"message": "Estadística eliminada correctamente."
}
En este tutorial hemos desarrollado un API RESTful completo para gestionar estadísticas de baloncesto utilizando PHP, MySQL y PDO con programación orientada a objetos, implementando un sistema seguro de conexión a base de datos con PDO, un modelo que representa la entidad PlayerStats, un controlador para manejar solicitudes HTTP, operaciones CRUD completas, validación básica de datos y respuestas JSON con códigos de estado HTTP apropiados. Esta API puede ampliarse con funcionalidades adicionales como autenticación, paginación, filtrado avanzado o relaciones con otras tablas (como equipos o partidos), entre otras mejoras, para adaptarse a necesidades más complejas en un entorno de producción.
Es cuánto.
Si quieres citar este artículo en tu texto, documento, etc. puedes hacerlo de la siguiente forma:
Aguilar-Calderón, J. A. (27 de marzo de 2025). Creando un API REST para Estadísticas de Basket-ball con PHP, MySQL y PDO. ANOVA LAB MX. https://anovalabmx.blogspot.com/2025/03/creando-un-api-rest-para-estadisticas.html

- José Alfonso Aguilar
- Mazatlán, Sinaloa, Mexico
-
Me gusta aprender y escribir sobre tecnología y desarrollo. Soy Ingeniero en Sistemas Computacionales, trabajo como Profesor-Investigador en la Facultad de Informática Mazatlán, de la Universidad Autónoma de Sinaloa. México. Me gusta combinar la docencia-investigación con el giro profesional del desarrollo de software y gestión de proyectos de innovación.
Comentarios
Publicar un comentario
Gracias por tu comentario ;) Ayúdanos a difundir este blog !!