Creando un API REST para Estadísticas de Basket-ball con PHP, MySQL y PDO
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
{
"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"
}
]
}
Obtener una estadística específica (GET), agregamos el id con el número de estadística que queremos obtener.URL: http://localhost/basketball-api/?id=1
Método: GET
Respuesta al obtener una estadística específica (GET /?id=1){
"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"
}
Actualizar una estadística (PUT).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
Comentarios
Publicar un comentario
Gracias por tu comentario ;) Ayúdanos a difundir este blog !!