DALT.PHP
Framework Deep Dive2. Routing

Route Registration

How routes are defined and registered in the application

The Restaurant Menu

Think of routing like a restaurant menu:

  • Customer says: "I want pizza"
  • Waiter checks the menu: "Pizza → Kitchen Station A"
  • Waiter sends order to Station A

In web apps:

  • Browser says: "I want GET /posts"
  • Router checks routes: "/postsposts/index.php"
  • Router runs that controller file

Where Routes Are Defined

Routes live in routes/routes.php. This is your application's "menu" - the list of all available URLs.

The Global Router

global $router;

$router->get('/', 'welcome.php');

What is global $router;?

This is a PHP quirk. Let me explain:

In public/index.php:

$router = new \Core\Router();
require base_path('routes/routes.php');

In routes/routes.php:

global $router;  // "I want to use the $router from outside"
$router->get('/', 'welcome.php');

Why this works:

  • When you require a file, it runs in the current scope
  • global $router means "use the variable that already exists"
  • So the routes file can call methods on the router object

Is this good?

  • ✅ Simple and works
  • ❌ "Magic" - beginners might not understand where $router comes from
  • ❌ Not testable (hard to mock)

Better alternatives:

// Option 1: Return routes array
return [
    ['GET', '/', 'welcome.php'],
    ['GET', '/posts', 'posts/index.php'],
];

// Option 2: Pass router as parameter
// (requires changing how routes are loaded)

But for learning, global is fine and explicit.


Registering Routes

Basic GET Route

$router->get('/', 'welcome.php');

What this means:

  • Method: GET
  • URL: /
  • Controller: welcome.php

When someone visits http://yoursite.com/, the router will:

  1. Match this route
  2. Run app/Http/controllers/welcome.php

Basic POST Route

$router->post('/login', 'session/store.php');

What this means:

  • Method: POST
  • URL: /login
  • Controller: session/store.php

When a form submits to /login, this controller handles it.

All HTTP Methods

$router->get('/posts', 'posts/index.php');      // View list
$router->post('/posts', 'posts/store.php');     // Create new
$router->patch('/posts/{id}', 'posts/update.php');  // Update
$router->put('/posts/{id}', 'posts/replace.php');   // Replace
$router->delete('/posts/{id}', 'posts/destroy.php'); // Delete

Why different methods?

  • RESTful convention
  • Same URL, different actions
  • Semantic meaning (GET = read, POST = create, etc.)

Route Parameters

Dynamic Segments

$router->get('/posts/{id}', 'posts/show.php');

What {id} means:

  • This is a placeholder
  • Matches any value in that position
  • The value is captured and passed to the controller

Examples:

/posts/1      → matches, id = 1
/posts/42     → matches, id = 42
/posts/hello  → matches, id = hello
/posts/       → doesn't match (missing id)
/posts/1/2    → doesn't match (extra segment)

Multiple Parameters

$router->get('/users/{userId}/posts/{postId}', 'posts/show.php');

Matches:

/users/5/posts/10  → userId = 5, postId = 10

Accessing Parameters in Controllers

The router injects parameters into $_GET:

// In posts/show.php controller
$id = $_GET['id'];

$post = $db->query('SELECT * FROM posts WHERE id = :id', [
    'id' => $id
])->findOrFail();

view('posts/show.view.php', ['post' => $post]);

Why $_GET?

  • Familiar to beginners
  • No extra abstractions to learn
  • Works with existing PHP knowledge

The trade-off:

  • ✅ Simple and intuitive
  • ❌ Can overwrite real query string params
  • ❌ Mixes route params with query params

Attaching Middleware

Protecting Routes

$router->get('/dashboard', 'dashboard/index.php')->only('auth');

What ->only('auth') means:

  • Before running the controller, run the auth middleware
  • If middleware fails (user not logged in), stop and redirect
  • If middleware passes, continue to controller

Multiple Middleware

$router->post('/posts', 'posts/store.php')->only(['auth', 'csrf']);

This runs both:

  1. auth - Check if user is logged in
  2. csrf - Check if form has valid CSRF token

Guest-Only Routes

$router->get('/login', 'session/create.php')->only('guest');

What guest means:

  • User must NOT be logged in
  • If already logged in, redirect away
  • Prevents logged-in users from seeing login page

Method Chaining

Notice the ->only() pattern:

$router->get('/dashboard', 'dashboard/index.php')->only('auth');

How this works:

// Inside Router class
public function get($uri, $controller)
{
    $this->add('GET', $uri, $controller);
    return $this;  // ← Returns the router itself
}

public function only($middleware)
{
    // Attach middleware to last route
    return $this;  // ← Returns the router itself
}

By returning $this, you can chain methods:

$router
    ->get('/admin', 'admin/index.php')
    ->only('auth')
    ->only('admin');  // Could chain more if needed

Route Order Matters

Important: Routes are checked in order, first match wins.

The Problem

$router->get('/posts/{id}', 'posts/show.php');
$router->get('/posts/create', 'posts/create.php');

What happens:

/posts/create → matches first route with id = "create"
                Never reaches second route!

The Solution

$router->get('/posts/create', 'posts/create.php');  // Specific first
$router->get('/posts/{id}', 'posts/show.php');      // Generic last

Rule: More specific routes before more generic routes.


Platform Routes

After your app routes, the platform adds its own:

// Your routes (routes/routes.php)
$router->get('/', 'welcome.php');
$router->get('/posts', 'posts/index.php');

// Platform routes (.dalt/routes/routes.php) - loaded after
$router->get('/learn', 'learn/index.php');
$router->get('/learn/lessons/{lesson}', 'learn/lesson.php');

Why this order?

  • Your app routes have priority
  • Platform routes only add if .dalt exists
  • No conflicts because platform uses /learn prefix

Key Takeaways

  1. Routes are explicit - Every URL must be registered
  2. Parameters use {name} syntax - Captured and passed to controllers
  3. Middleware attaches with ->only() - Runs before controllers
  4. Order matters - Specific routes before generic ones
  5. Method chaining - Clean, readable route definitions

What's Good Here

✅ Simple, readable syntax
✅ Explicit route definitions (no magic)
✅ Middleware is easy to attach
✅ Familiar to Laravel developers
✅ Easy to understand for beginners

Design Note

Advanced routing features like named routes, route groups, and avoiding global $router are intentionally omitted. These add complexity that would obscure the fundamental concept: mapping URLs to controllers. DALT keeps it simple and transparent.


Next, explore the routing internals and pattern matching.

On this page