Overview
The ReservationController handles all reservation-related operations in the Apartado de Salas system. It manages the complete lifecycle of room reservations including creation with time slots, material assignments, admin approval/rejection workflows, and listing for both users and administrators.
Location: app/controllers/ReservationController.php
Methods
create()
Displays the reservation creation form. Requires user authentication.
Renders the reservation creation view
Route Mapping:
GET /reservations/create -> ReservationController::create()
Method Signature:
public function create(): void
Implementation:
Auth::requireLogin();
require_once dirname(__DIR__) . '/views/reservations/create.php';
store()
Processes the creation of a new reservation with multiple time slots and materials. Uses database transactions to ensure data integrity.
ID of the room to reserve ($_POST['room_id'])
Name of the event ($_POST['event_name'])
Optional notes for the reservation ($_POST['notes'])
Array of dates for reservation slots ($_POST['dates'])
Array of start times for each date ($_POST['start_times'])
Array of end times for each date ($_POST['end_times'])
Array of material IDs to assign ($_POST['materials'])
Redirects to dashboard on success or back to form with error
Route Mapping:
POST /reservations/store -> ReservationController::store()
Method Signature:
public function store(): void
Implementation Flow:
Data Collection
Validation
Transaction
Auth::requireLogin();
// Get form data
$userId = $_SESSION['user']['id'] ?? null;
$roomId = $_POST['room_id'] ?? null;
$event = trim($_POST['event_name'] ?? '');
$notes = trim($_POST['notes'] ?? '');
$dates = $_POST['dates'] ?? []; // array
$startTimes = $_POST['start_times'] ?? []; // array
$endTimes = $_POST['end_times'] ?? []; // array
$materials = $_POST['materials'] ?? []; // array
$materials = array_map('intval', $materials);
// Validate required fields
if (!$userId || !$roomId || empty($event)) {
Session::setFlash('error', 'Datos incompletos.');
header('Location: '. BASE_URL . '/reservations/create');
exit;
}
if (count($dates) === 0) {
Session::setFlash('error', 'Debe agregar al menos un horario.');
header('Location: '. BASE_URL . '/reservations/create');
exit;
}
// Initialize models
$reservationModel = new Reservation();
$slotModel = new ReservationSlot();
$materialModel = new Material();
try {
$db = Database::getConnection();
$db->beginTransaction();
// Create reservation record
$reservationId = $reservationModel->create(
$userId,
(int)$roomId,
$event,
$notes ?: null
);
// Create time slots
foreach ($dates as $index => $date) {
$start = $startTimes[$index] ?? null;
$end = $endTimes[$index] ?? null;
if (!$date || !$start || !$end) {
throw new Exception('Bloque horario incompleto.');
}
if ($slotModel->hasConflict((int)$roomId, $date, $start, $end)) {
throw new Exception("Conflicto de horario el $date de $start a $end.");
}
$slotModel->create($reservationId, $date, $start, $end);
}
// Validate and assign materials
if (!$materialModel->validateForRoom($materials, (int)$roomId)) {
throw new Exception('Seleccionaste materiales no válidos para la sala.');
}
$reservationModel->attachMaterials($reservationId, $materials);
// Commit transaction
$db->commit();
Session::setFlash('success', 'La reservación fue creada correctamente.');
header('Location:'. BASE_URL.'/dashboard');
exit;
} catch (Exception $e) {
if(isset($db) && $db->inTransaction()) {
$db->rollBack();
}
Session::setFlash('error', $e->getMessage());
header('Location: ' . BASE_URL . '/reservations/create');
exit;
}
This method uses database transactions to ensure all operations (reservation creation, slot creation, material assignment) succeed or fail together.
Possible Error Messages:
"Datos incompletos." - Missing required fields
"Debe agregar al menos un horario." - No time slots provided
"Bloque horario incompleto." - Missing date, start time, or end time for a slot
"Conflicto de horario el {date} de {start} a {end}." - Time slot conflicts with existing reservation
"Seleccionaste materiales no válidos para la sala." - Invalid materials for the selected room
index()
Lists all reservations for administrators with optional status filtering.
Optional status filter ($_GET['status']). Valid values: pendiente, aprobado, rechazado
Renders the reservations index view
Route Mapping:
GET /reservations -> ReservationController::index()
Method Signature:
public function index(): void
Implementation:
// Only admins
Auth::requireRole('admin');
$reservationModel = new Reservation();
// Optional status filter
$status = $_GET['status'] ?? null;
if ($status) {
$reservations = $reservationModel->getByStatus($status);
} else {
$reservations = $reservationModel->getAll();
}
require_once dirname(__DIR__) . '/views/reservations/index.php';
Only users with admin role can access this method.
getByStatus()
Deprecated/Unused Method: This method exists in the controller source code (lines 143-171) but is never called by any route. The index() method instead uses the Reservation model’s getByStatus() method. See Reservation Model for the actively used implementation.
This method retrieves reservations filtered by status with joined room and user information.
Status to filter by: pendiente, aprobado, or rechazado
Array of reservation records with room and user details
Method Signature:
public function getByStatus(string $status): array
Note: In practice, use the Reservation model method via:
$reservationModel = new Reservation();
$reservations = $reservationModel->getByStatus($status);
approve()
Approves a pending reservation. Admin-only operation.
Reservation ID to approve ($_POST['id'])
Redirects back to reservations list with success message
Route Mapping:
POST /reservations/approve -> ReservationController::approve()
Method Signature:
public function approve(): void
Implementation:
Auth::requireRole('admin');
// Validate POST request
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
header('Location: ' . BASE_URL . '/reservations');
exit;
}
// Get ID
$id = $_POST['id'] ?? null;
if (!$id) {
Session::setFlash('error', 'Solicitud inválida.');
header('Location: ' . BASE_URL . '/reservations');
exit;
}
// Update status
$reservationModel = new Reservation();
$reservationModel->updateStatus((int)$id, 'aprobado');
// Feedback
Session::setFlash('success', 'Solicitud aprobada correctamente.');
// Redirect
header('Location: ' . BASE_URL . '/reservations');
exit;
reject()
Rejects a pending reservation. Admin-only operation.
Reservation ID to reject ($_POST['id'])
Redirects back to reservations list with success message
Route Mapping:
POST /reservations/reject -> ReservationController::reject()
Method Signature:
public function reject(): void
Implementation:
Auth::requireRole('admin');
// Validate POST request
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
header('Location: ' . BASE_URL . '/reservations');
exit;
}
// Get ID
$id = $_POST['id'] ?? null;
if (!$id) {
Session::setFlash('error', 'Solicitud inválida.');
header('Location: ' . BASE_URL . '/reservations');
exit;
}
// Update status
$reservationModel = new Reservation();
$reservationModel->updateStatus((int)$id, 'rechazado');
// Feedback
Session::setFlash('success', 'Solicitud rechazada.');
// Redirect
header('Location: ' . BASE_URL . '/reservations');
exit;
mine()
Displays all reservations created by the currently authenticated user.
Renders the user’s reservations view
Route Mapping:
GET /reservations/mine -> ReservationController::mine()
Method Signature:
public function mine(): void
Implementation:
Auth::requireLogin();
$userId = $_SESSION['user']['id'] ?? null;
if (!$userId) {
Session::setFlash('error', 'Sesión inválida');
header('Location: ' . BASE_URL . '/login');
exit;
}
$reservationModel = new Reservation();
$reservations = $reservationModel->getByUser($userId);
require_once dirname(__DIR__) . '/views/reservations/mine.php';
show()
Displays detailed information about a specific reservation including time slots and materials. Admin-only.
Reservation ID to display ($_GET['id'])
Renders the reservation detail view
Route Mapping:
GET /reservations/show -> ReservationController::show()
Method Signature:
public function show(): void
Implementation:
Auth::requireRole('admin');
$id = $_GET['id'] ?? null;
if (!$id) {
Session::setFlash('error', 'Solicitud no válida.');
header('Location: ' . BASE_URL . '/reservations');
exit;
}
$reservationModel = new Reservation();
$reservation = $reservationModel->findById((int)$id);
$slots = $reservationModel->getSlots((int)$id);
$materials = $reservationModel->getMaterials((int)$id);
if (!$reservation) {
Session::setFlash('error', 'Solicitud no encontrada.');
header('Location: ' . BASE_URL . '/reservations');
exit;
}
require_once dirname(__DIR__) . '/views/reservations/show.php';
Dependencies
require_once dirname(__DIR__) . '/models/Reservation.php';
require_once dirname(__DIR__) . '/models/ReservationSlot.php';
require_once dirname(__DIR__) . '/models/Material.php';
require_once dirname(__DIR__) . '/Helpers/Session.php';
require_once dirname(__DIR__) . '/Helpers/Auth.php';
require_once dirname(__DIR__) . '/core/Database.php';
Required Classes:
Reservation - Model for reservation management
ReservationSlot - Model for time slot management
Material - Model for material management
Session - Helper for session management
Auth - Helper for authentication and authorization
Database - Database connection handler
Reservation Status Flow
Status Values:
pendiente - Newly created, awaiting admin review
aprobado - Approved by administrator
rechazado - Rejected by administrator