Cómo organizar tu código PHP en proyectos pequeños
En este post vamos a ver cómo organizar un proyecto sencillo en PHP.
Seguro, que cuando empezaste con PHP o si estas empezando, habrás visto código PHP mezclado con HTML, luego un archivo por aquí, otro archivo por allá y, en definitiva, un poco de caos ¿no?
Pues bien, el objetivo de este articulo es tener una mejor idea de como organizar nuestro código y el proyecto en sí. Vamos a verlo de una forma sencilla para que a partir de aquí tengamos una base.
Como he comentado, cuando comenzamos un proyecto en PHP, es común empezar con un único archivo que mezcla HTML, lógica de negocio, consultas SQL, etc. Esto no es que este mal, porque al final funcionar funciona, pero a medida que el proyecto crezca se volverá más difícil de mantener.
¿Entonces que hacemos?
Pues la clave está en aplicar una separación de responsabilidades y si, incluso en proyectos pequeños. No hay necesidad de usar un framework pesado como Laravel o Symfony.
¡Pongámonos manos a la obra!

Un ejemplo típico
Un ejemplo típico en proyectos pequeños es algo así:
<?php // conexión a base de datos $conn = new PDO("mysql:host=localhost;dbname=noticias", "usuario", "clave"); $id = $_GET['id']; $stmt = $conn->prepare("SELECT * FROM noticias WHERE id = ?"); $stmt->execute([$id]); $articulo = $stmt->fetch(); // vista echo "<h1>" . $articulo['titulo'] . "</h1>"; echo "<p>" . $articulo['contenido'] . "</p>";
Bfff que recuerdos de los inicios…
En el ejemplo anterior, aunque es funcional y seguro que lo hemos hecho más de una vez, tenemos el problema de que mezclamos conexión, lógica y vista en un mismo archivo. Esto dificulta escalar, depurar y reutilizar el código. Y si, nos impide también convertirnos en buenos programadores, ¡es lo que hay! 🤷♂️ Entonces… la solución pasa por aplicar el famoso MVC que consiste en dividir el código en capas, concretamente en tres:
- Modelo (M): interacción con la base de datos.
- Controlador (C): lógica de negocio.
- Vista (V): presentación en HTML.
Aviso: No vamos a ver el patrón MVC como tal, solo lo vamos a simular de manera ligera y sin usar frameworks.
A efectos prácticos vamos a suponer que queremos crear una web de noticias.
Bueno perdona, como somos modernos y eso de “web” suena como de la prehistoria digital y hoy le llamamos a todo lo que carga en un navegador “app” (ya sabes, porque queda más moderno y eso), mejor digamos:
“Vamos a crear una app de noticias”.
Organización del proyecto
mi-proyecto/ public/ # Archivos accesibles desde el navegador (index.php, css, js) app/ Models/ # Modelos (consultas a BD) Controllers/ # Lógica de negocio Views/ # Plantillas HTML config/ # Configuración (ej. base de datos) vendor/ # Librerías externas (si usamos Composer)
Evidentemente esto es solo una idea, sino te gusta, puedes hacerlo de otra manera, pero créeme si te digo que esta es una de las maneras más aceptadas, así que no está mal empezar así 😉
1. Configuración
Es importante acostumbrarnos a tener un punto central donde esten todas las configuraciones de nuestra aplicación/web, como los datos de la base de datos, la ruta base, etc.
Para simplificación, en nuestro caso solo incluiremos los datos de configuración de nuestra base de datos.
Por ejemplo, la ruta será config/database.php y su código el siguiente:
<?php return [ 'dsn' => 'mysql:host=localhost;dbname=noticias', 'user' => 'usuario', 'password' => 'clave' ];
2. Modelo
Recordemos que el modelo es la capa que se encarga de la lógica de negocio y todo lo relacionado con los datos:
<?php class Noticia { private $db; public function __construct($pdo) { $this->db = $pdo; } public function obtenerPorId($id) { $stmt = $this->db->prepare("SELECT * FROM noticias WHERE id = ?"); $stmt->execute([$id]); return $stmt->fetch(); } }
3. Controlador
El controlador es la capa intermedia que se encarga de recibir las peticiones del usuario y de comunicárselas al modelo, es decir, algo así como… “oye tú, el usuario quiere los datos de todos los usuarios registrados, ¡dámelos!”
Bueno, quizás no con ese tono, pero ya me entiendes 😆
Por otra parte, decimos que es intermedia porque también hace de intermediario (valga la redundancia) con la capa de la vista, es decir, una vez obtenidos los datos del modelo, se encarga de pasárselos a la vista:
<?php require_once __DIR__ . '/../Models/Noticia.php'; class NoticiasController { private $modelo; public function __construct($pdo) { $this->modelo = new Noticia($pdo); } public function mostrar($id) { $noticia = $this->modelo->obtenerPorId($id); require __DIR__ . '/../Views/noticia.php'; } }
4. Vista
Es simple, se encarga de mostrar el resultado final de la petición en un formato adecuado.
<!DOCTYPE html> <html> <head> <title><?= htmlspecialchars($noticia['titulo']) ?></title> </head> <body> <h1><?= htmlspecialchars($noticia['titulo']) ?></h1> <p><?= nl2br(htmlspecialchars($noticia['contenido'])) ?></p> </body> </html>
5. Punto de entrada
Normalmente se redirige todo a un punto de entrada (un archivo index.php) mediante un archivo .htaccess. A partir de ahí todas las peticiones se envían a un lado u otro, por eso, aquí entraría en juego los llamados routers (que por simplicidad del articulo no vamos a ver). Por tanto, un punto de entrada simplón sería:
<?php $config = require __DIR__ . '/../config/database.php'; $pdo = new PDO($config['dsn'], $config['user'], $config['password']); require __DIR__ . '/../app/Controllers/NoticiasController.php'; $controller = new NoticiasController($pdo); // Obtener id desde URL (ej: index.php?id=2) $id = $_GET['id'] ?? 1; $controller->mostrar($id);
Como vemos todas las peticiones de noticias están en un único punto, el archivo index.php. A partir de este punto se accede a todas las noticias que tengamos.
Beneficios de esta organización
Quizás al no estar demasiado desarrollado y por ser un ejemplo bastante simple, no se aprecien del todo los beneficios, pero creo que algunos sí que son visibles.
Por ejemplo, podemos apreciar que, si nos acostumbramos a organizar nuestros proyectos de esta manera, podemos cambiar la vista sin necesidad de toca la lógica o la base de datos, es decir, obtenemos algo reutilizable.
Por otro lado, otro beneficio es la escalabilidad. Si el proyecto va creciendo, partimos de una estructura básica pero clara.
Esto nos lleva otra ventaja que es el mantenimiento, pues cada archivo cumple una función específica permitiéndonos un mantenimiento más sencillo.
Por último, si nos acostumbramos a organizar nuestros proyectos de esta manera, estamos empezando a cumplir con las buenas prácticas desde un inicio, pues aplicamos principios básicos de arquitectura sin necesidad de frameworks pesados (por ahora 🤭)
Porque es verdad, no necesitamos un framework para tener una mínima separación de responsabilidades en PHP. Basta con organizar carpetas, dividir lógica en modelos, controladores y vistas, y centralizar configuraciones. Y si, muchas cosas mas que no hemos visto aquí como el uso de espacios de nombres (namespaces), interfaces, urls amigables, , un archivo .htaccess, etc.
PSR (PHP Standards Recommendations)
Estaría mal por mi parte hablar de como organizamos la estructura de nuestros proyectos y no hablar de las recomendaciones o guías de estilo de PHP o PSR (PHP Standards Recommendations en inglés) o al menos, verlas un poco por encima. Las PSR, son estándares propuestos por el PHP-FIG (PHP Framework Interop Group).
Algunas de las más importantes son:
PSR-1. Normas básicas de codificación
Se encarga de establecer unas reglas mínimas a cumplir como el uso de la etiqueta de inicio de código php <?php, el nombrado de clases en UpperCamelCase, métodos en lowerCamelCase, etc.
Establece cómo organizar namespaces y carpetas para que las clases se carguen automáticamente con un autoloader (ej. Composer).
PSR-12. Estándar de estilo de código
Extiende un poco más el PSR-1 con recomendaciones de formato, es decir, aplicar indentación con 4 espacios, llaves en la misma línea para clases y funciones, un espacio después de cada coma, etc.
Por ejemplo, sin ninguna recomendación (al final recordemos que son recomendaciones y nadie nos obliga):
Sin PSR-12:
class articulo{ function get_titulo(){ return "Titulo"; } }
Con PSR-12:
class Articulo { public function getTitulo(): string { return "Título"; } }
Como ves, aplicar PSR no cambia la funcionalidad de nuestra aplicación, pero sí que mejora la legibilidad y la coherencia de nuestro código.
Incluso en proyectos pequeños, acostumbrarte a estas prácticas hará que tu código sea más profesional y te facilitará trabajar en equipo o migrar a frameworks más grandes.
Yyyyyyy ya está! Eso es todo por ahora. Espero que una vez más hayas llegado hasta aquí y te haya resultado útil 😉
Sobre el autor
Este artículo está publicado bajo una licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional . Puedes compartirlo y adaptarlo, incluso con fines comerciales, siempre que cites al autor y mantengas esta misma licencia.