Contenido
- Fases del proyecto
- Fase 1: repaso de la versión inicial
- Fase 2: análisis de las clases necesarias
- Fase 3: estructura de carpetas del proyecto
- Fase 4: creación de la clase Conexion
- Fase 5: creación de la clase Producto
- Fase 6: creación de la clase Categoria
- Fase 7: creación de la clase Usuario
- Fase 8: qué es una clase DAO
- Fase 9: creación de ProductoDAO
- Fase 10: mostrar el catálogo usando objetos
- Fase 11: alta de productos usando objetos
- Fase 12: listado administrativo usando objetos
- Fase 13: obtener un producto por ID
- Fase 14: actualización de productos
- Fase 15: borrado lógico de productos
- Fase 16: gestión de categorías con CategoriaDAO
- Fase 17: login usando UsuarioDAO
- Fase 18: protección de páginas privadas
- Fase 19: subida de imágenes usando una función auxiliar
- Fase 20: autoload básico de clases
- Fase 21: diagrama de clases
- Fase 22: diagrama de base de datos
- Fase 23: preparación para hosting
- Fase 24: ejecución en Docker
- Fase 25: documentación final del proyecto
- 26. Qué aprenderemos con esta versión
- 27. Conclusión
En este proyecto vamos a desarrollar una aplicación web completa utilizando PHP orientado a objetos, MySQL, HTML y CSS. La temática será una tienda online de cuencos tibetanos llamada Sonido Interior.
Este proyecto es una evolución del proyecto inicial realizado con PHP básico. En la primera versión trabajamos con páginas PHP sencillas, formularios, includes, conexión directa a base de datos y consultas SQL escritas en cada archivo.
En esta nueva versión vamos a dar un paso más importante: organizaremos el código mediante clases, separaremos responsabilidades y construiremos una estructura más cercana a la que se utiliza en aplicaciones web reales.
El objetivo no es complicar el proyecto sin necesidad, sino aprender a programar de forma más ordenada, reutilizable y mantenible.
1. Objetivo general del proyecto
Vamos a crear una tienda web de cuencos tibetanos con una parte pública y una parte administrativa.
La parte pública permitirá ver la página de inicio, consultar el catálogo de productos y visualizar información de la tienda.
La parte administrativa permitirá iniciar sesión, añadir productos, listar productos, editar productos y borrar productos.
La diferencia principal respecto a la versión anterior será la forma de organizar el código.
En lugar de escribir toda la lógica directamente en archivos como producto-guardar.php, producto-editar.php o catalogo.php, crearemos clases encargadas de realizar esas tareas.
Por ejemplo, tendremos clases como:
ProductoCategoriaUsuarioConexionProductoDAOCategoriaDAOUsuarioDAO
Con esto empezaremos a trabajar una arquitectura más limpia y profesional.
2. Qué significa usar programación orientada a objetos
La programación orientada a objetos, o POO, es una forma de programar basada en clases y objetos.
Una clase es como un molde. Define qué datos tendrá un elemento y qué acciones podrá realizar.
Un objeto es una instancia concreta de una clase.
Por ejemplo, en nuestro proyecto podemos tener una clase Producto.
Esa clase representa cualquier producto de la tienda.
class Producto {
private $idProducto;
private $nombre;
private $descripcion;
private $precio;
private $stock;
private $imagen;
}
Después, un cuenco tibetano concreto sería un objeto de esa clase.
$producto = new Producto();
La ventaja de este enfoque es que el código queda mejor organizado. En lugar de trabajar con variables sueltas y consultas repartidas por muchas páginas, agrupamos la información y el comportamiento en clases.
3. Diferencia entre la versión básica y la versión POO
En la versión básica del proyecto, la conexión a la base de datos y las consultas pueden estar escritas directamente dentro de las páginas.
Por ejemplo:
include("includes/conexion.php");
$sql = "SELECT * FROM productos";
$resultado = $conexion->query($sql);
En la versión orientada a objetos, intentaremos que la página no tenga que saber cómo se hace la consulta.
La página pedirá los productos a una clase especializada:
$productoDAO = new ProductoDAO();
$productos = $productoDAO->obtenerTodos();
La página solo se encargará de mostrar los datos. La clase ProductoDAO será la encargada de hablar con la base de datos.
Esto hace que el proyecto sea más fácil de mantener, ampliar y corregir.
4. Tecnologías que vamos a utilizar
En esta versión seguiremos utilizando las mismas tecnologías principales:
- HTML.
- CSS.
- PHP.
- MySQL o MariaDB.
- Apache.
- phpMyAdmin.
- Hosting o contenedor Docker.
La diferencia es que en PHP trabajaremos con:
- Clases.
- Objetos.
- Atributos.
- Métodos.
- Constructores.
- Encapsulación.
- Getters y setters.
- Clases DAO.
- Separación de responsabilidades.
- Consultas preparadas.
- Sesiones.
- Autoload básico o includes de clases.
5. Resultado final esperado
Al finalizar esta versión del proyecto tendremos una aplicación web parecida a la anterior, pero con una organización interna mucho mejor.
La aplicación incluirá:
- Página de inicio.
- Catálogo público de productos.
- Página de login.
- Zona administrativa.
- Alta de productos.
- Listado administrativo.
- Edición de productos.
- Borrado lógico de productos.
- Subida de imagen por producto.
- Conexión a base de datos mediante una clase.
- Entidades representadas mediante clases.
- Clases DAO para acceder a la base de datos.
- Uso de sesiones para proteger la administración.
- Código más limpio y reutilizable.
Fases del proyecto
Fase 1: repaso de la versión inicial
Antes de empezar con la versión orientada a objetos, revisaremos brevemente la versión anterior del proyecto.
Recordaremos cómo funcionaba:
- Las páginas públicas.
- Las páginas administrativas.
- Los formularios.
- Los includes.
- La conexión a la base de datos.
- Las consultas
SELECT,INSERT,UPDATEyDELETE. - La subida de imágenes.
- El login con sesiones.
Esto es importante porque la versión POO no cambia la finalidad del proyecto. Lo que cambia es la forma de organizar el código.
Fase 2: análisis de las clases necesarias
El primer paso será pensar qué elementos importantes tiene nuestro proyecto.
En una tienda de cuencos tibetanos podemos encontrar estos elementos:
- Productos.
- Categorías.
- Usuarios administradores.
- Mensajes de contacto.
- Pedidos.
- Detalles de pedido.
- Carrito.
- Imágenes.
Cada uno de estos elementos puede representarse con una clase.
Para empezar, trabajaremos con las clases principales:
ProductoCategoriaUsuarioConexion
Después añadiremos las clases encargadas de acceder a la base de datos:
ProductoDAOCategoriaDAOUsuarioDAO
Fase 3: estructura de carpetas del proyecto
En esta versión necesitaremos una estructura más organizada.
Una posible estructura será esta:
sonido-interior-poo/
│
├── index.php
├── catalogo.php
├── login.php
├── validar-login.php
├── logout.php
│
├── admin/
│ ├── panel.php
│ ├── productos.php
│ ├── producto-alta.php
│ ├── producto-guardar.php
│ ├── producto-editar.php
│ ├── producto-actualizar.php
│ └── producto-borrar.php
│
├── includes/
│ ├── header.php
│ ├── menu.php
│ ├── menu-admin.php
│ ├── footer.php
│ └── seguridad.php
│
├── clases/
│ ├── Conexion.php
│ ├── Producto.php
│ ├── Categoria.php
│ ├── Usuario.php
│ ├── ProductoDAO.php
│ ├── CategoriaDAO.php
│ └── UsuarioDAO.php
│
├── css/
│ └── estilos.css
│
├── img/
│ └── productos/
│
└── sql/
└── tienda_cuencos.sql
La carpeta más importante de esta versión será clases/.
Ahí guardaremos las clases del proyecto.
Fase 4: creación de la clase Conexion
La primera clase que crearemos será Conexion.
Esta clase se encargará de conectarse a la base de datos.
En lugar de tener un archivo conexion.php con variables sueltas, crearemos una clase con un método que devuelva la conexión.
Ejemplo:
<?php
class Conexion {
private $servidor = "localhost";
private $usuario = "root";
private $password = "";
private $baseDatos = "tienda_cuencos";
public function conectar() {
$conexion = new mysqli(
$this->servidor,
$this->usuario,
$this->password,
$this->baseDatos
);
if ($conexion->connect_error) {
die("Error de conexión: " . $conexion->connect_error);
}
$conexion->set_charset("utf8");
return $conexion;
}
}
?>
Con esta clase podremos conectarnos a la base de datos desde cualquier DAO.
Ejemplo:
$conexionBD = new Conexion();
$conexion = $conexionBD->conectar();
Fase 5: creación de la clase Producto
Después crearemos la clase Producto.
Esta clase representará un producto de la tienda.
Tendrá atributos privados como:
- ID.
- Nombre.
- Descripción.
- Precio.
- Stock.
- Imagen.
- Diámetro.
- Peso.
- Material.
- Nota musical.
- Procedencia.
- Activo.
- Categoría.
Ejemplo:
<?php
class Producto {
private $idProducto;
private $nombre;
private $descripcion;
private $precio;
private $stock;
private $imagen;
private $diametro;
private $peso;
private $material;
private $notaMusical;
private $procedencia;
private $activo;
private $idCategoria;
public function __construct(
$idProducto = null,
$nombre = "",
$descripcion = "",
$precio = 0,
$stock = 0,
$imagen = "",
$diametro = 0,
$peso = 0,
$material = "",
$notaMusical = "",
$procedencia = "",
$activo = true,
$idCategoria = null
) {
$this->idProducto = $idProducto;
$this->nombre = $nombre;
$this->descripcion = $descripcion;
$this->precio = $precio;
$this->stock = $stock;
$this->imagen = $imagen;
$this->diametro = $diametro;
$this->peso = $peso;
$this->material = $material;
$this->notaMusical = $notaMusical;
$this->procedencia = $procedencia;
$this->activo = $activo;
$this->idCategoria = $idCategoria;
}
public function getIdProducto() {
return $this->idProducto;
}
public function getNombre() {
return $this->nombre;
}
public function setNombre($nombre) {
$this->nombre = $nombre;
}
public function getDescripcion() {
return $this->descripcion;
}
public function setDescripcion($descripcion) {
$this->descripcion = $descripcion;
}
public function getPrecio() {
return $this->precio;
}
public function setPrecio($precio) {
$this->precio = $precio;
}
public function getStock() {
return $this->stock;
}
public function setStock($stock) {
$this->stock = $stock;
}
}
?>
Durante el proyecto iremos completando la clase con todos los getters y setters necesarios.
Fase 6: creación de la clase Categoria
La clase Categoria representará una categoría de producto.
Ejemplo:
<?php
class Categoria {
private $idCategoria;
private $nombre;
private $descripcion;
private $activo;
public function __construct(
$idCategoria = null,
$nombre = "",
$descripcion = "",
$activo = true
) {
$this->idCategoria = $idCategoria;
$this->nombre = $nombre;
$this->descripcion = $descripcion;
$this->activo = $activo;
}
public function getIdCategoria() {
return $this->idCategoria;
}
public function getNombre() {
return $this->nombre;
}
public function setNombre($nombre) {
$this->nombre = $nombre;
}
public function getDescripcion() {
return $this->descripcion;
}
public function setDescripcion($descripcion) {
$this->descripcion = $descripcion;
}
public function getActivo() {
return $this->activo;
}
public function setActivo($activo) {
$this->activo = $activo;
}
}
?>
Esta clase nos permitirá trabajar con categorías de forma ordenada.
Fase 7: creación de la clase Usuario
La clase Usuario representará a los usuarios administradores.
Tendrá atributos como:
- ID.
- Nombre.
- Email.
- Usuario.
- Password.
- Rol.
Ejemplo:
<?php
class Usuario {
private $idUsuario;
private $nombre;
private $email;
private $usuario;
private $password;
private $rol;
public function __construct(
$idUsuario = null,
$nombre = "",
$email = "",
$usuario = "",
$password = "",
$rol = "admin"
) {
$this->idUsuario = $idUsuario;
$this->nombre = $nombre;
$this->email = $email;
$this->usuario = $usuario;
$this->password = $password;
$this->rol = $rol;
}
public function getIdUsuario() {
return $this->idUsuario;
}
public function getNombre() {
return $this->nombre;
}
public function getUsuario() {
return $this->usuario;
}
public function getPassword() {
return $this->password;
}
public function getRol() {
return $this->rol;
}
}
?>
Esta clase se usará principalmente para el login y la gestión de sesiones.
Fase 8: qué es una clase DAO
En esta versión del proyecto usaremos clases DAO.
DAO significa Data Access Object, es decir, objeto de acceso a datos.
Una clase DAO se encarga de hablar con la base de datos.
Por ejemplo, la clase Producto representa los datos de un producto, pero no debería ser la encargada de hacer consultas SQL.
Para eso tendremos ProductoDAO.
La clase ProductoDAO tendrá métodos como:
obtenerTodos()obtenerPorId($id)crear($producto)actualizar($producto)eliminar($id)disminuirStock($idProducto, $cantidad)
La ventaja es que las consultas SQL quedan agrupadas en una clase concreta.
Fase 9: creación de ProductoDAO
La clase ProductoDAO será una de las más importantes del proyecto.
Ejemplo inicial:
<?php
require_once "Conexion.php";
require_once "Producto.php";
class ProductoDAO {
private $conexion;
public function __construct() {
$conexionBD = new Conexion();
$this->conexion = $conexionBD->conectar();
}
public function obtenerTodos() {
$sql = "SELECT * FROM productos WHERE activo = 1";
$resultado = $this->conexion->query($sql);
$productos = [];
while ($fila = $resultado->fetch_assoc()) {
$producto = new Producto(
$fila["id_producto"],
$fila["nombre"],
$fila["descripcion"],
$fila["precio"],
$fila["stock"],
$fila["imagen"],
$fila["diametro"],
$fila["peso"],
$fila["material"],
$fila["nota_musical"],
$fila["procedencia"],
$fila["activo"],
$fila["id_categoria"]
);
$productos[] = $producto;
}
return $productos;
}
}
?>
Ahora, desde catalogo.php, podremos obtener los productos así:
require_once "clases/ProductoDAO.php";
$productoDAO = new ProductoDAO();
$productos = $productoDAO->obtenerTodos();
Y después recorrerlos:
foreach ($productos as $producto) {
echo "<h3>" . $producto->getNombre() . "</h3>";
echo "<p>" . $producto->getPrecio() . " €</p>";
}
Fase 10: mostrar el catálogo usando objetos
En la página catalogo.php, ya no haremos directamente una consulta SQL.
La página simplemente pedirá los productos a ProductoDAO.
Ejemplo:
<?php
require_once "clases/ProductoDAO.php";
include("includes/header.php");
include("includes/menu.php");
$productoDAO = new ProductoDAO();
$productos = $productoDAO->obtenerTodos();
?>
<main class="contenedor">
<h1>Catálogo de productos</h1>
<section class="grid-productos">
<?php foreach ($productos as $producto): ?>
<article class="tarjeta-producto">
<img src="img/productos/<?php echo $producto->getImagen(); ?>" alt="<?php echo $producto->getNombre(); ?>">
<h3><?php echo $producto->getNombre(); ?></h3>
<p><?php echo $producto->getPrecio(); ?> €</p>
</article>
<?php endforeach; ?>
</section>
</main>
<?php include("includes/footer.php"); ?>
Para que esto funcione, la clase Producto deberá tener métodos como:
getImagen()getNombre()getPrecio()
Fase 11: alta de productos usando objetos
Cuando enviemos el formulario de alta, recogeremos los datos del formulario y crearemos un objeto Producto.
Ejemplo en producto-guardar.php:
require_once "../clases/Producto.php";
require_once "../clases/ProductoDAO.php";
$producto = new Producto();
$producto->setNombre($_POST["nombre"]);
$producto->setDescripcion($_POST["descripcion"]);
$producto->setPrecio($_POST["precio"]);
$producto->setStock($_POST["stock"]);
$producto->setMaterial($_POST["material"]);
$producto->setProcedencia($_POST["procedencia"]);
$productoDAO = new ProductoDAO();
$productoDAO->crear($producto);
Después, dentro de ProductoDAO, tendremos el método crear().
Ejemplo:
public function crear($producto) {
$sql = "INSERT INTO productos
(nombre, descripcion, precio, stock, material, procedencia)
VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $this->conexion->prepare($sql);
$nombre = $producto->getNombre();
$descripcion = $producto->getDescripcion();
$precio = $producto->getPrecio();
$stock = $producto->getStock();
$material = $producto->getMaterial();
$procedencia = $producto->getProcedencia();
$stmt->bind_param(
"ssdiss",
$nombre,
$descripcion,
$precio,
$stock,
$material,
$procedencia
);
return $stmt->execute();
}
Este enfoque es más limpio porque el archivo del formulario no contiene directamente toda la consulta SQL.
Fase 12: listado administrativo usando objetos
El listado administrativo también usará ProductoDAO.
La página admin/productos.php pedirá todos los productos:
$productoDAO = new ProductoDAO();
$productos = $productoDAO->obtenerTodos();
Después los mostrará en una tabla:
<?php foreach ($productos as $producto): ?>
<tr>
<td><?php echo $producto->getIdProducto(); ?></td>
<td><?php echo $producto->getNombre(); ?></td>
<td><?php echo $producto->getPrecio(); ?> €</td>
<td><?php echo $producto->getStock(); ?></td>
<td>
<a href="producto-editar.php?id=<?php echo $producto->getIdProducto(); ?>">Editar</a>
<a href="producto-borrar.php?id=<?php echo $producto->getIdProducto(); ?>">Borrar</a>
</td>
</tr>
<?php endforeach; ?>
La página se centra en mostrar información, no en saber cómo se consulta la base de datos.
Fase 13: obtener un producto por ID
Para editar un producto necesitaremos recuperarlo por su ID.
Añadiremos este método a ProductoDAO:
public function obtenerPorId($id) {
$sql = "SELECT * FROM productos WHERE id_producto = ?";
$stmt = $this->conexion->prepare($sql);
$stmt->bind_param("i", $id);
$stmt->execute();
$resultado = $stmt->get_result();
if ($fila = $resultado->fetch_assoc()) {
return new Producto(
$fila["id_producto"],
$fila["nombre"],
$fila["descripcion"],
$fila["precio"],
$fila["stock"],
$fila["imagen"],
$fila["diametro"],
$fila["peso"],
$fila["material"],
$fila["nota_musical"],
$fila["procedencia"],
$fila["activo"],
$fila["id_categoria"]
);
}
return null;
}
Después, desde producto-editar.php:
$id = $_GET["id"];
$productoDAO = new ProductoDAO();
$producto = $productoDAO->obtenerPorId($id);
Así podremos rellenar el formulario de edición con los datos actuales del producto.
Fase 14: actualización de productos
Cuando se envíe el formulario de edición, crearemos un objeto Producto con los datos actualizados.
Después llamaremos al método actualizar() de ProductoDAO.
Ejemplo:
$producto = new Producto();
$producto->setIdProducto($_POST["id_producto"]);
$producto->setNombre($_POST["nombre"]);
$producto->setDescripcion($_POST["descripcion"]);
$producto->setPrecio($_POST["precio"]);
$producto->setStock($_POST["stock"]);
$productoDAO = new ProductoDAO();
$productoDAO->actualizar($producto);
El método actualizar() hará una consulta UPDATE.
public function actualizar($producto) {
$sql = "UPDATE productos
SET nombre = ?, descripcion = ?, precio = ?, stock = ?
WHERE id_producto = ?";
$stmt = $this->conexion->prepare($sql);
$nombre = $producto->getNombre();
$descripcion = $producto->getDescripcion();
$precio = $producto->getPrecio();
$stock = $producto->getStock();
$id = $producto->getIdProducto();
$stmt->bind_param("ssdii", $nombre, $descripcion, $precio, $stock, $id);
return $stmt->execute();
}
Fase 15: borrado lógico de productos
En lugar de borrar físicamente un producto, lo marcaremos como inactivo.
En ProductoDAO añadiremos un método eliminar():
public function eliminar($id) {
$sql = "UPDATE productos SET activo = 0 WHERE id_producto = ?";
$stmt = $this->conexion->prepare($sql);
$stmt->bind_param("i", $id);
return $stmt->execute();
}
Desde producto-borrar.php:
$id = $_GET["id"];
$productoDAO = new ProductoDAO();
$productoDAO->eliminar($id);
header("Location: productos.php");
exit();
Con esto el producto seguirá en la base de datos, pero dejará de mostrarse en la tienda.
Fase 16: gestión de categorías con CategoriaDAO
Igual que tenemos ProductoDAO, crearemos CategoriaDAO.
Esta clase tendrá métodos como:
obtenerTodas()obtenerPorId($id)crear($categoria)actualizar($categoria)eliminar($id)
Ejemplo básico:
<?php
require_once "Conexion.php";
require_once "Categoria.php";
class CategoriaDAO {
private $conexion;
public function __construct() {
$conexionBD = new Conexion();
$this->conexion = $conexionBD->conectar();
}
public function obtenerTodas() {
$sql = "SELECT * FROM categorias WHERE activo = 1";
$resultado = $this->conexion->query($sql);
$categorias = [];
while ($fila = $resultado->fetch_assoc()) {
$categoria = new Categoria(
$fila["id_categoria"],
$fila["nombre"],
$fila["descripcion"],
$fila["activo"]
);
$categorias[] = $categoria;
}
return $categorias;
}
}
?>
Esto nos permitirá cargar categorías dinámicamente en el formulario de alta de productos.
Fase 17: login usando UsuarioDAO
Para el login crearemos la clase UsuarioDAO.
Esta clase tendrá un método para buscar un usuario por su nombre de usuario o email.
Ejemplo:
public function obtenerPorUsuario($usuario) {
$sql = "SELECT * FROM usuarios WHERE usuario = ? OR email = ?";
$stmt = $this->conexion->prepare($sql);
$stmt->bind_param("ss", $usuario, $usuario);
$stmt->execute();
$resultado = $stmt->get_result();
if ($fila = $resultado->fetch_assoc()) {
return new Usuario(
$fila["id_usuario"],
$fila["nombre"],
$fila["email"],
$fila["usuario"],
$fila["password"],
$fila["rol"]
);
}
return null;
}
Después, en validar-login.php:
session_start();
require_once "clases/UsuarioDAO.php";
$usuarioFormulario = $_POST["usuario"];
$passwordFormulario = $_POST["password"];
$usuarioDAO = new UsuarioDAO();
$usuario = $usuarioDAO->obtenerPorUsuario($usuarioFormulario);
if ($usuario != null && password_verify($passwordFormulario, $usuario->getPassword())) {
$_SESSION["usuario"] = $usuario->getUsuario();
$_SESSION["rol"] = $usuario->getRol();
header("Location: admin/panel.php");
exit();
} else {
header("Location: login.php?error=1");
exit();
}
Así el login queda mucho más ordenado.
Fase 18: protección de páginas privadas
Seguiremos usando sesiones para proteger la parte administrativa.
El archivo includes/seguridad.php puede mantenerse parecido a la versión anterior.
<?php
session_start();
if (!isset($_SESSION["usuario"])) {
header("Location: ../login.php");
exit();
}
?>
En cada página privada incluiremos este archivo.
<?php include("../includes/seguridad.php"); ?>
Fase 19: subida de imágenes usando una función auxiliar
La subida de imágenes puede hacerse al principio dentro de producto-guardar.php, pero podemos mejorarla creando una función o una clase auxiliar.
Una opción sencilla sería crear:
clases/GestorImagenes.php
Ejemplo:
<?php
class GestorImagenes {
public static function subirImagen($archivo, $carpetaDestino) {
$nombreImagen = time() . "_" . basename($archivo["name"]);
$rutaDestino = $carpetaDestino . $nombreImagen;
if (move_uploaded_file($archivo["tmp_name"], $rutaDestino)) {
return $nombreImagen;
}
return null;
}
}
?>
Después, en producto-guardar.php:
require_once "../clases/GestorImagenes.php";
$imagen = GestorImagenes::subirImagen(
$_FILES["imagen"],
"../img/productos/"
);
$producto->setImagen($imagen);
Esto permite separar la lógica de subida de archivos del resto del código.
Fase 20: autoload básico de clases
Cuando el proyecto crece, puede resultar pesado escribir muchos require_once.
Por ejemplo:
require_once "clases/Conexion.php";
require_once "clases/Producto.php";
require_once "clases/ProductoDAO.php";
require_once "clases/Categoria.php";
require_once "clases/CategoriaDAO.php";
Para evitarlo, podemos crear un pequeño autoload.
Ejemplo en includes/autoload.php:
<?php
spl_autoload_register(function ($nombreClase) {
require_once __DIR__ . "/../clases/" . $nombreClase . ".php";
});
?>
Después, en cada página solo haríamos:
require_once "includes/autoload.php";
O desde la carpeta admin:
require_once "../includes/autoload.php";
Esto nos permitirá cargar clases automáticamente cuando las necesitemos.
Fase 21: diagrama de clases
En esta versión será especialmente importante trabajar con un diagrama de clases.
El diagrama nos ayudará a entender qué clases existen y cómo se relacionan.
Algunas clases principales serán:
ConexionProductoCategoriaUsuarioProductoDAOCategoriaDAOUsuarioDAOGestorImagenes
La relación sería sencilla:
ProductoDAOusaConexion.ProductoDAOcrea y devuelve objetosProducto.CategoriaDAOusaConexion.CategoriaDAOcrea y devuelve objetosCategoria.UsuarioDAOusaConexion.UsuarioDAOcrea y devuelve objetosUsuario.- Las páginas PHP usan los DAO para obtener o guardar datos.
Este diagrama nos permitirá ver que las páginas ya no hablan directamente con la base de datos, sino que lo hacen a través de clases especializadas.
Fase 22: diagrama de base de datos
El diagrama de base de datos seguirá siendo parecido al de la versión anterior.
Tendremos tablas como:
usuarioscategoriasproductosmensajespedidosdetalle_pedidocarritocarrito_producto
La tabla más importante al principio será productos, relacionada con categorias.
La diferencia es que ahora intentaremos que las tablas tengan una correspondencia clara con las clases del proyecto.
Por ejemplo:
- Tabla
productos→ ClaseProducto - Tabla
categorias→ ClaseCategoria - Tabla
usuarios→ ClaseUsuario
Esto ayuda a entender la relación entre el modelo de datos y el modelo de objetos.
Fase 23: preparación para hosting
El despliegue en hosting será similar al de la versión anterior.
Tendremos que subir:
- Archivos PHP.
- Carpeta
clases/. - Carpeta
includes/. - Carpeta
css/. - Carpeta
img/. - Base de datos exportada.
También deberemos modificar los datos de conexión dentro de la clase Conexion.
En local podríamos tener:
private $servidor = "localhost";
private $usuario = "root";
private $password = "";
private $baseDatos = "tienda_cuencos";
En hosting tendremos otros datos:
private $servidor = "servidor_del_hosting";
private $usuario = "usuario_hosting";
private $password = "password_hosting";
private $baseDatos = "base_datos_hosting";
Esta parte nos ayudará a entender que una aplicación debe adaptarse al entorno donde se ejecuta.
Fase 24: ejecución en Docker
También podremos preparar el proyecto para ejecutarlo en Docker.
La diferencia principal será que la clase Conexion deberá conectarse al servicio de base de datos usando el nombre del contenedor.
Por ejemplo, si en docker-compose.yml el servicio de base de datos se llama db, entonces en PHP usaremos:
private $servidor = "db";
Ejemplo de docker-compose.yml:
services:
web:
image: php:8.2-apache
ports:
- "8080:80"
volumes:
- ./src:/var/www/html
db:
image: mariadb:10.11
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: tienda_cuencos
MYSQL_USER: usuario
MYSQL_PASSWORD: usuario
phpmyadmin:
image: phpmyadmin
ports:
- "8081:80"
environment:
PMA_HOST: db
Con esta configuración podríamos acceder a la web desde:
http://localhost:8080
Y a phpMyAdmin desde:
http://localhost:8081
Fase 25: documentación final del proyecto
Al terminar esta versión, cada alumno deberá documentar el proyecto.
La documentación debería incluir:
- Explicación general del proyecto.
- Diferencias entre la versión básica y la versión POO.
- Estructura de carpetas.
- Diagrama de clases.
- Diagrama de base de datos.
- Explicación de las clases principales.
- Explicación de las clases DAO.
- Explicación del CRUD.
- Capturas de pantalla.
- Problemas encontrados.
- Mejoras posibles.
- Conclusión personal.
En esta versión será especialmente importante que el alumno sepa explicar por qué se han creado ciertas clases y qué responsabilidad tiene cada una.
26. Qué aprenderemos con esta versión
Con esta versión aprenderemos a organizar mejor un proyecto PHP.
No solo veremos cómo hacer que una aplicación funcione, sino cómo estructurarla para que sea más clara y mantenible.
Aprenderemos conceptos como:
- Clases.
- Objetos.
- Atributos.
- Métodos.
- Constructores.
- Getters y setters.
- Encapsulación.
- DAO.
- Separación de responsabilidades.
- Consultas preparadas.
- Sesiones.
- Subida de archivos.
- Reutilización de código.
- Relación entre tablas y clases.
- Preparación del proyecto para hosting o Docker.
Esta versión es un paso intermedio muy importante antes de trabajar con arquitecturas más avanzadas como MVC o frameworks como Laravel.
27. Conclusión
La versión orientada a objetos de Sonido Interior nos permitirá mejorar el mismo proyecto que ya conocemos.
En lugar de empezar una aplicación totalmente nueva, evolucionaremos una tienda sencilla construida con PHP básico hacia una estructura más ordenada y profesional.
Esto nos permitirá entender mejor por qué existe la programación orientada a objetos y qué problemas resuelve.
La idea principal es que el alumno vea una evolución clara:
Primero hacemos que funcione.
Después hacemos que esté mejor organizado.
Y finalmente preparamos el proyecto para poder crecer, mantenerse y desplegarse en un entorno real.











