📌 Nota: Esta guía está diseñada para ayudarte a entender el código del proyecto MVC que has desarrollado. Léela con calma y practica cada concepto.
PHP (Hypertext Preprocessor) es un lenguaje de programación del lado del servidor diseñado específicamente para el desarrollo web. Se ejecuta en el servidor y genera HTML que se envía al navegador del cliente.
En PHP, las variables comienzan con el símbolo $
:
<?php
$nombre = "Juan"; // String (cadena de texto)
$edad = 25; // Integer (número entero)
$precio = 19.99; // Float (número decimal)
$activo = true; // Boolean (verdadero/falso)
$colores = ["rojo", "azul"]; // Array (arreglo)
?>
<?php
$nombre = "Juan";
$apellido = "Pérez";
// Usando el operador punto (.)
$nombreCompleto = $nombre . " " . $apellido;
// Usando comillas dobles (interpola variables)
$saludo = "Hola, $nombre $apellido";
// Usando comillas simples (NO interpola)
$texto = 'Hola, $nombre'; // Imprime: Hola, $nombre
?>
<?php
// IF - ELSE
if ($edad >= 18) {
echo "Eres mayor de edad";
} else {
echo "Eres menor de edad";
}
// SWITCH
switch ($dia) {
case "lunes":
echo "Inicio de semana";
break;
case "viernes":
echo "Fin de semana cerca";
break;
default:
echo "Día normal";
}
// BUCLE FOR
for ($i = 0; $i < 5; $i++) {
echo "Número: $i";
}
// BUCLE FOREACH (para arrays)
$frutas = ["manzana", "pera", "uva"];
foreach ($frutas as $fruta) {
echo $fruta;
}
?>
<?php
// Definir una función
function saludar($nombre) {
return "Hola, " . $nombre;
}
// Llamar a la función
$mensaje = saludar("Juan");
echo $mensaje; // Imprime: Hola, Juan
// Función con valor por defecto
function calcularPrecio($precio, $descuento = 0) {
return $precio - ($precio * $descuento / 100);
}
echo calcularPrecio(100); // 100
echo calcularPrecio(100, 10); // 90
?>
La Programación Orientada a Objetos es un paradigma que organiza el código en "objetos" que contienen datos (propiedades) y comportamientos (métodos). Es como crear plantillas (clases) para crear cosas (objetos).
<?php
// Definir una clase (plantilla)
class Coche {
// Propiedades (características)
public $marca;
public $color;
private $velocidad = 0;
// Constructor (se ejecuta al crear el objeto)
public function __construct($marca, $color) {
$this->marca = $marca;
$this->color = $color;
}
// Métodos (acciones)
public function acelerar($cantidad) {
$this->velocidad += $cantidad;
return "Acelerando a " . $this->velocidad . " km/h";
}
public function getVelocidad() {
return $this->velocidad;
}
}
// Crear objetos (instancias de la clase)
$miCoche = new Coche("Toyota", "rojo");
echo $miCoche->marca; // Toyota
echo $miCoche->acelerar(50); // Acelerando a 50 km/h
?>
Modificador Descripción Acceso desde
public
Accesible desde cualquier lugar Clase, subclases, fuera de la clase
private
Solo accesible dentro de la clase Solo dentro de la clase
protected
Accesible en la clase y subclases Clase y subclases
<?php
class Ejemplo {
private $nombre = "Juan";
private static $contador = 0;
public function mostrarNombre() {
// $this se refiere al objeto actual
return $this->nombre;
}
public static function incrementar() {
// self se refiere a la clase (para propiedades estáticas)
self::$contador++;
}
}
?>
MVC es un patrón de arquitectura de software que separa la aplicación en tres componentes principales:
- Modelo (Model): Maneja los datos y la lógica de negocio
- Vista (View): Presenta la información al usuario (HTML)
- Controlador (Controller): Gestiona las peticiones y coordina Modelo y Vista
💡 Analogía: Imagina un restaurante:
- Modelo: La cocina (prepara la comida/datos)
- Vista: El plato servido (presentación)
- Controlador: El camarero (toma pedidos y coordina) :::
1. Usuario hace clic en un enlace: index.php?c=post&action=create
2. index.php recibe la petición
3. index.php carga el controlador correspondiente (postController)
4. El controlador llama al método correspondiente (create)
5. El controlador puede usar el Modelo para obtener/guardar datos
6. El controlador carga la Vista con los datos
7. La Vista genera el HTML
8. El HTML se envía al navegador del usuario
mvc/
├── controllers/ # Controladores (lógica de la aplicación)
│ ├── mainController.php
│ ├── userController.php
│ ├── postController.php
│ └── commentController.php
├── models/ # Modelos (datos y lógica de negocio)
│ ├── User.php
│ ├── UserRepositori.php
│ ├── Post.php
│ ├── PostRepository.php
│ ├── Comment.php
│ └── CommentRepository.php
├── views/ # Vistas (presentación HTML)
│ ├── blogView.phtml
│ ├── loginView.phtml
│ ├── registerView.phtml
│ └── createPostView.phtml
├── scripts/ # Scripts SQL
│ ├── create_database.sql
│ └── add_roles.sql
├── db.php # Conexión a la base de datos
└── index.php # Punto de entrada de la aplicación
<?php
session_start(); // Inicia la sesión para manejar usuarios logueados
// Obtiene el controlador de la URL (por defecto 'main')
$controller = $_GET['c'] ?? 'main';
// Carga el archivo del controlador
require_once "controllers/{$controller}Controller.php";
// Crea el nombre de la clase del controlador
$controllerClass = $controller . 'Controller';
// Crea una instancia del controlador
$controllerObj = new $controllerClass();
// Obtiene la acción de la URL (por defecto 'index')
$action = $_GET['action'] ?? 'index';
// Llama al método del controlador
$controllerObj->$action();
?>
📌 Ejemplo: Si visitas index.php?c=post&action=create
:
- $controller = "post"
- Se carga "controllers/postController.php"
- $controllerClass = "postController"
- Se crea un objeto de postController
- $action = "create"
- Se llama al método create() del controlador
<?php
class Database {
private static $instance = null;
private $connection;
// Constructor privado (patrón Singleton)
private function __construct() {
$host = 'localhost';
$dbname = 'blog';
$username = 'root';
$password = '';
// PDO: PHP Data Objects (forma segura de conectar a BD)
$this->connection = new PDO(
"mysql:host=$host;dbname=$dbname;charset=utf8",
$username,
$password
);
// Configurar PDO para lanzar excepciones en errores
$this->connection->setAttribute(
PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION
);
}
// Obtener la única instancia (Singleton)
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getConnection() {
return $this->connection;
}
}
?>
💡 Patrón Singleton: Garantiza que solo exista UNA conexión a la base de datos en toda la aplicación. Es como tener una sola llave para
Las clases de entidad representan objetos del mundo real (User, Post, Comment):
<?php
class User {
private $id;
private $username;
private $password;
private $role;
// Constructor
public function __construct($id, $username, $password, $role = 'user') {
$this->id = $id;
$this->username = $username;
$this->password = $password;
$this->role = $role;
}
// Getters (obtener valores)
public function getId() {
return $this->id;
}
public function getUsername() {
return $this->username;
}
public function getRole() {
return $this->role;
}
// Método para verificar si es administrador
public function isAdmin() {
return $this->role === 'admin';
}
}
?>
Los repositorios manejan todas las operaciones de base de datos:
<?php
class UserRepositori {
private $db;
public function __construct() {
// Obtener la conexión a la base de datos
$this->db = Database::getInstance()->getConnection();
}
// Buscar usuario por nombre
public function findByUsername($username) {
// Preparar consulta SQL (previene inyección SQL)
$stmt = $this->db->prepare(
"SELECT * FROM user WHERE username = :username"
);
// Ejecutar con parámetros
$stmt->execute(['username' => $username]);
// Obtener resultado
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row) {
// Crear objeto User con los datos
return new User(
$row['id'],
$row['username'],
$row['password'],
$row['role']
);
}
return null;
}
// Crear nuevo usuario
public function create($username, $password, $role = 'user') {
$stmt = $this->db->prepare(
"INSERT INTO user (username, password, role)
VALUES (:username, :password, :role)"
);
return $stmt->execute([
'username' => $username,
'password' => password_hash($password, PASSWORD_DEFAULT),
'role' => $role
]);
}
}
?>
prepare()
y execute()
con parámetros
nombrados para prevenir inyección SQL. NUNCA concatenes variables
directamente en SQL.
<?php
class userController {
private $userRepo;
public function __construct() {
$this->userRepo = new UserRepositori();
}
// Mostrar formulario de login
public function login() {
require_once 'views/loginView.phtml';
}
// Procesar login
public function authenticate() {
// Obtener datos del formulario
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
// Buscar usuario en la base de datos
$user = $this->userRepo->findByUsername($username);
// Verificar si existe y la contraseña es correcta
if ($user && password_verify($password, $user->getPassword())) {
// Guardar datos en sesión
$_SESSION['user_id'] = $user->getId();
$_SESSION['username'] = $user->getUsername();
$_SESSION['role'] = $user->getRole();
// Redirigir al blog
header('Location: index.php?c=main');
exit;
} else {
// Login fallido
$error = "Usuario o contraseña incorrectos";
require_once 'views/loginView.phtml';
}
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>Iniciar Sesión</h1>
<?php if (isset($error)): ?>
<p style="color: red;"><?= $error ?></p>
<?php endif; ?>
<form method="POST" action="index.php?c=user&action=authenticate">
<label>Usuario:</label>
<input type="text" name="username" required>
<label>Contraseña:</label>
<input type="password" name="password" required>
<button type="submit">Entrar</button>
</form>
</body>
</html>
Las sesiones permiten mantener información del usuario entre diferentes páginas:
<?php
// Iniciar sesión (siempre al principio)
session_start();
// Guardar datos en sesión
$_SESSION['user_id'] = 1;
$_SESSION['username'] = 'juan';
// Leer datos de sesión
$userId = $_SESSION['user_id'];
// Verificar si existe un dato
if (isset($_SESSION['user_id'])) {
echo "Usuario logueado";
}
// Destruir sesión (logout)
session_destroy();
?>
Variable Descripción Ejemplo
$_GET
Datos de la URL index.php?id=5 → $_GET['id']
$_POST
Datos de formularios <input name="email"> → $_POST['email']
$_SESSION
Datos de sesión $_SESSION['user_id']
$_SERVER
Info del servidor $_SERVER['REQUEST_METHOD']
$_FILES
Archivos subidos $_FILES['imagen']
PDO es la forma moderna y segura de trabajar con bases de datos en PHP:
<?php
// Conectar
$pdo = new PDO("mysql:host=localhost;dbname=blog", "root", "");
// Consulta preparada (SEGURA)
$stmt = $pdo->prepare("SELECT * FROM user WHERE id = :id");
$stmt->execute(['id' => $userId]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
// Insertar datos
$stmt = $pdo->prepare("INSERT INTO post (title, content) VALUES (:title, :content)");
$stmt->execute([
'title' => $title,
'content' => $content
]);
// Obtener ID del último registro insertado
$lastId = $pdo->lastInsertId();
?>
El sistema de roles controla qué puede hacer cada usuario:
<?php
// Verificar si el usuario está logueado
function isLoggedIn() {
return isset($_SESSION['user_id']);
}
// Verificar si es administrador
function isAdmin() {
return isset($_SESSION['role']) && $_SESSION['role'] === 'admin';
}
// Verificar si puede eliminar un post
function canDeletePost($postAuthorId) {
// Admin puede eliminar cualquier post
if (isAdmin()) {
return true;
}
// Usuario puede eliminar solo sus propios posts
return isset($_SESSION['user_id']) &&
$_SESSION['user_id'] == $postAuthorId;
}
// Uso en el controlador
if (!canDeletePost($post->getUserId())) {
die("No tienes permiso para eliminar este post");
}
?>
❌ MAL (vulnerable):
$sql = "SELECT * FROM user WHERE username = '$username'";
$result = $pdo->query($sql);
✅ BIEN (seguro):
$stmt = $pdo->prepare("SELECT * FROM user WHERE username = :username");
$stmt->execute(['username' => $username]);
<?php
// Al registrar: hashear la contraseña
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// Al hacer login: verificar la contraseña
if (password_verify($passwordIngresada, $hashedPasswordBD)) {
echo "Contraseña correcta";
}
?>
<?php
// Validar que no esté vacío
if (empty($_POST['username'])) {
die("El username es requerido");
}
// Validar longitud
if (strlen($_POST['username']) < 3) {
die("El username debe tener al menos 3 caracteres");
}
// Sanitizar HTML (prevenir XSS)
$username = htmlspecialchars($_POST['username']);
// Validar email
if (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
die("Email inválido");
}
?>
- Empieza por index.php: Es el punto de entrada, entiende cómo se enrutan las peticiones
- Sigue el flujo: URL → Controlador → Modelo → Vista
- Lee los nombres: Los nombres de clases, métodos y variables deben ser descriptivos
- Busca patrones: Los repositorios siempre tienen métodos similares (find, create, update, delete)
- Entiende las relaciones: ¿Qué clases usan otras clases?
- ¿Qué hace este método? (lee el nombre y los comentarios)
- ¿Qué datos recibe? (parámetros)
- ¿Qué datos devuelve? (return)
- ¿Qué otras clases/métodos usa?
- ¿Hay validaciones o comprobaciones?
- ¿Hay manejo de errores?
<?php
public function create() {
// 1. ¿Qué hace? Crea un nuevo post
// 2. Verifica si el usuario está logueado
if (!isset($_SESSION['user_id'])) {
header('Location: index.php?c=user&action=login');
exit;
}
// 3. Si es GET, muestra el formulario
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
require_once 'views/createPostView.phtml';
return;
}
// 4. Si es POST, procesa el formulario
$title = $_POST['title'] ?? '';
$content = $_POST['content'] ?? '';
$userId = $_SESSION['user_id'];
// 5. Valida los datos
if (empty($title) || empty($content)) {
$error = "Todos los campos son requeridos";
require_once 'views/createPostView.phtml';
return;
}
// 6. Guarda en la base de datos
$this->postRepo->create($title, $content, $userId);
// 7. Redirige al blog
header('Location: index.php?c=main');
exit;
}
?>
- Modifica la tabla user en SQL para agregar el campo email
- Actualiza la clase User para incluir la propiedad email
- Modifica UserRepositori para guardar y recuperar el email
- Actualiza el formulario de registro para pedir el email
- Crea un método edit() en postController
- Crea un método update() en PostRepository
- Crea una vista editPostView.phtml
- Agrega un botón "Editar" en blogView.phtml
- Verifica que solo el autor o admin puedan editar
- Modifica getAllPosts() para aceptar límite y offset
- Usa LIMIT y OFFSET en la consulta SQL
- Agrega botones "Anterior" y "Siguiente" en la vista
- PHP Manual: https://www.php.net/manual/es/
- PDO: https://www.php.net/manual/es/book.pdo.php
- Sesiones: https://www.php.net/manual/es/book.session.php
- Composer (gestor de dependencias)
- Namespaces en PHP
- Autoloading de clases
- Manejo de excepciones (try-catch)
- APIs REST con PHP
- Frameworks PHP (Laravel, Symfony)
Término Definición
MVC Patrón de diseño que separa datos, lógica y presentación PDO Interfaz para acceder a bases de datos de forma segura OOP Programación Orientada a Objetos CRUD Create, Read, Update, Delete (operaciones básicas de BD) Sesión Mecanismo para mantener datos del usuario entre páginas Hash Transformación irreversible de datos (ej: contraseñas) Inyección SQL Ataque que inserta código SQL malicioso XSS Cross-Site Scripting, ataque que inyecta JavaScript Repository Patrón que encapsula el acceso a datos Singleton Patrón que garantiza una única instancia de una clase