Routes ====== In LacePHP, a route is like a signpost on a map, it tells your application which code to run when a user visits a particular URL. Think of each route as an address: when someone “knocks” on that address (makes an HTTP request), your code responds by showing a page, returning data, or performing an action. The `routes/` folder in your project contains three starter files: - ``api.php`` – for JSON-based API endpoints - ``web.php`` – for browser views and standard web pages - ``system.php`` – for internal or framework-level routes You can organize your own routes in whichever file makes the most sense. Closure Routes -------------- A **closure** route uses an anonymous function right in the route definition. This is the simplest way to get started. 1. Open ``routes/web.php``. 2. Add this code: .. code-block:: php $router->sewGet('/hello', function() { return 'Hello, LacePHP!'; }); 3. Start your built-in server (Run this in your terminal, changing the directory to the root of your project folder.): .. code-block:: console php lace tread 4. In your browser, go to ``http://127.0.0.1:6916``. You should see: .. code-block:: text Hello, LacePHP! Here’s what happened: - **sewGet('/hello', …)** registers a route that only responds to **GET** requests at the path `/hello`. - The **closure** (the `function() { … }` block) runs when someone visits `/hello`. - Whatever the function returns becomes the HTTP response body. .. container:: note **Tip:** The “sew” prefix is just LacePHP’s playful nod to stitching together routes. You can also write `$router->get('/hello', …)` if you prefer. Controller Routes ----------------- As your app grows, you’ll want to move logic out of closures and into dedicated Controller classes. Controllers group related actions together. 1. Create a file **weave/Controllers/GreetingController.php**: .. code-block:: php sewGet( '/greet', # URL pattern with {name} placeholder [GreetingController::class, 'hello'] ); 3. Visit ``http://127.0.0.1:6916/greet`` to see: .. code-block:: text Hello, Alice! How this works: - **'/greet'** tells LacePHP to capture the path greet. - LacePHP instantiates `GreetingController`, calls its `hello()` method. - The return value is sent back to the browser. .. warning:: Each route path + HTTP method combination must be unique. You cannot have two `GET /hello` routes. Route names and placeholders should use only letters, numbers, hyphens, or underscores—no spaces or special symbols. Route Parameters ---------------- You can capture dynamic parts of the URL and pass them directly into your controller methods. Placeholders go in curly braces (`{}`) and LacePHP will extract them in the order you declare them. 1. **Create the controller** .. code-block:: php sewGet( '/greet/{name}', [GreetingController::class, 'hello'] ); 3. **Try it out** .. code-block:: console http://127.0.0.1:6916/greet/Alice Hello, Alice Multiple Parameters -------------------- You can capture more than one value. For example: .. code-block:: php $router->sewGet( '/user/{id}/post/{postId}', ['UserController', 'show'] ); Your controller method must match the placeholder order: .. code-block:: php public function show($id, $postId) { // $id = user ID // $postId = post ID return "User {$id}, Post {$postId}"; } .. container:: note **Tip:** Always ensure the number and order of method parameters matches your route’s placeholders. That way LacePHP can map each segment correctly. Optional Route Parameters ^^^^^^^^^^^^^^^^^^^^^^^^^^ Sometimes you want a URL segment to be optional—for example, you might want both: - `/profile` (show a generic profile page) - `/profile/alice` (show Alice’s profile) LacePHP now supports this with a `?` mark inside your `{}` placeholder. 1. **Define an optional parameter** by appending `?` inside the braces: .. code-block:: php $router->sewGet( '/profile/{username?}', [ProfileController::class, 'show'] ); 2. **Controller signature** Make the method argument optional, too: .. code-block:: php class ProfileController { public function show(string $username = null) { if ($username) { return "Profile of {$username}"; } return "Your generic profile page"; } } 3. **Example requests** - **Request:** ``GET /profile`` **Result:** Controller gets ``$username === null`` - **Request:** ``GET /profile/bob`` **Result:** Controller gets ``$username === 'bob'`` 4. **Why this matters** - **Cleaner routes**: You don’t need two separate definitions. - **DRY**: One route handles both “no parameter” and “with parameter.” - **User-friendly**: URLs can be shorter when the extra segment isn’t needed. .. note:: If you omit the leading slash in your route (e.g. ``'posts/{id?}'``), LacePHP will normalise it to ``'/posts/{id?}'`` for you. Always write your patterns without a trailing slash (except root `/`). HTTP Methods Cheat Sheet ^^^^^^^^^^^^^^^^^^^^^^^^ Besides **GET**, LacePHP supports **POST**, **PUT**, **PATCH**, **DELETE**, and **OPTIONS**. Here are two equivalent ways to register each: .. list-table:: :widths: 20 40 :header-rows: 1 * - **Method** - **Route Registration** * - GET - ``$router->sewGet('/users', …)`` or ``$router->get('/users', …)`` * - POST - ``$router->sewPost('/users', …)`` or ``$router->post('/users', …)`` * - PUT - ``$router->sewPut('/users/{id}', …)`` * - PATCH - ``$router->sewPatch('/users/{id}', …)`` * - DELETE - ``$router->sewDelete('/users/{id}', …)`` * - OPTIONS - ``$router->sewOptions('/users', …)`` Route Grouping -------------- When several routes share a common prefix, middleware, or namespace, you can **group** them: .. code-block:: php use Weave\Middleware\AuthMiddleware; $router->group([ 'prefix' => '/admin', 'middleware' => [AuthMiddleware::class, [[\Lacebox\Knots\RateLimitKnots::class, [10, 60]]]], 'namespace' => 'Controllers\Admin', ], function($r) { $r->get('/dashboard', ['DashboardController', 'index']); $r->post('/users', ['UserController', 'create']); }); In this example: - All paths start with `/admin` (so the final URLs are `/admin/dashboard` and `/admin/users`). - Every request in this group runs through `AuthMiddleware` first. - Controllers resolve under `Weave\Plugins\YourApp\Controllers\Admin` by default. This keeps your route definitions concise and DRY. | Further Reading --------------- Next up, dive into **Middleware** to learn how to secure, log, or transform requests before they reach your routes. See :doc:`middlewares`. | |