Proyecto Wari · Symfony 7.3 / PHP 8.2
cascade: ['persist', 'remove']. Eliminar un usuario borra en cascada todos sus eventos e invitaciones sin posibilidad de recuperación.flush() individual. En operaciones que generan múltiples logs se ejecutan N queries extra fuera de la transacción principal.sleep en el hilo principal. La sincronización con BoardGameGeek congela la aplicación. Ya existe SincronizarColeccionHandler asíncrono que debería absorber toda esta lógica.array_map, serialización JSON directamente en el controlador.GrupoService inyecta 10 dependencias y gestiona creación, membresía, invitaciones, expulsiones, Telegram, auditoría y notificaciones. EventoService inyecta 7. Servicios "dios".TelegramBotRepository inyectado y usado en el controlador sin pasar por ningún servicio. Rompe la separación de capas.innerJoin sobre miembros sin addSelect(). Al iterar resultados y acceder a la colección de miembros, Doctrine ejecuta una query adicional por cada grupo.nombre, email y usuarioBgg en Usuario sin constraints. Las validaciones solo existen en formularios. Código que persiste vía API o servicio directo no valida nada.hasPermission()), en el servicio (can()) y como chequeos ad-hoc en el controlador. Tres fuentes de verdad distintas para el mismo concepto.EventoService y GrupoService.getPermalink() construye una URL con el dominio antiguo bfbg.birdez.ginernet.net hardcodeado. Lógica de infraestructura dentro de una entidad de dominio.'bfbg@asociacion.local' hardcodeado en la llamada a la API de BoardGameGeek. Nombre del proyecto antiguo, valor no configurable.fetch: 'LAZY' explícito en varias entidades. Ausencia de declaración explícita genera confusión y bugs potenciales con proxies de Doctrine.grupoPermisoService->can() dispersas en el controlador. Sin Voter, la lógica de permisos es difícil de centralizar, testear y extender.u.roles LIKE '%ROLE_ADMIN%' es frágil si el formato del JSON cambia. Debería usar JSON_CONTAINS de MySQL.scheb/2fa soporta backup codes de forma nativa.flush(), dejando esa responsabilidad al código llamador. Inconsistente con AuditService. El patrón no está documentado.flush() dentro del mismo flujo. Debería ser una sola transacción atómica.GROUP BY, HAVING y ORDER BY sobre campos que probablemente carecen de índices en la BD.countDisponibles() itera la colección en memoria e implementa lógica de negocio, generando un N+1 potencial en plantillas.try/catch → addFlash() + redirectToRoute() se repite en cuatro acciones distintas del controlador.$grupoMiembroRepository->isMember() comprobado manualmente en tres métodos distintos del mismo controlador.TelegramGrupo gestionados directamente en el controlador.foreach (array_chunk(...)) idénticos con el mismo patrón de sleep y retry.findOneBy([...]) !== null. Candidatos a un método privado existsBy().PermisoGrupo::cases(). Validación en setters de entidades es frágil y difícil de testear. El setter debe ser dumb.GrupoController. Debería estar en TelegramService.can() (devuelve bool) pero también lanza AccessDeniedHttpException en otros flujos. La interfaz es confusa.bannedAt, creadoEn, fechaCreacion entre distintas entidades.find(['grupo' => $grupo, 'usuario' => $usuario]) debería ser findOneBy(). Funciona, pero es semánticamente incorrecto.GrupoController, votación en EventoController, sin principio explícito.strlen() puede dar longitud incorrecta con caracteres multibyte en contraseñas.->set('n.leida', 'true') usa string en lugar de booleano PHP. Funciona en MySQL pero es semánticamente incorrecto.Evento::getPermalink()DT-029.envDT-030#[Assert] en entidades críticas (Usuario, Evento)DT-011addSelect()DT-008hasPermission() de la entidadDT-027