Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 128 additions & 4 deletions document/controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Controllers receive HTTP requests, process data using Models, and pass data to V
## Basic Concepts

- Controllers define processing for each HTTP method (GET, POST, PUT, DELETE)
- Action-based routing allows defining multiple actions (index, show, new, edit, etc.) in a single Controller
- The `resources` function provides concise RESTful route definitions
- There are `<web-controller>` for web applications and `<rest-controller>` for REST APIs
- Routing tables bind URL paths to Controllers
- URL parameters are automatically extracted and accessible from the Controller
Expand Down Expand Up @@ -173,6 +175,126 @@ Define routes in `config/routes.lisp` or similar.
))
```

### Action-Based Routing

By specifying `:action` and `:method` in a route, you can define multiple action methods in a single Controller.

```common-lisp
(setf clails/environment:*routing-tables*
'((:path "/todos"
:controller "your-app/controllers/todo-controller::<todo-controller>"
:action "index"
:method :get)
(:path "/todos/:id"
:controller "your-app/controllers/todo-controller::<todo-controller>"
:action "show"
:method :get)
(:path "/todos/new"
:controller "your-app/controllers/todo-controller::<todo-controller>"
:action "new"
:method :get)))
```

In the corresponding Controller, define action-named methods instead of `do-get`.

```common-lisp
(defclass <todo-controller> (<web-controller>)
())

(defmethod index ((controller <todo-controller>))
(set-view controller "todos/index.html"))

(defmethod show ((controller <todo-controller>))
(let ((id (param controller "id")))
(set-view controller "todos/show.html" `(:id ,id))))

(defmethod new ((controller <todo-controller>))
(set-view controller "todos/new.html"))
```

**Route matching priority:**

1. Routes where both path and HTTP method match (highest priority)
2. Routes where only the path matches and `:method` is not specified
3. No match → 404

**Backward compatibility:** Routes without `:action` continue to dispatch to `do-get`, `do-post`, etc. as before.

### RESTful Routing with the `resources` Function

The `resources` function lets you define 7 standard RESTful routes in a single line.

```common-lisp
(setf clails/environment:*routing-tables*
`(,@(resources "todos" "your-app/controllers/todo-controller::<todo-controller>")
,@(resources "blogs" "your-app/controllers/blog-controller::<blog-controller>")))
```

`(resources "todos" "controller")` generates the following 7 routes:

| HTTP Method | Path | Action | Purpose |
|---|---|---|---|
| GET | /todos | index | List |
| GET | /todos/new | new | New form |
| POST | /todos | create | Create |
| GET | /todos/:id | show | Detail |
| GET | /todos/:id/edit | edit | Edit form |
| PUT | /todos/:id | update | Update |
| DELETE | /todos/:id | destroy | Delete |

`/todos/new` is placed before `/todos/:id`, so "new" will never be matched as an `:id` parameter.

#### `:only` Option — Generate Only Specific Actions

```common-lisp
;; Generate only index and show
(resources "todos" "controller" :only '(:index :show))
```

#### `:except` Option — Exclude Specific Actions

```common-lisp
;; Exclude destroy and update
(resources "todos" "controller" :except '(:destroy :update))
```

#### Controller Implementation Example with `resources`

```common-lisp
(defclass <todo-controller> (<web-controller>)
())

(defmethod index ((controller <todo-controller>))
(let ((todos (get-all-todos)))
(set-view controller "todos/index.html" `(:todos ,todos))))

(defmethod show ((controller <todo-controller>))
(let ((todo (find-todo (param controller "id"))))
(set-view controller "todos/show.html" `(:todo ,todo))))

(defmethod new ((controller <todo-controller>))
(set-view controller "todos/new.html"))

(defmethod create ((controller <todo-controller>))
(let ((title (param controller "title")))
(create-todo title)
(set-redirect controller "/todos")))

(defmethod edit ((controller <todo-controller>))
(let ((todo (find-todo (param controller "id"))))
(set-view controller "todos/edit.html" `(:todo ,todo))))

(defmethod update ((controller <todo-controller>))
(let ((id (param controller "id"))
(title (param controller "title")))
(update-todo id title)
(set-redirect controller (format nil "/todos/~A" id))))

(defmethod destroy ((controller <todo-controller>))
(destroy-todo (param controller "id"))
(set-redirect controller "/todos"))
```

---

## 4. Retrieving Request Parameters
Expand Down Expand Up @@ -656,9 +778,11 @@ REST APIs should follow RESTful design principles.
clails Controllers have the following features:

1. **Simple Design**: Just define methods for each HTTP method
2. **Flexible Routing**: Automatic URL parameter extraction and pattern matching
3. **View Integration**: Easy view rendering with `set-view`
4. **REST API Support**: Return JSON responses with `<rest-controller>`
5. **Transaction Support**: Transaction management in coordination with Models
2. **RESTful Routing**: Rails-like route definitions with the `resources` function (supports `:only` / `:except` options)
3. **Flexible Routing**: Automatic URL parameter extraction and pattern matching
4. **View Integration**: Easy view rendering with `set-view`
5. **REST API Support**: Return JSON responses with `<rest-controller>`
6. **Transaction Support**: Transaction management in coordination with Models
7. **Backward Compatibility**: Traditional `do-get` / `do-post` style and new action-based style can coexist

For detailed API reference, please refer to the docstring of each function.
140 changes: 133 additions & 7 deletions document/controller_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Controller は HTTP リクエストを受け取り、Model を使ってデータ
## 基本概念

- Controller は HTTP メソッド(GET、POST、PUT、DELETE)ごとに処理を定義します
- アクションベースのルーティングにより、1つの Controller で複数のアクション(index, show, new, edit など)を定義できます
- `resources` 関数を使って、RESTful なルーティングを簡潔に定義できます
- Web アプリケーション用の `<web-controller>` と REST API 用の `<rest-controller>` があります
- ルーティングテーブルで URL パスと Controller を紐付けます
- URL パラメータは自動的に抽出され、Controller からアクセスできます
Expand Down Expand Up @@ -173,6 +175,126 @@ URL パス内の `:parameter-name` は自動的に抽出され、`param` 関数
))
```

### アクションベースのルーティング

ルートに `:action` と `:method` を指定することで、1つの Controller に複数のアクションメソッドを定義できます。

```common-lisp
(setf clails/environment:*routing-tables*
'((:path "/todos"
:controller "your-app/controllers/todo-controller::<todo-controller>"
:action "index"
:method :get)
(:path "/todos/:id"
:controller "your-app/controllers/todo-controller::<todo-controller>"
:action "show"
:method :get)
(:path "/todos/new"
:controller "your-app/controllers/todo-controller::<todo-controller>"
:action "new"
:method :get)))
```

対応する Controller では、`do-get` の代わりにアクション名のメソッドを定義します。

```common-lisp
(defclass <todo-controller> (<web-controller>)
())

(defmethod index ((controller <todo-controller>))
(set-view controller "todos/index.html"))

(defmethod show ((controller <todo-controller>))
(let ((id (param controller "id")))
(set-view controller "todos/show.html" `(:id ,id))))

(defmethod new ((controller <todo-controller>))
(set-view controller "todos/new.html"))
```

**ルートマッチングの優先順位:**

1. パスとHTTPメソッドが両方一致するルート(最優先)
2. パスのみ一致し、`:method` が指定されていないルート
3. マッチなし → 404

**後方互換性:** `:action` が指定されていないルートは、従来通り `do-get`、`do-post` 等にディスパッチされます。

### `resources` 関数による RESTful ルーティング

`resources` 関数を使うと、RESTful な7つの標準ルートを1行で定義できます。

```common-lisp
(setf clails/environment:*routing-tables*
`(,@(resources "todos" "your-app/controllers/todo-controller::<todo-controller>")
,@(resources "blogs" "your-app/controllers/blog-controller::<blog-controller>")))
```

`(resources "todos" "controller")` は以下の7ルートを生成します。

| HTTP メソッド | パス | アクション | 用途 |
|---|---|---|---|
| GET | /todos | index | 一覧表示 |
| GET | /todos/new | new | 作成フォーム表示 |
| POST | /todos | create | 作成実行 |
| GET | /todos/:id | show | 詳細表示 |
| GET | /todos/:id/edit | edit | 編集フォーム表示 |
| PUT | /todos/:id | update | 更新実行 |
| DELETE | /todos/:id | destroy | 削除実行 |

`/todos/new` は `/todos/:id` より前に配置されるため、"new" が `:id` パラメータとしてマッチすることはありません。

#### `:only` オプション — 特定のアクションのみ生成

```common-lisp
;; index と show のみ生成
(resources "todos" "controller" :only '(:index :show))
```

#### `:except` オプション — 特定のアクションを除外

```common-lisp
;; destroy と update を除外
(resources "todos" "controller" :except '(:destroy :update))
```

#### `resources` を使った Controller の実装例

```common-lisp
(defclass <todo-controller> (<web-controller>)
())

(defmethod index ((controller <todo-controller>))
(let ((todos (get-all-todos)))
(set-view controller "todos/index.html" `(:todos ,todos))))

(defmethod show ((controller <todo-controller>))
(let ((todo (find-todo (param controller "id"))))
(set-view controller "todos/show.html" `(:todo ,todo))))

(defmethod new ((controller <todo-controller>))
(set-view controller "todos/new.html"))

(defmethod create ((controller <todo-controller>))
(let ((title (param controller "title")))
(create-todo title)
(set-redirect controller "/todos")))

(defmethod edit ((controller <todo-controller>))
(let ((todo (find-todo (param controller "id"))))
(set-view controller "todos/edit.html" `(:todo ,todo))))

(defmethod update ((controller <todo-controller>))
(let ((id (param controller "id"))
(title (param controller "title")))
(update-todo id title)
(set-redirect controller (format nil "/todos/~A" id))))

(defmethod destroy ((controller <todo-controller>))
(destroy-todo (param controller "id"))
(set-redirect controller "/todos"))
```

---

## 4. リクエストパラメータの取得
Expand Down Expand Up @@ -367,11 +489,12 @@ View パスから自動的にパッケージ名が解決されます。

### リクエスト処理の流れ

1. **ルーティング**: URL パスから対応する Controller を検索
1. **ルーティング**: URL パスと HTTP メソッドから対応する Controller を検索
2. **インスタンス化**: Controller のインスタンスを作成
3. **パラメータ設定**: URL パラメータ、クエリパラメータ、POST データを設定
4. **メソッド呼び出し**: HTTP メソッドに応じて `do-get`、`do-post` などを呼び出し
5. **レスポンス生成**: View のレンダリング、またはレスポンスデータの返却
4. **アクション設定**: ルートに `:action` が指定されていれば、Controller の `action` スロットにセット
5. **メソッド呼び出し**: アクションが指定されていればアクションメソッドを、そうでなければ `do-get`、`do-post` などを呼び出し
6. **レスポンス生成**: View のレンダリング、またはレスポンスデータの返却

### Controller のスロット

Expand All @@ -384,6 +507,7 @@ Controller インスタンスには以下のスロットがあります。
- `code` - HTTP ステータスコード(デフォルト: 200)
- `header` - HTTP レスポンスヘッダー(plist)
- `params` - リクエストパラメータ(ハッシュテーブル)
- `action` - アクション名(例: "index"、"show")。ルートに `:action` が指定された場合にセットされる

#### `<web-controller>` の追加スロット

Expand Down Expand Up @@ -662,9 +786,11 @@ REST API は RESTful な設計原則に従います。
clails の Controller は以下の特徴を持ちます。

1. **シンプルな設計**: HTTP メソッドごとにメソッドを定義するだけ
2. **柔軟なルーティング**: URL パラメータの自動抽出とパターンマッチング
3. **View の統合**: `set-view` による簡単な View レンダリング
4. **REST API サポート**: `<rest-controller>` による JSON レスポンスの返却
5. **トランザクション対応**: Model と連携したトランザクション管理
2. **RESTful ルーティング**: `resources` 関数による Rails ライクなルート定義(`:only` / `:except` オプション対応)
3. **柔軟なルーティング**: URL パラメータの自動抽出とパターンマッチング
4. **View の統合**: `set-view` による簡単な View レンダリング
5. **REST API サポート**: `<rest-controller>` による JSON レスポンスの返却
6. **トランザクション対応**: Model と連携したトランザクション管理
7. **後方互換性**: 従来の `do-get` / `do-post` 方式と新しいアクション方式が共存可能

詳細な API リファレンスについては、各関数の docstring を参照してください。
Loading
Loading