> For the complete documentation index, see [llms.txt](https://vladimirkostikov.gitbook.io/laraboom/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://vladimirkostikov.gitbook.io/laraboom/http/08-paths.md).

# 8. Paths and Blade

A Path is the web route class of a host. It lives in `app/Routes` and extends `LaraBoom\Definition\Path`. Instead of `routes/web.php` you write methods with HTTP attributes.

## A minimal Path

```php
namespace App\Routes;

use LaraBoom\Attributes\Get;
use LaraBoom\Definition\Path;

final class Shop extends Path
{
    #[Get('/', name: 'shop.home')]
    public function home(): mixed
    {
        return view('shop.home');
    }
}
```

The template is `web/views/shop/home.blade.php`. A dot in the view name means Blade subdirectories.

## HTTP attributes

```php
#[Get($uri, only: null, name: null)]
#[Post(...)]
#[Put(...)]
#[Patch(...)]
#[Delete(...)]
```

* `uri` is the path from the site root (`/login`, `/action/ping`)
* `name` is the Laravel route name (`route('demo.home')`)
* `only` is the middleware aliases from `App::through()` (a string or an array)

URIs starting with `api` land in the API stack. The rest go into the web stack (sessions, cookies, CSRF for forms).

## The Demo Path. Route map

From `apps/app/Routes/Demo.php`:

| Method | URI              | Name                   | Middleware |
| ------ | ---------------- | ---------------------- | ---------- |
| GET    | `/`              | `demo.home`            | -          |
| GET    | `/login`         | `demo.login`           | guest      |
| POST   | `/login`         | `demo.login.submit`    | guest      |
| GET    | `/register`      | `demo.register`        | guest      |
| POST   | `/register`      | `demo.register.submit` | guest      |
| POST   | `/logout`        | `demo.logout`          | auth       |
| GET    | `/account`       | `demo.account`         | auth       |
| POST   | `/action/notify` | `demo.notify`          | auth       |
| POST   | `/action/ping`   | `demo.ping`            | -          |

## Injecting a Resource into a Path

```php
final class Demo extends Path
{
    public function __construct(
        private readonly DemoResource $demos,
    ) {}

    #[Get('/', name: 'demo.home')]
    public function home(): mixed
    {
        return view('demo.home', [
            'rows' => $this->demos->query()->latest('id')->limit(20)->get(),
            'user' => Auth::user(),
        ]);
    }
}
```

A Path does not duplicate query logic. It orchestrates the Resource, the view and auth.

## Allow on Path methods

```php
#[Get('/account', name: 'demo.account', only: 'auth')]
#[Allow('auth')]
public function account(): mixed { ... }
```

On a Path the first argument of `Allow` is often treated as a rule (`auth`, `guest` and so on) rather than as an action name. Check against the `RunsRoutes` runtime.

`only: 'auth'` gives you redirect middleware. `#[Allow('auth')]` gives an explicit Access check. Usually you put both.

Guest pages:

```php
#[Get('/login', name: 'demo.login', only: 'guest')]
#[Allow('guest')]
```

An unauthorized web visitor is redirected to `/login`. Someone already logged in who hits a guest route goes to `/account` (the behavior of the Demo host).

## Form validation

```php
#[Post('/login', name: 'demo.login.submit', only: 'guest')]
#[Allow('guest')]
#[Throttle(10, 1)]
#[Check([
    'email' => ['required', 'email'],
    'password' => ['required', 'string'],
])]
public function login(Request $request, array $data): mixed
{
    if (! Auth::attempt(['email' => $data['email'], 'password' => $data['password']])) {
        return back()->withErrors(['email' => 'Invalid credentials.'])->onlyInput('email');
    }

    $request->session()->regenerate();

    return redirect()->route('demo.account');
}
```

`#[Check]` validates before the method is entered. The injection:

* `array $data` is an array
* `Validated $data` is an object with helpers like `string()`

See [chapter 10](/laraboom/http/10-http-guards.md).

## Transactions and idempotency on a Path

```php
#[Post('/register', ...)]
#[Atomic]
#[Check([...])]
public function register(Request $request, Validated $data): mixed { ... }

#[Post('/action/ping', name: 'demo.ping')]
#[Atomic]
#[Idempotent]
#[Throttle(30, 1)]
#[Check(['message' => ['nullable', 'string', 'max:200']])]
public function ping(Validated $data): mixed { ... }
```

`#[Atomic]` wraps the handler in `DB::transaction`. `#[Idempotent]` makes a repeat with the same `Idempotency-Key` skip running the side effect twice.

## Views and share

The directory is `{host}/web/views`.

Shared variables from `App::share()` are available in all templates (`$demo_name`, `$user` and so on). The Demo layout usually pulls CSS from `web/css`.

The helper in PHP:

```php
boom('demo_name');
```

## Route registration

`AppRegistrar` reads discovery and attaches routes to `AppController`. That one calls `$path->run($route, $request)`. Then come Allow, Check, Atomic and the method itself.

The list:

```bash
php boom routes
php boom routes --json
```

## CSRF

A web POST from Blade needs `@csrf` in the form. This is the standard Laravel session stack. The `/api` API works through JSON and token scenarios without Blade CSRF.

## When a Path, when API only

| Scenario               | Choice                    |
| ---------------------- | ------------------------- |
| SPA / mobile client    | Resource + `/api`         |
| Multi page site, forms | Path + Blade              |
| Both                   | both, one shared Resource |

Next up is [Authorization](/laraboom/http/09-authorization.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://vladimirkostikov.gitbook.io/laraboom/http/08-paths.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
