# Routing System

**Table of Contents**

* Introduction
* Router Library Overview
* Architecture
* Route Definition
* HTTP Methods
* URL Parameters
* Route Organization
* Advanced Features
* Best Practices
* Common Patterns
* Troubleshooting

***

### Introduction

PHPTRAVELS v10 uses a lightweight, fast, and flexible routing system powered by the [**qaxim/php-router**](https://packagist.org/packages/qaxim/php-router) library. This regex-based router provides clean URL structures, pattern matching, and efficient request handling while maintaining simplicity and performance.

#### What is Routing?

Routing is the mechanism that maps incoming HTTP requests (URLs) to specific code handlers (functions or controllers). Instead of creating separate PHP files for every page, the router directs all requests through a single entry point and executes the appropriate code based on the URL pattern.

**Traditional Approach (Without Router):**

```
/login.php          -> login.php file
/user/profile.php   -> user/profile.php file
/flights/search.php -> flights/search.php file
```

**Modern Routing Approach:**

```
/login              -> Route handler function
/user/profile       -> Route handler function
/flights/search     -> Route handler function
```

#### Benefits of Modern Routing

**Clean URLs**

* SEO-friendly URLs without .php extensions
* Readable and memorable paths
* Professional appearance

**Centralized Control**

* All routes defined in one location
* Easy to maintain and update
* Clear application structure

**Dynamic Parameters**

* Extract values from URL paths
* Flexible pattern matching
* RESTful API support

**Security**

* Single entry point for all requests
* Consistent authentication checks
* Centralized error handling

**Performance**

* Efficient pattern matching
* No filesystem lookups for every request
* Optimized for speed

***

### Router Library Overview

#### About qaxim/php-router

**Official Package:** <https://packagist.org/packages/qaxim/php-router>

**Key Characteristics:**

* Lightweight (\~300 lines of code)
* Zero dependencies (only requires PHP >=5.3.0)
* Regex-based pattern matching
* RESTful HTTP method support
* Closure/callback support
* Namespace support for controllers
* Service injection capability
* Route grouping with prefixes

**Installation:**

```bash
composer require qaxim/php-router
```

#### Core Features

**1. Regex Pattern Matching** Routes support full regular expression patterns for flexible URL matching:

```php
// Match exact path
$router->get('/flights', $handler);

// Match with parameters
$router->get('/user/([0-9]+)', $handler);

// Match with named patterns
$router->get('/flights/([A-Z]{3})/([A-Z]{3})', $handler);
```

**2. HTTP Method Support** Full support for all standard HTTP methods:

* GET (retrieve data)
* POST (create/submit data)
* PUT (update existing data)
* DELETE (remove data)
* PATCH (partial update)
* HEAD (headers only)
* OPTIONS (CORS preflight)
* TRACE (debugging)
* CONNECT (proxy tunneling)

**3. Closure Handlers** Routes can use anonymous functions (closures) for quick implementations:

```php
$router->get('/about', function() {
    
    // Set page metadata
    $title = $GLOBALS['app']['home_title'];
    $description = $GLOBALS['app']['meta_description'];
    
    // Load 404 page
    require_once views."includes/header.php";
    require_once views."about-file-view.php";
    require_once views."includes/footer.php";
    
});
```

**4. Controller Support** Routes can reference controller classes and methods:

```php
$router->get('/users', '@UserController.index');
```

**5. Service Injection** Share services (like database) across all routes:

```php
$router->post('/users', function() use ($db) {
```

***

### Architecture

#### Application Flow

```
┌─────────────────────────────────────────────────────────────────┐
│                    HTTP Request Received                         │
│              (e.g., GET /flights/NYC/LAX)                        │
└────────────────────────────┬────────────────────────────────────┘
                             │
                             ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Apache/Nginx Server                           │
│           (.htaccess redirects all to index.php)                 │
└────────────────────────────┬────────────────────────────────────┘
                             │
                             ▼
┌─────────────────────────────────────────────────────────────────┐
│                      index.php Entry Point                       │
│   • Load config.php (database, environment)                      │
│   • Initialize Router with 404 handler                           │
│   • Load global data (settings, languages, currencies)           │
│   • Set session defaults                                         │
└────────────────────────────┬────────────────────────────────────┘
                             │
                             ▼
┌─────────────────────────────────────────────────────────────────┐
│              Load Routes (_routes.php)                           │
│   • Components routes (dashboard, UI elements)                   │
│   • Main routes (homepage)                                       │
│   • CMS routes (pages)                                           │
│   • User routes (login, signup, profile)                         │
│   • Module routes (flights, hotels, tours, cars)                 │
│   • Admin routes (admin panel)                                   │
│   • AJAX routes (async operations)                               │
│   • Cron routes (scheduled tasks)                                │
└────────────────────────────┬────────────────────────────────────┘
                             │
                             ▼
┌─────────────────────────────────────────────────────────────────┐
│            Router->dispatchGlobal()                              │
│   • Extract HTTP method from $_SERVER['REQUEST_METHOD']         │
│   • Extract path from $_SERVER['REQUEST_URI']                   │
│   • Clean and normalize the path                                │
└────────────────────────────┬────────────────────────────────────┘
                             │
                             ▼
┌─────────────────────────────────────────────────────────────────┐
│              Pattern Matching Process                            │
│   • Loop through routes for current HTTP method                  │
│   • Test each regex pattern against request path                 │
│   • Extract URL parameters from matches                          │
└────────────────────────────┬────────────────────────────────────┘
                             │
                    ┌────────┴────────┐
                    ▼                 ▼
        ┌───────────────────┐  ┌──────────────────┐
        │  Route Found      │  │  No Match Found  │
        └────────┬──────────┘  └────────┬─────────┘
                 │                      │
                 ▼                      ▼
    ┌──────────────────────┐  ┌──────────────────────┐
    │ Execute Handler      │  │  404 Error Handler   │
    │ • Pass parameters    │  │  • Set 404 status    │
    │ • Run closure/method │  │  • Load 404 view     │
    └──────────┬───────────┘  └──────────┬───────────┘
               │                         │
               └──────────┬──────────────┘
                          │
                          ▼
            ┌──────────────────────────┐
            │    Generate Response     │
            │  • Load view files       │
            │  • Process data          │
            │  • Output HTML/JSON      │
            └──────────────────────────┘
                          │
                          ▼
            ┌──────────────────────────┐
            │   Send to Browser        │
            └──────────────────────────┘
```

#### File Structure

```
/v10
├── index.php                          # Application entry point
├── config.php                         # Global configuration & database
├── .htaccess                          # URL rewriting rules
│
├── app/
│   ├── routes/
│   │   ├── _routes.php                # Master routes loader
│   │   ├── mainRoutes.php             # Homepage route
│   │   ├── globalRoutes.php           # Global app routes
│   │   ├── ajaxRoutes.php             # AJAX endpoint routes
│   │   │
│   │   ├── components/                # UI component routes
│   │   │   ├── dashboard.php
│   │   │   ├── alerts-notifications.php
│   │   │   ├── badges-buttons.php
│   │   │   └── ...
│   │   │
│   │   ├── users/                     # User authentication routes
│   │   │   ├── login.php              # GET & POST /login
│   │   │   ├── signup.php             # GET & POST /signup
│   │   │   ├── logout.php             # GET /logout
│   │   │   ├── dashboard.php          # GET /dashboard
│   │   │   ├── profile.php            # GET /profile
│   │   │   ├── bookings.php           # GET /bookings
│   │   │   └── ...
│   │   │
│   │   ├── flights/                   # Flights module routes
│   │   │   ├── home.php               # GET /flights
│   │   │   ├── listing.php            # GET /flights/{from}/{to}/...
│   │   │   ├── booking.php            # GET /flights/booking/{id}
│   │   │   ├── checkout.php           # POST /flights/checkout
│   │   │   └── invoice.php            # GET /flights/invoice/{id}
│   │   │
│   │   ├── stays/                     # Hotels module routes
│   │   ├── tours/                     # Tours module routes
│   │   ├── cars/                      # Cars module routes
│   │   ├── cms/                       # CMS pages routes
│   │   │
│   │   ├── admin/                     # Admin panel routes
│   │   │   ├── globalRoutes.php       # Admin entry & common
│   │   │   ├── dashboardRoutes.php    # Admin dashboard
│   │   │   ├── settingsRoutes.php     # Settings management
│   │   │   ├── bookingsRoutes.php     # Bookings management
│   │   │   ├── usersRoutes.php        # User management
│   │   │   └── ...
│   │   │
│   │   └── crons/                     # Scheduled task routes
│   │       └── credits-reminders.php
│   │
│   └── views/                         # View templates
│       ├── home.php
│       ├── 404.php
│       ├── auth/
│       ├── modules/
│       └── admin/
│
└── vendor/
    └── qaxim/
        └── php-router/
            └── src/
                └── Router.php         # Router library
```

#### .htaccess Configuration

**Location:** `/v10/.htaccess`

```apache
# Enable URL rewriting
RewriteEngine On

# Redirect all requests to index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
```

**What it does:**

* Enables Apache mod\_rewrite module
* Checks if requested file exists (images, CSS, JS) - serves directly
* If file doesn't exist, sends request to index.php
* Preserves query strings (QSA)
* Stops processing after match (L)

***

### Route Definition

#### Router Initialization

**Location:** `index.php`

```php
use AppRouter\Router;

// Initialize router with 404 error handler
$router = new Router(function ($method, $path, $statusCode) use ($SECURE, $db) {
    // Set HTTP status code
    http_response_code($statusCode);
    
    // Set page metadata
    $title = $GLOBALS['app']['home_title'];
    $description = $GLOBALS['app']['meta_description'];
    
    // Load 404 page
    require_once views."includes/header.php";
    require_once views."404.php";
    require_once views."includes/footer.php";
});
```

**Error Handler Parameters:**

* `$method` - HTTP method (GET, POST, etc.)
* `$path` - Requested URL path
* `$statusCode` - HTTP status code (404 for not found)

#### Basic Route Syntax

**Simple GET Route:**

```php
$router->get('/about', function () use ($db) {
    // Route handler code
    require_once views."pages/about.php";
});
```

**Route with Database Access:**

```php
$router->get('/products', function () use ($SECURE, $db) {
    $products = $db->select("products", "*", ["status" => 1]);
    require_once views."products/list.php";
});
```

#### Route Handler Structure

**Typical Route Pattern in PHPTRAVELS:**

```php
$router->get('/route-path', function () use ($SECURE, $db) {
    
    // 1. AUTHENTICATION (if required)
    // Check if user is logged in
    if (!isset($_SESSION['user_id'])) {
        header('Location: '.root.'login');
        exit;
    }
    
    // 2. DATA PROCESSING
    // Fetch data, process input, business logic
    $user = $db->get("users", "*", ["id" => $_SESSION['user_id']]);
    $bookings = $db->select("bookings", "*", ["user_id" => $user['user_id']]);
    
    // 3. META DATA
    // Set page title and description for SEO
    $title = "My Bookings";
    $description = "View all your travel bookings";
    $header = true;   // Include header
    $footer = true;   // Include footer
    
    // 4. VIEW RENDERING
    // Load template files in sequence
    require_once views."includes/header.php";
    require_once views."user/bookings.php";
    require_once views."includes/footer.php";
});
```

**Components Explained:**

**use ($SECURE, $db)**

* Imports variables from parent scope into closure
* `$SECURE` - Security flag to prevent direct file access
* `$db` - Medoo database instance

**META DATA**

* `$title` - Page title for `<title>` tag
* `$description` - Meta description for SEO
* `$header` / `$footer` - Boolean flags to control layout

**View Files**

* header.php - HTML head, navigation, header
* page.php - Main content for the route
* footer.php - Footer content, scripts, closing tags

***

### HTTP Methods

#### GET Method

Used for retrieving data and displaying pages.

**Homepage:**

```php
$router->get('/', function () use ($SECURE, $db) {
    $title = $GLOBALS['app']['home_title'];
    $description = $GLOBALS['app']['meta_description'];
    
    require_once views."includes/header.php";
    require_once views."home.php";
    require_once views."includes/footer.php";
});
```

**User Dashboard:**

```php
$router->get('/dashboard', function () use ($SECURE, $db) {
    // Ensure user is logged in
    if (!isset($_SESSION['user_id'])) {
        header('Location: '.root.'login');
        exit;
    }
    
    $user = $db->get("users", "*", ["user_id" => $_SESSION['user_id']]);
    
    require_once views."includes/header.php";
    require_once views."user/dashboard.php";
    require_once views."includes/footer.php";
});
```

**Flights Module Home:**

```php
$router->get('/flights', function () use ($SECURE, $db) {
    $title = $GLOBALS['app']['home_title'];
    $description = $GLOBALS['app']['meta_description'];
    
    require_once views."includes/header.php";
    require_once views."modules/flights/index.php";
    require_once views."includes/footer.php";
});
```

#### POST Method

Used for form submissions and data creation.

**Login Form Processing:**

```php
$router->post('/login', function () use ($SECURE, $db) {
    // CSRF validation
    if (!CSRF::validateToken($_POST['csrf_token'] ?? '')) {
        $_SESSION['login_error'] = 'csrf';
        header('Location: '.root.'login');
        exit;
    }
    
    // Get form data
    $email = trim($_POST['email'] ?? '');
    $password = trim($_POST['password'] ?? '');
    
    // Validate inputs
    if (empty($email) || empty($password)) {
        $_SESSION['login_error'] = 'empty';
        header('Location: '.root.'login');
        exit;
    }
    
    // Verify credentials
    $user = $db->get('users', '*', ['email' => $email]);
    
    if (!$user || !password_verify($password, $user['password'])) {
        $_SESSION['login_error'] = 'invalid';
        header('Location: '.root.'login');
        exit;
    }
    
    // Set session and redirect
    $_SESSION['user_id'] = $user['user_id'];
    $_SESSION['user_data'] = $user;
    
    header('Location: '.root.'dashboard');
    exit;
});
```

**Signup Form Processing:**

```php
$router->post('/signup', function () use ($SECURE, $db) {
    // CSRF validation
    if (!CSRF::validateToken($_POST['csrf_token'] ?? '')) {
        $_SESSION['error'] = 'Invalid security token';
        header('Location: '.root.'signup');
        exit;
    }
    
    // Get and sanitize form data
    $first_name = trim($_POST['first_name'] ?? '');
    $last_name = trim($_POST['last_name'] ?? '');
    $email = filter_var($_POST['email'] ?? '', FILTER_SANITIZE_EMAIL);
    $password = $_POST['password'] ?? '';
    
    // Validate email format
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $_SESSION['error'] = 'Invalid email format';
        header('Location: '.root.'signup');
        exit;
    }
    
    // Check if email already exists
    if ($db->has("users", ["email" => $email])) {
        $_SESSION['error'] = 'Email already registered';
        header('Location: '.root.'signup');
        exit;
    }
    
    // Create user
    $user_id = uniqid('user_');
    $hashed_password = password_hash($password, PASSWORD_BCRYPT);
    
    $db->insert("users", [
        "user_id" => $user_id,
        "first_name" => $first_name,
        "last_name" => $last_name,
        "email" => $email,
        "password" => $hashed_password,
        "status" => "active",
        "created_at" => date('Y-m-d H:i:s')
    ]);
    
    // Send verification email (optional)
    // MAILER('email-verification', ...);
    
    $_SESSION['success'] = 'Account created successfully!';
    header('Location: '.root.'login');
    exit;
});
```

#### PUT Method

Used for updating existing resources (typically for APIs).

```php
$router->put('/api/user/([0-9]+)', function ($userId) use ($SECURE, $db) {
    // Get JSON input
    $input = json_decode(file_get_contents('php://input'), true);
    
    // Update user
    $db->update("users", [
        "first_name" => $input['first_name'],
        "last_name" => $input['last_name'],
        "updated_at" => date('Y-m-d H:i:s')
    ], [
        "id" => $userId
    ]);
    
    // Return JSON response
    header('Content-Type: application/json');
    echo json_encode(['success' => true, 'message' => 'User updated']);
});
```

#### DELETE Method

Used for removing resources (typically for APIs).

```php
$router->delete('/api/booking/([0-9]+)', function ($bookingId) use ($SECURE, $db) {
    // Verify ownership
    $booking = $db->get("bookings", "*", ["id" => $bookingId]);
    
    if ($booking['user_id'] !== $_SESSION['user_id']) {
        http_response_code(403);
        echo json_encode(['error' => 'Unauthorized']);
        exit;
    }
    
    // Soft delete
    $db->update("bookings", [
        "booking_status" => "cancelled",
        "updated_at" => date('Y-m-d H:i:s')
    ], [
        "id" => $bookingId
    ]);
    
    header('Content-Type: application/json');
    echo json_encode(['success' => true]);
});
```

#### Handling Multiple Methods

**Same Route, Different Methods:**

```php
// Display login form
$router->get('/login', function () use ($SECURE, $db) {
    require_once views."auth/login.php";
});

// Process login form
$router->post('/login', function () use ($SECURE, $db) {
    // Form processing logic
});
```

**Universal Handler:**

```php
$router->route('*', '/api/test', function () use ($db) {
    // Handles ALL HTTP methods (GET, POST, PUT, DELETE, etc.)
    $method = $_SERVER['REQUEST_METHOD'];
    echo "Received $method request";
});
```

***

### URL Parameters

#### Dynamic Route Parameters

Routes can capture dynamic values from the URL using regular expressions.

#### Basic Parameter Extraction

**User Profile by ID:**

```php
$router->get('/user/([0-9]+)', function ($userId) use ($SECURE, $db) {
    // $userId contains the captured number from URL
    // Example: /user/123 -> $userId = "123"
    
    $user = $db->get("users", "*", ["id" => $userId]);
    
    if (!$user) {
        http_response_code(404);
        echo "User not found";
        exit;
    }
    
    require_once views."user/profile.php";
});
```

**Blog Post by Slug:**

```php
$router->get('/blog/([a-z0-9-]+)', function ($slug) use ($SECURE, $db) {
    // $slug contains alphanumeric characters and hyphens
    // Example: /blog/travel-tips-2024 -> $slug = "travel-tips-2024"
    
    $post = $db->get("blogs", "*", ["post_slug" => $slug]);
    
    require_once views."blog/post.php";
});
```

#### Multiple Parameters

**Flight Search Route:**

```php
$router->get('/flights/([A-Z]{3})/([A-Z]{3})', function ($from, $to) use ($SECURE, $db) {
    // Captures two 3-letter airport codes
    // Example: /flights/NYC/LAX -> $from = "NYC", $to = "LAX"
    
    $_SESSION['from_airport'] = $from;
    $_SESSION['to_airport'] = $to;
    
    require_once views."modules/flights/search.php";
});
```

**Complex Flight Route with Multiple Parameters:**

```php
$router->get(
    '/flights/([^/]+)/([^/]+)/(oneway|roundtrip|multicity)/(economy|premium_economy|business|first)/([^/]+)/(.*)', 
    function ($from, $to, $type, $class, $departure, $additional) use ($SECURE, $db) {
        // Example URL: /flights/NYC/LAX/roundtrip/economy/2024-12-25/2024-12-30/2/0/0
        
        // Convert to uppercase
        $from = strtoupper(trim($from));
        $to = strtoupper(trim($to));
        
        // Parse additional parameters
        $additionalParts = explode('/', trim($additional, '/'));
        
        // Set session values
        $_SESSION['from_airport'] = $from;
        $_SESSION['to_airport'] = $to;
        $_SESSION['flight_type'] = $type;
        $_SESSION['class'] = $class;
        $_SESSION['flights_departure_date'] = $departure;
        
        // Handle based on flight type
        if ($type === 'oneway') {
            $_SESSION['flights_return_date'] = '';
            $_SESSION['adults'] = intval($additionalParts[0] ?? 1);
            $_SESSION['children'] = intval($additionalParts[1] ?? 0);
            $_SESSION['infants'] = intval($additionalParts[2] ?? 0);
        } elseif ($type === 'roundtrip') {
            $_SESSION['flights_return_date'] = $additionalParts[0] ?? '';
            $_SESSION['adults'] = intval($additionalParts[1] ?? 1);
            $_SESSION['children'] = intval($additionalParts[2] ?? 0);
            $_SESSION['infants'] = intval($additionalParts[3] ?? 0);
        }
        
        // Trigger webhook
        triggerWebhook('flights/search', 'flights.search.initiated', [
            'from' => $from,
            'to' => $to,
            'type' => $type,
            'class' => $class
        ]);
        
        require_once views."modules/flights/listing/flights.php";
    }
);
```

#### Regex Patterns Reference

| Pattern                        | Matches                        | Example                       |
| ------------------------------ | ------------------------------ | ----------------------------- |
| `([0-9]+)`                     | One or more digits             | 123, 4567                     |
| `([a-z]+)`                     | One or more lowercase letters  | hello, world                  |
| `([A-Z]{3})`                   | Exactly 3 uppercase letters    | NYC, LAX                      |
| `([a-z0-9-]+)`                 | Letters, numbers, hyphens      | my-post-123                   |
| `([^/]+)`                      | Anything except forward slash  | any-text-here                 |
| `(.*)`                         | Anything (greedy)              | captures/everything/remaining |
| `(oneway\|roundtrip)`          | Either "oneway" or "roundtrip" | oneway                        |
| `([0-9]{4}-[0-9]{2}-[0-9]{2})` | Date format YYYY-MM-DD         | 2024-12-25                    |

#### Parameter Validation

**Always validate captured parameters:**

```php
$router->get('/booking/([0-9]+)', function ($bookingId) use ($SECURE, $db) {
    // Convert to integer
    $bookingId = intval($bookingId);
    
    // Validate it's a positive number
    if ($bookingId <= 0) {
        http_response_code(400);
        echo "Invalid booking ID";
        exit;
    }
    
    // Verify booking exists and belongs to user
    $booking = $db->get("bookings", "*", [
        "id" => $bookingId,
        "user_id" => $_SESSION['user_id']
    ]);
    
    if (!$booking) {
        http_response_code(404);
        echo "Booking not found";
        exit;
    }
    
    require_once views."bookings/detail.php";
});
```

***

### Route Organization

#### Modular Structure

PHPTRAVELS v10 organizes routes into logical modules for maintainability and scalability.

#### Master Routes Loader

**Location:** `app/routes/_routes.php`

```php
<?php
@$SECURE or die('Access Denied!');

// COMPONENTS ROUTES - UI elements
require_once 'app/routes/components/dashboard.php';
require_once 'app/routes/components/alerts-notifications.php';
require_once 'app/routes/components/form-inputs.php';

// MAIN ROUTES - Homepage
require_once 'app/routes/mainRoutes.php';

// CMS ROUTES - Content pages
require_once 'app/routes/cms/page.php';

// USER ROUTES - Authentication & profiles
require_once 'app/routes/users/login.php';
require_once 'app/routes/users/signup.php';
require_once 'app/routes/users/dashboard.php';

// BOOKING MODULE ROUTES
require_once 'app/routes/flights/home.php';
require_once 'app/routes/flights/listing.php';
require_once 'app/routes/stays/home.php';
require_once 'app/routes/tours/home.php';
require_once 'app/routes/cars/home.php';

// ADMIN ROUTES - Admin panel
require_once 'app/routes/admin/globalRoutes.php';
require_once 'app/routes/admin/dashboardRoutes.php';
require_once 'app/routes/admin/settingsRoutes.php';

// UTILITY ROUTES
require_once 'app/routes/globalRoutes.php';
require_once 'app/routes/ajaxRoutes.php';
require_once 'app/routes/crons/credits-reminders.php';
```

**Benefits of This Structure:**

* **Separation of Concerns:** Each file handles related routes
* **Easy Maintenance:** Find routes quickly by feature/module
* **Team Collaboration:** Multiple developers can work on different route files
* **Lazy Loading:** Only load needed routes (can be optimized)
* **Clear Hierarchy:** Understand app structure at a glance

#### Creating a New Route Module

**Step 1: Create Route File**

**Location:** `app/routes/bookings/history.php`

```php
<?php
// FILE: app/routes/bookings/history.php
@$SECURE or die('Access Denied!');

// Booking history page
$router->get('/bookings/history', function () use ($SECURE, $db) {
    // Check authentication
    if (!isset($_SESSION['user_id'])) {
        header('Location: '.root.'login');
        exit;
    }
    
    // Fetch user bookings
    $bookings = $db->select("bookings", "*", [
        "user_id" => $_SESSION['user_id'],
        "ORDER" => ["created_at" => "DESC"]
    ]);
    
    $title = "Booking History";
    $description = "View your past bookings";
    
    require_once views."includes/header.php";
    require_once views."bookings/history.php";
    require_once views."includes/footer.php";
});

// Booking detail page
$router->get('/bookings/([0-9]+)', function ($bookingId) use ($SECURE, $db) {
    if (!isset($_SESSION['user_id'])) {
        header('Location: '.root.'login');
        exit;
    }
    
    $booking = $db->get("bookings", "*", [
        "id" => $bookingId,
        "user_id" => $_SESSION['user_id']
    ]);
    
    if (!$booking) {
        http_response_code(404);
        require_once views."404.php";
        exit;
    }
    
    require_once views."includes/header.php";
    require_once views."bookings/detail.php";
    require_once views."includes/footer.php";
});
```

**Step 2: Register in \_routes.php**

```php
// Add to app/routes/_routes.php
require_once 'app/routes/bookings/history.php';
```

**Step 3: Create View File**

**Location:** `app/views/bookings/history.php`

```php
<div class="container">
    <h1>Booking History</h1>
    <?php foreach ($bookings as $booking): ?>
        <div class="booking-card">
            <h3>Invoice: <?= $booking['invoice_id'] ?></h3>
            <p>Status: <?= $booking['booking_status'] ?></p>
            <a href="<?= root ?>bookings/<?= $booking['id'] ?>">View Details</a>
        </div>
    <?php endforeach; ?>
</div>
```

#### Route File Naming Conventions

**Descriptive Names:**

* `users/login.php` - User login routes
* `flights/booking.php` - Flight booking routes
* `admin/dashboardRoutes.php` - Admin dashboard routes

**Plural for Collections:**

* `bookings/list.php` - List all bookings
* `users/management.php` - User management

**Suffix for Related Routes:**

* `settingsRoutes.php` - Multiple settings-related routes
* `ajaxRoutes.php` - AJAX endpoint routes

***

### Advanced Features

#### Route Grouping with Prefixes

Group related routes under a common URL prefix using `mount()`.

**Admin Routes Example:**

```php
$router->mount('/admin', function($router) use ($SECURE, $db) {
    
    // All these routes are automatically prefixed with /admin
    
    $router->get('/dashboard', function() use ($SECURE, $db) {
        // Accessible at: /admin/dashboard
        ADMIN_AUTH(); // Check admin authentication
        require_once views."admin/dashboard.php";
    });
    
    $router->get('/users', function() use ($SECURE, $db) {
        // Accessible at: /admin/users
        ADMIN_AUTH();
        $users = $db->select("users", "*");
        require_once views."admin/users.php";
    });
    
    $router->get('/settings', function() use ($SECURE, $db) {
        // Accessible at: /admin/settings
        ADMIN_AUTH();
        require_once views."admin/settings.php";
    });
});
```

**Benefits:**

* DRY (Don't Repeat Yourself) - Write prefix once
* Easy URL restructuring
* Logical grouping
* Cleaner code

**Nested Mounting:**

```php
$router->mount('/api', function($router) use ($db) {
    
    // API v1 routes
    $router->mount('/v1', function($router) use ($db) {
        
        $router->get('/users', function() use ($db) {
            // Accessible at: /api/v1/users
            echo json_encode($db->select("users", "*"));
        });
        
        $router->get('/bookings', function() use ($db) {
            // Accessible at: /api/v1/bookings
            echo json_encode($db->select("bookings", "*"));
        });
    });
    
    // API v2 routes
    $router->mount('/v2', function($router) use ($db) {
        $router->get('/users', function() use ($db) {
            // Accessible at: /api/v2/users
            // Different implementation for v2
        });
    });
});
```

#### Service Injection

Share services (like database, logger, cache) across all routes.

**Setting Service:**

```php
// In index.php
$router = new Router($errorHandler);
$router->setService($db); // Inject database
```

**Using Service in Routes:**

```php
// Routes now receive $db as first parameter automatically
$router->get('/users', function($db) {
    // $db is automatically injected
    $users = $db->select("users", "*");
    echo json_encode($users);
});

// With additional parameters
$router->get('/user/([0-9]+)', function($db, $userId) {
    // $db is first, then captured parameters
    $user = $db->get("users", "*", ["id" => $userId]);
    echo json_encode($user);
});
```

**Note:** PHPTRAVELS currently uses `use ($db)` closure syntax instead of service injection, but both approaches are supported.

#### Custom 404 Handling

Customize error pages based on different scenarios.

**Basic 404 Handler:**

```php
$router = new Router(function ($method, $path, $statusCode) use ($SECURE, $db) {
    http_response_code($statusCode);
    
    $title = "Page Not Found";
    $description = "The requested page could not be found";
    
    require_once views."includes/header.php";
    require_once views."404.php";
    require_once views."includes/footer.php";
});
```

**Advanced 404 with Logging:**

```php
$router = new Router(function ($method, $path, $statusCode) use ($SECURE, $db) {
    http_response_code($statusCode);
    
    // Log 404 errors
    $db->insert("logs_404", [
        "path" => $path,
        "method" => $method,
        "ip_address" => $_SERVER['REMOTE_ADDR'],
        "user_agent" => $_SERVER['HTTP_USER_AGENT'],
        "referrer" => $_SERVER['HTTP_REFERER'] ?? '',
        "created_at" => date('Y-m-d H:i:s')
    ]);
    
    // Check if it's an API request
    if (strpos($path, '/api/') === 0) {
        header('Content-Type: application/json');
        echo json_encode([
            'error' => true,
            'message' => 'Endpoint not found',
            'path' => $path
        ]);
        exit;
    }
    
    // Regular 404 page
    require_once views."includes/header.php";
    require_once views."404.php";
    require_once views."includes/footer.php";
});
```

#### Middleware Pattern

Implement middleware-like functionality using helper functions.

**Authentication Middleware:**

```php
// helpers.php
function requireAuth() {
    if (!isset($_SESSION['user_id'])) {
        $_SESSION['redirect_after_login'] = $_SERVER['REQUEST_URI'];
        header('Location: '.root.'login');
        exit;
    }
}

function requireAdmin() {
    if (!isset($_SESSION['admin_logged_in']) || !$_SESSION['admin_logged_in']) {
        header('Location: '.root.'admin/login');
        exit;
    }
}
```

**Usage in Routes:**

```php
$router->get('/dashboard', function () use ($SECURE, $db) {
    requireAuth(); // Middleware check
    
    // Route logic continues only if authenticated
    require_once views."user/dashboard.php";
});

$router->get('/admin/users', function () use ($SECURE, $db) {
    requireAdmin(); // Admin-only middleware
    
    $users = $db->select("users", "*");
    require_once views."admin/users.php";
});
```

***

### Best Practices

#### 1. Security

**Always Validate Input:**

```php
$router->post('/api/booking', function () use ($SECURE, $db) {
    // Validate and sanitize all inputs
    $user_id = filter_var($_POST['user_id'] ?? '', FILTER_SANITIZE_NUMBER_INT);
    $email = filter_var($_POST['email'] ?? '', FILTER_SANITIZE_EMAIL);
    
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        http_response_code(400);
        echo json_encode(['error' => 'Invalid email']);
        exit;
    }
    
    // Process booking...
});
```

**CSRF Protection:**

```php
$router->post('/profile/update', function () use ($SECURE, $db) {
    // Verify CSRF token
    if (!CSRF::validateToken($_POST['csrf_token'] ?? '')) {
        $_SESSION['error'] = 'Invalid security token';
        header('Location: '.root.'profile');
        exit;
    }
    
    // Update profile...
});
```

**SQL Injection Prevention:**

```php
// NEVER do this:
$user_id = $_GET['id'];
$query = "SELECT * FROM users WHERE id = $user_id"; // VULNERABLE!

// ALWAYS use Medoo's parameterized queries:
$user = $db->get("users", "*", ["id" => $_GET['id']]); // SAFE
```

#### 2. Performance

**Cache Route Results:**

```php
$router->get('/api/countries', function () use ($SECURE, $db) {
    // Check cache first
    if (isset($_SESSION['countries_cache'])) {
        header('Content-Type: application/json');
        echo json_encode($_SESSION['countries_cache']);
        exit;
    }
    
    // Fetch from database
    $countries = $db->select("countries", "*", ["status" => 1]);
    
    // Cache for session
    $_SESSION['countries_cache'] = $countries;
    
    header('Content-Type: application/json');
    echo json_encode($countries);
});
```

**Minimize Database Queries:**

```php
// BAD - Multiple queries in loop
foreach ($bookings as $booking) {
    $user = $db->get("users", "*", ["id" => $booking['user_id']]);
    // Use $user...
}

// GOOD - Single query with JOIN
$bookings = $db->select("bookings", [
    "[>]users" => ["user_id" => "id"]
], [
    "bookings.id",
    "bookings.invoice_id",
    "users.first_name",
    "users.last_name",
    "users.email"
]);
```

#### 3. Code Organization

**Keep Routes Thin:**

```php
// BAD - Too much logic in route
$router->post('/booking/create', function () use ($SECURE, $db) {
    // 200 lines of validation, processing, email sending...
});

// GOOD - Delegate to service classes
$router->post('/booking/create', function () use ($SECURE, $db) {
    requireAuth();
    
    $bookingService = new BookingService($db);
    $result = $bookingService->createBooking($_POST);
    
    if ($result['success']) {
        header('Location: '.root.'bookings/'.$result['booking_id']);
    } else {
        $_SESSION['error'] = $result['message'];
        header('Location: '.root.'checkout');
    }
});
```

**Use Constants:**

```php
// Define in config.php
define('MIN_PASSWORD_LENGTH', 8);
define('MAX_LOGIN_ATTEMPTS', 5);

// Use in routes
$router->post('/signup', function () use ($SECURE, $db) {
    $password = $_POST['password'] ?? '';
    
    if (strlen($password) < MIN_PASSWORD_LENGTH) {
        $_SESSION['error'] = 'Password too short';
        header('Location: '.root.'signup');
        exit;
    }
    
    // Continue...
});
```

#### 4. Error Handling

**Always Handle Errors:**

```php
$router->get('/invoice/([0-9]+)', function ($invoiceId) use ($SECURE, $db) {
    try {
        $invoice = $db->get("bookings", "*", ["id" => $invoiceId]);
        
        if (!$invoice) {
            throw new Exception("Invoice not found");
        }
        
        // Verify ownership
        if ($invoice['user_id'] !== $_SESSION['user_id']) {
            throw new Exception("Unauthorized access");
        }
        
        require_once views."invoices/detail.php";
        
    } catch (Exception $e) {
        error_log("Invoice Error: " . $e->getMessage());
        $_SESSION['error'] = "Unable to load invoice";
        header('Location: '.root.'dashboard');
        exit;
    }
});
```

**Graceful Degradation:**

```php
$router->get('/api/weather', function () use ($db) {
    try {
        $weather = file_get_contents('https://api.weather.com/...');
        echo $weather;
    } catch (Exception $e) {
        // Return cached data or default response
        http_response_code(200); // Don't break the app
        echo json_encode([
            'temp' => 'N/A',
            'message' => 'Weather data temporarily unavailable'
        ]);
    }
});
```

#### 5. RESTful Design

**Follow REST Conventions:**

```php
// List all bookings
$router->get('/api/bookings', function () use ($db) {
    echo json_encode($db->select("bookings", "*"));
});

// Get single booking
$router->get('/api/bookings/([0-9]+)', function ($id) use ($db) {
    echo json_encode($db->get("bookings", "*", ["id" => $id]));
});

// Create new booking
$router->post('/api/bookings', function () use ($db) {
    $data = json_decode(file_get_contents('php://input'), true);
    $db->insert("bookings", $data);
    echo json_encode(['id' => $db->id()]);
});

// Update booking
$router->put('/api/bookings/([0-9]+)', function ($id) use ($db) {
    $data = json_decode(file_get_contents('php://input'), true);
    $db->update("bookings", $data, ["id" => $id]);
    echo json_encode(['success' => true]);
});

// Delete booking
$router->delete('/api/bookings/([0-9]+)', function ($id) use ($db) {
    $db->delete("bookings", ["id" => $id]);
    echo json_encode(['success' => true]);
});
```

***

### Common Patterns

#### Pattern 1: Authentication Check

```php
$router->get('/protected-page', function () use ($SECURE, $db) {
    // Check if user is logged in
    if (!isset($_SESSION['user_id'])) {
        $_SESSION['redirect_after_login'] = $_SERVER['REQUEST_URI'];
        header('Location: '.root.'login');
        exit;
    }
    
    // User is authenticated, continue
    require_once views."protected/page.php";
});
```

#### Pattern 2: AJAX JSON Response

```php
$router->post('/ajax/update-profile', function () use ($SECURE, $db) {
    header('Content-Type: application/json');
    
    try {
        // Validate session
        if (!isset($_SESSION['user_id'])) {
            echo json_encode(['success' => false, 'message' => 'Not authenticated']);
            exit;
        }
        
        // Update profile
        $db->update("users", [
            "first_name" => $_POST['first_name'],
            "last_name" => $_POST['last_name']
        ], [
            "user_id" => $_SESSION['user_id']
        ]);
        
        echo json_encode(['success' => true, 'message' => 'Profile updated']);
        
    } catch (Exception $e) {
        echo json_encode(['success' => false, 'message' => $e->getMessage()]);
    }
});
```

#### Pattern 3: Form Processing with Validation

```php
$router->post('/contact/submit', function () use ($SECURE, $db) {
    // CSRF validation
    if (!CSRF::validateToken($_POST['csrf_token'] ?? '')) {
        $_SESSION['error'] = 'Invalid security token';
        header('Location: '.root.'contact');
        exit;
    }
    
    // Get and sanitize inputs
    $name = trim($_POST['name'] ?? '');
    $email = filter_var($_POST['email'] ?? '', FILTER_SANITIZE_EMAIL);
    $message = trim($_POST['message'] ?? '');
    
    // Validation
    $errors = [];
    
    if (empty($name)) {
        $errors[] = 'Name is required';
    }
    
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors[] = 'Invalid email address';
    }
    
    if (strlen($message) < 10) {
        $errors[] = 'Message must be at least 10 characters';
    }
    
    // If errors, redirect back
    if (!empty($errors)) {
        $_SESSION['errors'] = $errors;
        $_SESSION['form_data'] = $_POST;
        header('Location: '.root.'contact');
        exit;
    }
    
    // Save to database
    $db->insert("contact_messages", [
        "name" => $name,
        "email" => $email,
        "message" => $message,
        "created_at" => date('Y-m-d H:i:s')
    ]);
    
    // Send notification email
    // MAILER('contact-notification', ...);
    
    $_SESSION['success'] = 'Message sent successfully!';
    header('Location: '.root.'contact/thank-you');
});
```

#### Pattern 4: Pagination

```php
$router->get('/blogs', function () use ($SECURE, $db) {
    // Get page number from query string
    $page = isset($_GET['page']) ? intval($_GET['page']) : 1;
    $per_page = 10;
    $offset = ($page - 1) * $per_page;
    
    // Get total count
    $total = $db->count("blogs", ["status" => 1]);
    $total_pages = ceil($total / $per_page);
    
    // Get paginated results
    $blogs = $db->select("blogs", "*", [
        "status" => 1,
        "ORDER" => ["created_at" => "DESC"],
        "LIMIT" => [$offset, $per_page]
    ]);
    
    require_once views."includes/header.php";
    require_once views."blogs/list.php";
    require_once views."includes/footer.php";
});
```

#### Pattern 5: File Upload Handling

```php
$router->post('/upload/avatar', function () use ($SECURE, $db) {
    requireAuth();
    
    // Check if file was uploaded
    if (!isset($_FILES['avatar']) || $_FILES['avatar']['error'] !== UPLOAD_ERR_OK) {
        $_SESSION['error'] = 'File upload failed';
        header('Location: '.root.'profile');
        exit;
    }
    
    $file = $_FILES['avatar'];
    
    // Validate file type
    $allowed = ['image/jpeg', 'image/png', 'image/gif'];
    if (!in_array($file['type'], $allowed)) {
        $_SESSION['error'] = 'Invalid file type';
        header('Location: '.root.'profile');
        exit;
    }
    
    // Validate file size (2MB max)
    if ($file['size'] > 2 * 1024 * 1024) {
        $_SESSION['error'] = 'File too large (max 2MB)';
        header('Location: '.root.'profile');
        exit;
    }
    
    // Generate unique filename
    $ext = pathinfo($file['name'], PATHINFO_EXTENSION);
    $filename = uniqid('avatar_') . '.' . $ext;
    $upload_path = 'uploads/avatars/' . $filename;
    
    // Move uploaded file
    if (move_uploaded_file($file['tmp_name'], $upload_path)) {
        // Update user record
        $db->update("users", [
            "avatar" => $upload_path
        ], [
            "user_id" => $_SESSION['user_id']
        ]);
        
        $_SESSION['success'] = 'Avatar updated successfully';
    } else {
        $_SESSION['error'] = 'Failed to save file';
    }
    
    header('Location: '.root.'profile');
});
```

***

### Troubleshooting

#### Common Issues

**Problem 1: 404 on All Routes**

**Symptoms:**

* Homepage works, but all other routes return 404
* Apache shows "Not Found" error

**Solution:**

1. Check .htaccess file exists in root
2. Verify mod\_rewrite is enabled:

```bash
# Enable mod_rewrite on Apache
sudo a2enmod rewrite
sudo service apache2 restart
```

3. Ensure .htaccess is being read (check Apache config)
4. Verify .htaccess content:

```apache
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
```

**Problem 2: Route Not Matching**

**Symptoms:**

* Route is defined but returns 404
* Other routes work fine

**Solution:**

1. Check route pattern regex:

```php
// Debug: Print all registered routes
echo '<pre>';
print_r($router->routes);
echo '</pre>';
```

2. Test regex pattern:

```php
$pattern = '/flights/([A-Z]{3})/([A-Z]{3})';
$path = '/flights/NYC/LAX';
if (preg_match('@^' . $pattern . '$@', $path, $matches)) {
    print_r($matches); // Should show array with matches
}
```

3. Check route order (specific routes before general ones)
4. Verify route file is included in \_routes.php

**Problem 3: Parameters Not Captured**

**Symptoms:**

* Route matches but parameters are wrong/empty

**Solution:**

1. Check regex capturing groups use parentheses:

```php
// BAD - No capturing group
$router->get('/user/[0-9]+', ...);

// GOOD - With capturing group
$router->get('/user/([0-9]+)', ...);
```

2. Verify parameter order matches pattern:

```php
// Pattern has 3 groups
$router->get('/flights/([A-Z]{3})/([A-Z]{3})/([0-9]{4}-[0-9]{2}-[0-9]{2})', 
    function ($from, $to, $date) { // Must have 3 parameters
        // ...
    }
);
```

**Problem 4: POST Data Not Received**

**Symptoms:**

* POST route works but $\_POST is empty

**Solution:**

1. Check form Content-Type:

```html
<!-- Regular form -->
<form method="POST" enctype="application/x-www-form-urlencoded">

<!-- File upload form -->
<form method="POST" enctype="multipart/form-data">
```

2. For JSON requests, read from input stream:

```php
$router->post('/api/data', function () {
    // Don't use $_POST for JSON requests
    $data = json_decode(file_get_contents('php://input'), true);
});
```

**Problem 5: Infinite Redirect Loop**

**Symptoms:**

* Browser shows "Too many redirects" error

**Solution:**

```php
// BAD - Can cause infinite loop
$router->get('/dashboard', function () {
    if (!isset($_SESSION['user_id'])) {
        header('Location: /login');
        exit;
    }
    // ...
});

$router->get('/login', function () {
    if (isset($_SESSION['user_id'])) {
        header('Location: /dashboard'); // Loops if session is set!
        exit;
    }
    // ...
});

// GOOD - Clear redirect logic
$router->get('/dashboard', function () {
    if (!isset($_SESSION['user_id'])) {
        header('Location: '.root.'login');
        exit;
    }
    // Dashboard content
});

$router->get('/login', function () {
    // Only redirect if actually logged in
    if (isset($_SESSION['user_id']) && !empty($_SESSION['user_id'])) {
        header('Location: '.root.'dashboard');
        exit;
    }
    // Login form
});
```

#### Debugging Tools

**1. Route Inspection:**

```php
// In index.php before dispatch
echo '<pre>';
echo "Request Method: " . $_SERVER['REQUEST_METHOD'] . "\n";
echo "Request URI: " . $_SERVER['REQUEST_URI'] . "\n";
echo "Request Path: " . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) . "\n";
echo '</pre>';
exit;
```

**2. Enable Error Reporting:**

```php
// In config.php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
```

**3. Log Route Matches:**

```php
// Modify Router.php dispatch method to log matches
if (preg_match('@^' . $regex . '$@', $path, $params)) {
    error_log("Route matched: $regex -> $path");
    error_log("Parameters: " . print_r($params, true));
    // ...
}
```

***

### Summary

The PHPTRAVELS v10 routing system provides:

**Lightweight & Fast**

* Minimal overhead with regex-based matching
* Direct callback execution
* No heavy framework dependencies

**Flexible & Powerful**

* Full regex support for complex patterns
* Multiple parameter extraction
* All HTTP methods supported

**Well-Organized**

* Modular route file structure
* Logical grouping by feature/module
* Easy to maintain and extend

**Secure**

* Single entry point for all requests
* Consistent authentication checks
* CSRF protection support

**Developer-Friendly**

* Clear, readable syntax
* Closure and controller support
* Comprehensive error handling

**RESTful**

* Supports REST conventions
* Clean URL structures
* API-ready architecture

**Production-Ready**

* Handles complex real-world routing needs
* Proven in PHPTRAVELS booking platform
* Scalable for large applications

The qaxim/php-router library combined with PHPTRAVELS' organized structure creates a robust, maintainable routing system suitable for complex travel booking applications with multiple modules, authentication requirements, and API endpoints.
