concrete5.7 外部フォーム（最少）を作ってみる。

# はじめに

標準のフォームジェネレータで対応できない場合に、外部フォームブロックで独自のフォームを作成できるようです。
作り方次第では、フォーム以外の機能も作れそうです。


# 注意
外部フォームはあくまで、コントローラーとビューを１枚ずつサクッとアップしたいという程度の機能という認識でいてください。

# 更新履歴
2016.02.03 更新後、完了画面にリダイレクトするようにした。これにより、完了画面でブラウザの再読み込みで何度も更新処理が実行されなくなる。

# 外部フォームの設置場所

/application/blocks/external_form/form/ の中にファイルを設置します。

# 外部フォームに必要なファイル

今回は、tform1 という名前の外部フォームを作ります。

作成に必要なのは、以下の2種類、View と Controller の二種類、Model は無いようです。

/application/blocks/external_form/form/tform1.php (View)
/application/blocks/external_form/form/controller/tform1.php (Controller)

# 仕様

お名前、メールアドレス、メッセージを入力します。それぞれ必須チェックにします。
入力を完了しても、何も処理は行われません。あくまで動きを確認するだけです。
本当なら、メール送信処理とか書くと思いますが、コードがややこしくなるので、最低限書いてみます。

Controller から View には、以下の変数を渡します。
\$input / 入力値を配列で渡します。
\$error / 検証結果を配列で渡します。
\$isvalid / 入力値の検証が成功(true)か、失敗(false)を渡します。
\$section / 表示するセクションを指定します。セクションは、編集(edit) / 確認(confirm) / 完了(complete) に分かれます。

フォームの submit を、jQuery で指定しているのは、確認画面で「戻る」「送信」ボタンにより実行するアクションを切り替えるためです。

## ■ 入力画面
![01.jpg](https://qiita-image-store.s3.amazonaws.com/0/1154/f37170b6-351c-7a86-85c5-77c665f66a02.jpeg)

## ■ 入力画面（エラー）
![02.jpg](https://qiita-image-store.s3.amazonaws.com/0/1154/b224e062-a035-9cb0-b463-1d42efefa917.jpeg)

## ■ 確認画面
![03.jpg](https://qiita-image-store.s3.amazonaws.com/0/1154/84d51f2a-3002-b034-05b0-1b196fee7049.jpeg)

## ■ 完了画面
![04.jpg](https://qiita-image-store.s3.amazonaws.com/0/1154/0798b334-8d0b-f19a-47d1-6a7c4e62f197.jpeg)

# いくつかのコードの読み方

Controller から View に値を渡す方法。以下のように値を渡すと、View 内で $IsValid として参照できます。

```
$this->set('isvalid', true);
```

Controller 内で、$_POST の中身 p_name を取得する。

```
$this->post('p_name');
```

View 内でアクションを指定した URL を発行する。

```
$view->action('confirm')
```

View 内で変数を、文字列をサニタイズする。

```
h($text);
```

# コード

concrete5.7 用に、手探りで書いているので、参考程度に・・・。
また、Elemental テーマを利用しているので、CSS などは Bootstrap の書き方になっています。 ボタンの色とかですね。

## view 部分

あとで作成する、controller で $section を設定しています。$section は、controller の実行状態によって、出力する html を変えるために利用しています。
また、`echo($view->action('アクション名'));` と出力することで、controller に記述されたアクションへのURLが出力されます。

```/application/blocks/external_form/form/tform1.php
<?php
    // おまじない
    defined('C5_EXECUTE') or die(_("Access Denied."));
    // フォームヘルパー読み込み
    $form = Core::make('helper/form');
?>

<script type="text/javascript">
    $(document).ready(function(){
        $(document).on('click', '#OnClickConfirm', function () {
            $('#form_edit').attr('action', '<?php echo($view->action('confirm')); ?>');
            $('#form_edit').submit();
        });
        $(document).on('click', '#OnClickUpdate', function () {
            $('#form_confirm').attr('action', '<?php echo($view->action('update')); ?>');
            $('#form_confirm').submit();
        });
        $(document).on('click', '#OnClickBack', function () {
            $('#form_confirm').attr('action', '<?php echo($view->action('back')); ?>');
            $('#form_confirm').submit();
        });
    });
</script>

<?php
    if ($section == 'edit') {
?>
        <div>
            お問い合わせは、以下の内容フォームから入力してください。
        </div>
        <br>
        <form method="post" id="form_edit" action="#">
            <div class="form-group">
                <label class="control-label"><?php echo t('お名前')?></label>
                <?php echo $form->text('p_name')?>
                <?php echo(isset($error['p_name']) ? '<font color="#ff0000">'.$error['p_name'].'</font>' : ''); ?>
            </div>

            <div class="form-group">
                <label class="control-label"><?php echo t('メールアドレス')?></label>
                <?php echo $form->text('p_email')?>
                <?php echo(isset($error['p_email']) ? '<font color="#ff0000">'.$error['p_email'].'</font>' : ''); ?>
            </div>

            <div class="form-group">
                <label class="control-label"><?php echo t('メッセージ')?></label>
                <?php echo $form->textarea('p_message')?>
                <?php echo(isset($error['p_message']) ? '<font color="#ff0000">'.$error['p_message'].'</font>' : ''); ?>
            </div>

            <div class="form-group">
                <a href="#" id="OnClickConfirm" class="btn btn-primary">確認</a>
            </div>
        </form>

<?php
    }
?>

<?php
    if ($section == 'confirm') {
?>

    <div>
        以下の内容を送信してもよろしいですか？
    </div>

    <br>

    <table class="table table-stripe">
        <tr>
            <th>お名前</th>
            <td><?php echo(h($input['p_name'])); ?></td>
        </tr>
        <tr>
            <th>メールアドレス</th>
            <td><?php echo(h($input['p_email'])); ?></td>
        </tr>
        <tr>
            <th>メッセージ</th>
            <td><?php echo(nl2br(h($input['p_message']))); ?></td>
        </tr>
    </table>

    <form method="post" id="form_confirm" action="#">
        <div class="form-group">
            <?php echo $form->hidden('p_name')?>
            <?php echo $form->hidden('p_email')?>
            <?php echo $form->hidden('p_message')?>
            <a href="#" id="OnClickUpdate" class="btn btn-primary">送信</a>
            <a href="#" id="OnClickBack" class="btn btn-danger">戻る</a>
        </div>

    </form>

<?php
    }
?>

<?php
    if ($section == 'complete') {
?>

    <div>
        お問い合わせ、ありがとうございました。
    </div>

<?php
    }
?>
```

controller 部に、`public action_(アクション名)` として書くことで、action を定義します。action は、view で記載した URL から呼び出すことなどが可能です。

```/application/blocks/external_form/form/controller/tform1.php
<?php
namespace Application\Block\ExternalForm\Form\Controller;
use Concrete\Core\Controller\AbstractController;
use Core;
use Page;

class TForm1 extends AbstractController {

        // action を指定しなかった場合、view が実行されると思われる。初期表示。
        public function view() {

            // 最初にフォームを開いた時の処理です。初期値を view に渡して、編集画面の表示を指定します。

            $input['p_name'] = '';
            $input['p_email'] = '';
            $input['p_message'] = '';

            $this->set('isvalid', true);

            $this->set('input', array());
            $this->set('error', array());

            $this->set('section', 'edit');

        }

	// 入力値の検証処理
        private function validate() {

            // バリデーション結果を成功に設定
            $isvalid = true;

            // 入力値を取得
            $input['p_name'] = trim($this->post('p_name'));
            $input['p_email'] = trim($this->post('p_email'));
            $input['p_message'] = trim($this->post('p_message'));

            // 検証実行
            if ($input['p_name'] == '') {
                $isvalid = false;
                $error['p_name'] = 'お名前は、必ず入力してください。';
            }

            if ($input['p_email'] == '') {
                $isvalid = false;
                $error['p_email'] = 'メールアドレスは、必ず入力してください。';
            }

            if ($input['p_message'] == '') {
                $isvalid = false;
                $error['p_message'] = 'メッセージは、必ず入力してください。';
            }

            // View に値を渡す
            $this->set('isvalid', $isvald);

            $this->set('input', $input);
            $this->set('error', $error);

            return $isvalid;

        }

        public function action_confirm() {
            // 検証結果によって、確認(confirm) を、編集(edit) セクションを出し分け。
            if ($this->validate()) {
                $section = 'confirm';
            } else {
                $section = 'edit';
            }
            $this->set('section', $section);
        }

        public function action_update() {
            // 検証結果によって、完了(complete) を、編集(edit) セクションを出し分け。
            if ($this->validate()) {
		// *****
		// ***** ここにメール送信や更新処理を書く。 *****
		// *****
		$c = Page::getCurrentPage();
		header('location: '.Core::make('helper/navigation')->getLinkToCollection($c, true).'/complete');
		exit;
            } else {
	        $this->set('section', 'edit');
            }
        }

        public function action_complete() {
            $this->set('section','complete');
	}

        public function action_back() {
            // 戻るボタンで、編集(edit)セクションを表示します。
            $section = 'edit';
            $this->set('section', $section);
        }

    }
```

# 参考

http://legacy-documentation.concrete5.org/developers/forms/standard-widgets (5.6 系の資料)