Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/AngelZurita28/VeranoRegional/llms.txt

Use this file to discover all available pages before exploring further.

Verano Regional is built on a custom PHP MVC architecture that avoids third-party frameworks. A single index.php file acts as the application router, dispatching every request to the correct controller and method using a declarative route map. Controllers extend a shared BaseController that handles authentication, role enforcement, database access, and response helpers — keeping individual controllers focused on business logic.

Directory structure

VeranoRegional/
├── assets/         # Static files: CSS, JS, images
├── config/         # Database connection and constants
├── controllers/    # Request handlers (one class per feature area)
├── helpers/        # Utility classes (EnvLoader, EmailHelper)
├── lib/            # Third-party libraries
├── models/         # Database interaction layer
├── services/       # Additional logic and external integrations
├── views/          # PHP templates rendered by controllers
├── uploads/        # User-uploaded files (reports, documents)
├── index.php       # Main dynamic router
├── api.php         # Async JSON API endpoints
└── .env            # Local environment config (excluded from git)

Routing system

All web requests flow through index.php. Routes are declared as two associative arrays — $publicRoutes for unauthenticated access and $protectedRoutes for authenticated users. Each entry maps an action string (passed via ?action=) to a [ControllerClass, methodName] pair.
// Public routes — no session required
$publicRoutes = [
    'login'                => ['LoginController', 'login'],
    'pre_register'         => ['RegisterController', 'pre_register'],
    'send_verification_otp'=> ['RegisterController', 'send_verification_otp'],
    'forgot_password'      => ['PasswordController', 'showForgotPasswordForm'],
    // ...
];

// Protected routes — active session required
$protectedRoutes = [
    'dashboard'            => ['DashboardController', 'showDashboard'],
    'view_projects'        => ['ProjectController', 'showProjectList'],
    'create_project'       => ['ProjectController', 'createProject'],
    'my_applications'      => ['ApplicationController', 'showMyApplications'],
    // ...
];
The router resolves requests in this order:
1

Check for logout

The logout action is handled as a special case before any route lookup.
2

Match public routes

If the action exists in $publicRoutes, the router instantiates the controller and calls the method — no session check.
3

Verify authentication

If the action is not public and $_SESSION['user_email'] is not set, the router renders the login form.
4

Default to dashboard

An authenticated request with no action parameter is treated as action=dashboard.
5

Match protected routes

If the action exists in $protectedRoutes, the router calls the method. Role checks happen inside the controller via requireRole().
6

Fallback

An unrecognised action renders the login form.

Auto-loading controllers

index.php loads every controller file at startup using a single glob loop — no manual require_once per controller is needed:
$controllerDir = __DIR__ . '/controllers/';
foreach (glob($controllerDir . '*.php') as $controllerFile) {
    require_once $controllerFile;
}
Any new controller file dropped into the controllers/ directory is picked up automatically on the next request.

BaseController pattern

All controllers extend BaseController, which wires up the database connection and provides the shared methods below.
abstract class BaseController
{
    protected $conn;

    public function __construct(bool $requireAuth = true)
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
        if ($requireAuth && !isset($_SESSION['user_email'])) {
            header('Location: index.php');
            exit();
        }
        $database = new Database();
        $this->conn = $database->connect();
    }
}
Terminates the request with HTTP 403 if the current user’s $_SESSION['idUserType'] is not in the allowed list.
$this->requireRole([1, 2]); // Allow admin (1) and institutional coordinator (2)
Redirects to the given action if the request method is not POST.
$this->requirePost('show_add_project_form');
Extracts $data into local variables and loads the view inside the shared menu layout (views/shared/menu.php).
$this->render('project/list.php', ['projects' => $projects]);
Builds an index.php?action= URL from the action name and optional query parameters, then sends a Location header.
$this->redirect('manage_students', ['campus' => 3]);
Stores a flash message in $_SESSION['message'] before redirecting, so the destination view can display a success or error notice.
$this->redirectWithMessage('success', 'Project created.', 'my_projects');
Sets the Content-Type: application/json header, encodes $data, outputs it, and exits. Used for AJAX responses.
$this->jsonResponse(['status' => 'success'], 200);

Session management

After a successful login, the following keys are written to $_SESSION:
KeyTypeDescription
userIdintPrimary key of the authenticated user
user_emailstringUser’s email address — presence indicates an active session
user_type_descriptionstringHuman-readable role name (e.g., “Alumno”)
idUserTypeintNumeric role ID used by requireRole()
userCampusIdintCampus the user belongs to
isValidint1 if the account has been validated by a coordinator
user_email is the canonical session presence check. The router and BaseController both test for isset($_SESSION['user_email']) to decide whether a user is authenticated.

Adding a new route

1

Create or identify the controller method

Add a public method to the relevant controller in controllers/. If the route requires authentication, extend BaseController and call $this->requireRole() at the start of the method.
2

Register the route

Add an entry to $publicRoutes or $protectedRoutes in index.php:
$protectedRoutes = [
    // existing routes ...
    'export_report' => ['ReportController', 'exportReport'],
];
3

Link to the route

In any view, point links or form actions to the new action:
<a href="index.php?action=export_report&id=<?= $report['id'] ?>">Export</a>
There is no separate route file to update. The two arrays in index.php are the single source of truth for all routes.

Build docs developers (and LLMs) love