🏆 Pint League
Reescritura completa de DrinkLegends sobre PHP sin framework: router, JWT y push implementados a mano
Escala del Proyecto
Descripción General
Pint League es la segunda generación de DrinkLegends. El producto es el mismo — grupos, rondas con foto, muro con likes y comentarios, rankings, MVP, logros, chat, amigos y juegos — pero la implementación se rehízo por completo: fuera Laravel, todo en PHP sin framework.
Eso significó escribir a mano las piezas que un framework regala: el router con soporte de parámetros en la ruta, la autenticación JWT con firma y verificación, el servicio de notificaciones push y el procesado de fotos. Nueve controladores cubren las 40 rutas de la API.
El frontend también es vanilla: una SPA con router propio y ocho vistas, sin React
ni build. Y existe migrate_from_drinklegends.php, un script que
traslada los datos de la versión anterior, porque una reescritura que pierde los
datos de los usuarios no es una reescritura, es un producto nuevo.
Tecnologías Utilizadas
PHP vanilla
MySQL
JWT propio
Router propio
Web Push
SPA vanilla
PWA
PhotoService
Los 9 Controladores de la API
Registro, login, refresh de token, perfil, actualización de datos y cambio de contraseña.
Creación y acceso a bares (grupos), edición, bebidas personalizadas y salida del grupo.
Registro de rondas, muro del grupo, likes, comentarios y rondas propias del usuario.
Clasificación del grupo, histórico de MVP y ranking global.
Catálogo de logros con el progreso del usuario.
Mensajería del grupo: listado y envío.
Búsqueda de usuarios, solicitudes, aceptación, rechazo y eliminación de amigos.
Verdad o Reto, ¿Quién es más probable?, Yo nunca he… y la ruleta del grupo.
Registro del dispositivo para recibir notificaciones push.
Arquitectura
Backend estructurado como un mini-framework propio: Router resuelve las
rutas con parámetros dinámicos, Auth y Jwt gestionan la
autenticación, Database encapsula PDO, y PushService y
PhotoService aíslan las dos operaciones con efectos externos.
El frontend es una SPA sin build: router.js gestiona la navegación
entre las ocho vistas de views/, api.js centraliza las
llamadas y ui.js los componentes compartidos.
PintLeague/ ├── index.php · .htaccess ├── backend/src/ │ ├── Router.php # rutas con parámetros │ ├── routes.php # las 40 rutas │ ├── Auth.php · Jwt.php # autenticación │ ├── Database.php # capa PDO │ ├── PushService.php # notificaciones │ ├── PhotoService.php # procesado de imágenes │ └── Controllers/ # los 9 controladores ├── backend/migrate/ │ └── migrate_from_drinklegends.php # traspaso de datos └── frontend/ ├── index.html · sw.js · manifest.webmanifest ├── js/ # app, api, router, ui, players └── js/views/ # 8 vistas de la SPA
Las 40 Rutas por Área
- Autenticación — registro, login, refresh, perfil, edición y cambio de contraseña.
- Bares — listar, crear, unirse por código, ver, editar, bebidas personalizadas y salir.
- Rondas y muro — muro del grupo, registrar ronda, like, comentarios y rondas propias.
- Rankings — clasificación del grupo, histórico de MVP y ranking global.
- Logros — catálogo con progreso.
- Chat — mensajes del grupo: leer y enviar.
- Amigos — buscar, listar, solicitar, aceptar, rechazar y eliminar.
- Juegos — Verdad o Reto, ¿Quién es más probable?, Yo nunca he… y ruleta.
- Push — registro del dispositivo para notificaciones.
Seguridad Implementada
- JWT propio: generación, firma y verificación implementadas en
Jwt.php. - Endpoint de refresh para renovar el token sin volver a pedir credenciales.
- Separación explícita entre rutas públicas (registro, login, refresh) y privadas.
- Contraseñas hasheadas; el cambio de contraseña exige la actual.
- Consultas mediante la capa
Databasecon PDO y sentencias preparadas. backend/public/uploads/protegido con.htaccesseindex.html.- Configuración separada en
config.phpyconfig.local.php.
Retos Técnicos y Decisiones de Diseño
¿Por qué reescribir algo que funcionaba?
DrinkLegends en Laravel funcionaba, pero arrastraba el peso del framework para un producto que en realidad hace cuatro cosas: autenticar, guardar rondas, calcular rankings y notificar. Sobre PHP plano la aplicación despliega en cualquier hosting compartido sin Composer, y el coste de mantenimiento deja de depender de las versiones de un framework ajeno.
Escribir el JWT a mano
Un JWT es tres bloques en base64url separados por puntos, con una firma HMAC del
encabezado y el cuerpo. Implementarlo en Jwt.php obliga a resolver el
base64url — que no es el base64 estándar —, la comparación de firmas en tiempo
constante y la validación de la expiración. Es el tipo de código en el que un atajo
se convierte en un agujero de seguridad.
Un router con parámetros en 100 líneas
Router.php soporta rutas como
/api/groups/{groupId}/rounds: hay que convertir el patrón en una
expresión regular, extraer los parámetros con nombre y despachar al par
controlador-método. Es la pieza que hace que las 40 rutas de routes.php
se lean como una tabla y no como un switch.
Migrar los datos, no solo el código
Una reescritura que obliga a los usuarios a empezar de cero pierde los puntos, los
logros y el histórico, que es justamente el valor acumulado del producto.
migrate_from_drinklegends.php traslada los datos de la versión Laravel
al nuevo esquema; sin ese script la reescritura no habría sido viable.
Mi Rol
Desarrollador full stack — autor único.
Router, capa de autenticación con JWT, servicio de push, procesado de fotos, los nueve controladores, la SPA vanilla con su router y el script de migración desde DrinkLegends.
Estado del Proyecto
Aplicación funcional que sustituye a DrinkLegends. La API cubre las 40 rutas y la SPA sus ocho vistas. Incluye scripts de migración tanto desde DrinkLegends como para producción.
Código y Enlaces
Repositorio no publicado por contener configuración de despliegue. Puedo mostrar el código — en particular el router y la implementación del JWT — en una entrevista.