---
title: "フォーム"
description: "Nibiruの静的ファクトリでフォームを流暢に構築します。"
---

Nibiruのフォームは、`\Nibiru\Factory\Form`クラスの静的メソッドを呼び出すことで**流暢に**構築されます。各呼び出しでは、HTMLフラグメントが内部の静的なバッファに追加されます。最後に、`Form::addForm()`メソッドはバッファを`<form>`要素で囲み、レンダリングされたHTML文字列を返します。

## ファクトリをインポートする
```php
use Nibiru\Factory\Form;
```
これが唯一必要な `use` です。すべての入力タイプはこのクラスの静的メソッドです。

## メソッドの完全なカタログ

歴史的な命名により、3つのメソッドのバリエーションがあります。

### `addInputType*` — 実際の `<input>` 要素用
```
addInputTypeText        addInputTypePassword    addInputTypeEmail
addInputTypeDate        addInputTypeDatetime    addInputTypeColor
addInputTypeRadio       addInputTypeCheckbox    addInputTypeSwitch
addInputTypeSubmit      addInputTypeTextarea
```
### `addType*` — 非 `<input>` フォーム要素用
```
addTypeFileUpload       addTypeHidden           addTypeImageSubmit
addTypeNumber           addTypeRange            addTypeReset
addTypeSearch           addTypeTelefon          addTypeUrl
addTypeButton           addTypeLabel
```
### `addSelect` / `addSelectOption` — `<select>` と `<option>` のため
```
addSelect               addSelectOption
```
### レイアウトヘルパー
```
addOpenDiv     addCloseDiv     addOpenAny     addCloseAny
addOpenSpan    addCloseSpan
```
### ライフサイクル
```
create     // reset the static buffer; call before building a new form
addForm    // wrap the buffer in <form>...</form> and return as a string
```
:::caution[名前の不一致は意図的なもの、ある程度]
`addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — プレフィックスが要素が `<input type="…">` (Input プレフィックス) であるかどうか（Input プレフィックス）か、他のタグ（Type プレフィックス）であるかどうかに一致します。これは奇妙ですが安定しています；CLI の `./nibiru -c` スキャフォールディングは同じパターンを使用しているため、筋肉記憶が働きます。
:::

## フォームの構築
```php
use Nibiru\Factory\Form;

Form::create();   // reset the static buffer

Form::addOpenDiv(['class' => 'form-group']);
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
Form::addInputTypeText([
    'name'        => 'login',
    'id'          => 'login',
    'class'       => 'form-control',
    'required'    => 'required',
    'placeholder' => 'Type your name…',
]);
Form::addCloseDiv();

Form::addOpenDiv(['class' => 'form-group']);
Form::addTypeLabel(['for' => 'password', 'value' => 'Password']);
Form::addInputTypePassword([
    'name'     => 'password',
    'id'       => 'password',
    'class'    => 'form-control',
    'required' => 'required',
]);
Form::addCloseDiv();

Form::addInputTypeSubmit(['value' => 'Sign in', 'class' => 'btn btn-primary']);

$html = Form::addForm([
    'method' => 'POST',
    'action' => '/login',
    'name'   => 'loginForm',
]);
```
$html をビューに渡します：
```php
View::assign(['loginForm' => $html]);
``````smarty
<div class="card-body">
  {$loginForm nofilter}
</div>
```
(`nofilter` は `Form::addForm()` がすでにレンダリングされた HTML を返すためです — Smarty ではそれ以外をエスケープします。)

## レシピ

### オプション付きで選択する
```php
Form::addSelect(['name' => 'country', 'class' => 'form-control', 'id' => 'country']);
Form::addSelectOption(['value' => 'at', 'label' => 'Austria']);
Form::addSelectOption(['value' => 'lu', 'label' => 'Luxembourg']);
Form::addSelectOption(['value' => 'us', 'label' => 'United States']);
```
`addSelect` は `<select>` を開き、オプション収集状態をキューに登録します。各 `addSelectOption` はそれに追加されます。ラップする `</select>` は次の非オプション呼び出しが発生したときに自動的に生成されます。

### ラジオグループ
```php
foreach (['standard', 'admin', 'editor'] as $r) {
    Form::addInputTypeRadio([
        'name' => 'role', 'value' => $r, 'id' => "role-$r",
    ]);
    Form::addTypeLabel(['for' => "role-$r", 'value' => ucfirst($r)]);
}
```
### ファイルアップロード
```php
Form::addTypeFileUpload([
    'name'   => 'avatar',
    'accept' => 'image/png,image/jpeg',
]);
```
フォームで `enctype` を忘れないでください。
```php
Form::addForm([
    'method'  => 'POST',
    'action'  => '/profile/upload',
    'enctype' => 'multipart/form-data',
]);
```
### 隠された CSRF トークン
```php
Form::addTypeHidden([
    'name'  => 'csrf',
    'value' => bin2hex(random_bytes(16)),
]);
```
（値を `$_SESSION['csrf']` に保存し、POST 時に検証してください。）

## レイアウトヘルパー

`addOpenDiv` / `addCloseDiv` と `addOpenAny` / `addCloseAny` を使用すると、同じフレuent ストリーム内で Bootstrap スタイルのレイアウトを作成できます。
```php
Form::addOpenDiv(['class' => 'row']);
  Form::addOpenDiv(['class' => 'col-md-6']);
    Form::addInputTypeText(['name' => 'first']);
  Form::addCloseDiv();
  Form::addOpenDiv(['class' => 'col-md-6']);
    Form::addInputTypeText(['name' => 'last']);
  Form::addCloseDiv();
Form::addCloseDiv();
```
`addOpenAny([…, 'tag' => 'fieldset'])` は任意の他のタグを開きます；`addCloseAny([…, 'tag' => 'fieldset'])` はそれを閉じます。

## 内部でどのように動作するか

フォームのレンダリングは**文字列ベース**です。DOMベースではありません。各タイプクラスは`core/c/type<X>.php`にあり、プレースホルダーを含むHTMLテンプレートが含まれています：
```php
// core/c/typetext.php
private function _setElement() {
    $this->_element = '<input type="text" name="NAME" value="VALUE" '
                    . 'placeholder="PLACEHOLDER" maxlength="MAXLENGTH" ID CLASS '
                    . 'REQUIRED DATA>' . "\n";
}
```
ファクトリーは、あなたの `$attributes` 配列を `FormAttributes::loadAttributeValues()` に渡します。このメソッドは、各プレースホルダーを対応する値で置き換えます。空の値がある場合は、プレースホルダーが削除されるため、属性が空文字列でレンダリングされることはありません。

これがなぜ **のみ知られたキーが機能するか** — `'name'`, `'value'`, `'placeholder'`, `'class'`, `'id'`, `'required'`, `'data'` が認識され、任意のキーは静かにドロップされるのか。

## 生産からのパターン

`thorax.nibiru-framework.com` 上の `formsController` は、コンストラクタで連絡先フォームを構築し、プロパティに割り当てます。
```php
namespace Nibiru;
use Nibiru\Adapter\Controller;
use Nibiru\Factory\Form;

class formsController extends Controller {
    private string $form;

    public function __construct() {
        parent::__construct();
        Form::create();
        Form::addTypeLabel(['value' => 'Full Name', 'for' => 'full-name']);
        Form::addInputTypeText([
            'name'     => 'full-name',
            'id'       => 'full-name',
            'required' => 'required',
            'class'    => 'contacts-input form-control',
        ]);
        // ...more fields...
        $this->form = Form::addForm([
            'name'   => 'newregister',
            'method' => 'post',
            'action' => '/forms/submit',
        ]);
    }

    public function pageAction() {
        View::assign(['form' => $this->form]);
    }
}
```
これにより、コントローラーのアクションが小さくなります — フォームはテンプレートにレンダリングするための一行です。

## 一般的な落とし穴

- **`Form::create()` を忘れている。** バッファは静的です。`create()` がないと、最後にあったもの（長時間動作する PHP プロセス間を含む）に連結されます。
- **Smarty が HTML をエスケープしている。** レンダリングされた文字列を出力するときには `nofilter` （または `|nofilter`）を追加します。
- **カスタム属性が静かに無視される。** 各タイプは固定のプレースホルダーキーを受け付けます。`data-*` 属性（展開されます）のために `data` キーを使用しますが、真に任意の属性はドロップされます。
- **自動的な XSS エスケープがない。** フォーム層は文字列ベースです。ユーザー入力をデフォルトの `value` としてレンダリングする場合、ファクトリに渡す前に自分でエスケープしてください。
- **`addInputType…` vs `addType…` の混同。** 不確実な場合は、`core/c/type<X>.php` ファイル名を見てください — タイプの HTML 要素が `<input type="X">` であれば `addInputType<X>` を使用し、それ以外は `addType<X>` を使用します。`addSelect` は唯一の例外です（プレフィックスなしで `addSelect`）。

## フォームバリデーション

Nibiru はサーバーサイドバリデーションを搭載していません。一般的なパターン：

- `Respect/Validation` による宣言的なチェック（既に多くのプロダクションNibiruアプリで使用されています）。
- フォームタイプごとの `validate()` プラグインを含むモジュール。
- HTML5の `required`、`pattern`、`min`/`max` が第一線の防御です。
