よしかわーるど

プログラミングで世界を変える

2018-06-29

CakePHP 入門 4日目

前回の記事

CakePHP 入門 3 日目

フォームの利用

フォームの作成

「フォームの送信処理」をする。フォームを使って必要な情報を送信し、それを受け取って処理する、そのやり取りで動いています。フォームは、Web の処理を行う際の基本となるので、学習していきます。 CakePHP でフォーム送信の処理をする場合、どのような流れになるのか、どういうことはあるのかを確認していきましょう。

  1. フォームを組み込んだページを表示する。
  2. 送信先のコントローラで、送信された情報を取り出すなどの必要な処理をする。
  3. 何らかの結果を表示するような場合には、コントローラからビューへ必要な値を設定する。
  4. ビュー(テンプレート)で、3 番にとって設定された値を取り出し、必要に応じて画面に表示する。

CakePHP は、MVC により処理と表示が分けられています。コントローラとビューの間の値渡しなどが肝となるわけです。 処理の切り分けが出来るので、デザイン部分はデザイナーに任せることが出来ます。

フォーム送信ページを作る

フォームを送信して処理する簡単なサンプルを作成します。「フォームページ」と「ビューテンプレートでの結果表示ページ」を用意して、結果表示ページのコントローラで必要な処理を行います。1 つのコントローラに 2 つのアクションメソッドを記述します。

フォームのビューテンプレート

「index.ctp」を修正していきます。

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html;
      charset=utf-8">
    <title>フォームページ</title>
  </head>
  <body>
    <h1>サンプル</h1>
    <p>フォームの送信</p>
    <form action="/hello/sendForm" method="get">
      <input type="text" name="text1" />
      <input type="submit" />
    </form>
  </body>
</html>

今回は、GET でフォームを送信しています。フォームの送信先として、 action="/hello/sendForm" を指定しています。

結果表示のビューテンプレート

続いて、送信された結果を表示するためのビューテンプレートを用意します。これは、「Template」内の「Hello」フォルダの中に、「send_form.ctp」という名前で保存をします。フォームの送信先に「sendForm」というアクションを指定していますので、ファイル名を「send_form.ctp」にします。

アンダースコア記法

クラス名やメソッド名はキャメル記法(sendForm あるいは SendForm という形式)で命名されますが、クラスに関連しないファイル名は「アンダースコア記法」と呼ばれる形式で命名されます。アンダースコア記法に直すと「send_form.ctp」となります。

<h1>送信結果</h1>
<p><?=$result ?></p>

送信結果の表示に<?=$result ?>というタグを用意しています。表示する結果が$result という変数に収められているという前提テンプレートを作成します。

コントローラの修正

<?php
namespace App\Controller;

class HelloController extends AppController
{
  public function initialize(){
    //$this->name = 'Hello';
    $this->viewBuilder()->Layout('Hello');
    $this->set('msg', 'Hello/index');
    $this->set('footer', 'Hello/footer2');
  }

  public function index()
  {
  }

  public function sendForm(){
    $str = $this->request->query['text1'];
    $result = "";
    if ($str != "") {
      $result = "you type: " . $str;
    } else {
      $result = "empty.";
    }
    $this->set("result", $result);
  }
}
 ?>

送信情報を管理する$this->request->query

ここでは、sendForm メソッドで、送信されたフォームの情報を取り出し、ビューに渡す、といった処理をします。 $str = $this->request->query['text1']; この「request」は、$this に用意されているプロパティで、リクエストに関する情報を管理して、扱う機能がまとめられています。 「query」プロパティは、「_クエリーテキスト_」が連想配列としてまとめられているプロパティです。

「クエリーテキスト」というのは、Web ページのアドレスの後に「?」記号をつけて記述されているテキストのことです。<form>での送信でも、method="get"を指定して送信された場合、フォーム内のデータはすべてクエリーテキストとして action のアドレスに送信されるようになっているのです。method=“get”を指定したフォームの値は、query から取り出して利用することが出来ます。

request[][]の最初に情報の種類を示す値を指定し、次に、送られた値の名前を指定することで、必要な情報を得ることが出来ます。

$this->request の 1 次配列で用意されているキーワード

  • params
    • 送られた値をすべてまとめたもの
  • data
    • POST 送信された値
  • query
    • クエリーテキストで送られた値
  • url
    • 送信されたアドレス
  • base
    • ベースのディレクトリ名
  • webroot
    • webroot ディレクトリのアドレス
  • here
    • 現在のアドレス

ビューへの変数設定

$this->set("result",$result);

送信テキストのエスケープ処理

フォーム内容を処理できるようになりましたが、問題があります。XSS 攻撃などに無力であるということです。Chrome では、スクリプトの実行はされませんが、Edge などではスクリプトは動作します。

htmlspecialchars による脆弱性対策

PHP をやってる人間なら用いるであろう、*htmlspecialchars*で無効化にするというやり方です。 以下、サンプルコードです。

public function sendForm() {
  $str = $this->request->query['text1'];
  $result = "";
    if ($str != "") {
      $result = "you type: " . $str;
    } else {
      $result = "empty.";
    }
    $this->set("result", htmlspecialchars($result));
  }
}

ショートハンド関数「h」

htmlspecialchars という長い単語を入力すると可読性が下がります。この htmlspecialchars を呼び出すために*ショートハンド関数*が用意されています。

public function sendForm() {
  $str = $this->request->query['text1'];
  $result = "";
    if ($str != "") {
      $result = "you type: " . $str;
    } else {
      $result = "empty.";
    }
    $this->set("result", h($result));
  }
}

その他のフォーム項目

チェックボックス、ラジオボタン、選択リストを持ったフォームを作成し、送信された内容を表示させてみましょう。

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html;
      charset=utf-8">
    <title>フォームページ</title>
  </head>
  <body>
    <h1>サンプル</h1>
    <p>フォームの送信</p>
    <form method="get" action="/hello/sendForm">
      <input type="hidden" name="check1" value="off">
      <input type="hidden" name="radio1" value="off">
      <input type="checkbox" name="check1" id="c1">
        <label for="c1">チェック</label><br>
      <input type="radio" name="radio1" id="r1" value="No.1">
        <label for="r1">ラジオ1</label><br>
      <input type="radio" name="radio1" id="r2" value="No.2">
        <label for="r2">ラジオ2</label><br>
      <select name="select1">
        <option value="Windows">Windows</option>
        <option value="Linux">Linux</option>
        <option value="MacOSX">MacOSX</option>
      </select>
      <input type="submit">
    </form>
  </body>
</html>

未選択で送信すると…

ラジオボタンや、チェックボックスが OFF だと値そのものが送られていません。 解決方法は以下のとおりです。

<?php
namespace App\Controller;
class HelloController extends AppController
{
  public function initialize(){
    //$this->name = 'Hello';
    $this->viewBuilder()->Layout('Hello');
    $this->set('msg', 'Hello/index');
    $this->set('footer', 'Hello/footer2');
  }
  public function index()
  {
  }
  public function sendForm(){
    $result = "送信された情報<br>";
    foreach ($this->request->query as $key => $value) {
      $result .= $key . " => " . $value . "<br>";
    }
    $this->set("result", $result);
  }
}
 ?>

フォームの部分は少し修正するだけで大丈夫です!

POST による送信

ここまで GET を使っていました。CakePHP では、POST による送信も出来ます。POST はクエリーテキストとして値が送られませんので、別のコードを使います。

 <?php
namespace App\Controller;
class HelloController extends AppController
{
  public function initialize(){
    //$this->name = 'Hello';
    $this->viewBuilder()->Layout('Hello');
    $this->set('msg', 'Hello/index');
    $this->set('footer', 'Hello/footer2');
  }
  public function index()
  {
  }
  public function sendForm(){
    $str = $this->request->data('text1');
    $result = "";
    if ($str != "") {
      $result = "you type: " . $str;
    } else {
      $result = "empty.";
    }
    $this->set("result", h($result));
  }
}
 ?>

Form ヘルパーによるフォームの生成

Form ヘルパーを利用する

ビューテンプレートにそのままフォームのタグを書いて実行してきましたが、CakePHP には、自動生成する機能があります。これを「From ヘルパー」と呼びます。これを利用してフォームを自動生成することができます。 index.ctp を書き換えます。

index.ctp

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html;
      charset=utf-8">
    <title>フォームページ</title>
  </head>
  <body>
    <h1>サンプル</h1>
    <p>
      <?=$result; ?>
    </p>
    <?= $this->Form->create(null,
        ['type'=>'post', 'url'=>['controller'=>'Hello',
        'action'=>'index']]) ?>
        <?=$this->Form->text("HelloForm.text1") ?>
        <?=$this->Form->submit("送信") ?>
        <?=$this->Form->end(); ?>
  </body>
</html>

今はエラーが出ますが、コンロトーラの設定があれば消えます。 ようやく、Form ヘルパーを使って生成されたフォームが出来ました。

Form ヘルパーの基本メソッド

Form ヘルパーを利用したフォームの出力を見てみましょう。Form ヘルパーは、「FormHelper」というクラスとして用意されています。これは、ビューテンプレート内では$this->Formという変数のインスタンスが用意されています。Form ヘルパーを利用する場合には、この$this->Formインスタンスのメソッドを呼び出します。

メソッド類をまとめます。

  • フォームの開始タグを生成する create( モデル名, 属性); 引数には、値を保管する先となるモデル名と、タグに指定する各種の属性の指定を渡します。 ['type'=>'post', 'url'=>['controller'=>'Hello', 'action'=>'index']] type 属性に’post’, url 属性に[‘controller’=>‘Hello’, ‘action’=>‘index’]が設定されます。
  • 入力フィールドのタグを生成する text(名前, 属性指定); 1 行のテキストを入力するフィールドを生成します。引数には、name 属性で指定される名前を渡します。
  • 送信ボタンのタグを生成する submit(キャプション, 属性指定); 送信のためのボタンを生成します。第一引数には、ボタンに表示するキャプションの設定。
  • フォームの終了タグを生成する end(); フォーム終了タグを生成します。これは引数のなどのオプションは特にありません。引数としてテキストを渡すと、そのキャプションの submit を自動生成してから終了タグを作成することも出来ます。例えば、end(‘送信’)とすれば、自動的に submit ボタンを作ってくれます。

コントローラを修正する

index メソッドを修正します。

HelloController.php

<?php
namespace App\Controller;
class HelloController extends AppController
{
  public function initialize(){
    //$this->name = 'Hello';
    $this->viewBuilder()->Layout('Hello');
    $this->set('msg', 'Hello/index');
    $this->set('footer', 'Hello/footer2');
  }
  public function index(){
    $result = "";
    if ($this->request->isPost()) {
      $result = "<pre>送信された情報<br>";
      foreach ($this->request->data['HelloForm'] as $key => $value) {
        $result .= $key . ' => ' . $value;
      }
      $result .= "</pre>";
    } else {
      $result = "記述をしてください!";
    }
    $this->set("result", $result);
  }
}
 ?>

Form ヘルパーで生成されたコード

HMTL ソースコードを確認してみましょう。

<form method="post" accept-charset="utf-8" action="/hello"><div style="display:none;"><input type="hidden" name="_method" value="POST"/></div>        <input type="text" name="HelloForm[text1]" value="こんにちは!"/>        <div class="submit"><input type="submit" value="送信"/></div>

複雑に出力されているのがわかります。フォーム内には、本来用意されていないはずの<input type="hidden" name="_method" value="POST"/>が生成されています。これで _method という名前の値として送信の方式(POST)を送っています。Form ヘルパーでは、ヘルパー自身が必要とする情報などを type=“hidden”で自動的に埋め込むこともあります。 入力フィールドでは、name=“HelloForm[text1]“という形で名前が生成されています。$data 内にフォームの情報を送るようにしています。

メリット

実際に送信をしてみるとわかるのですが、送信して再度このページが表示されたとき、入力フィールドには、自分が書いたテキストが残ります。 通常は、送られてた値を保持してフォームに表示させたりする処理はプログラマが手作業で書いてやらなければいけません。Form ヘルパーを使えば送信後もフォームの値や状況が常に保持され続けます。

チェックボックスの生成

Form ヘルパーの性能を発揮していきましょう。チェックボックスの生成をしましょう。 checkbox( 名前, 属性指定); フォームのタグ(index.ctp)

<?=$this->Form->create(null, ['type'=>'post', 'url'=>['action'=>'index']]) ?>
<?=$this->Form->checkbox("HelloForm.check1", ['checked'=>'true']) ?>checkbox
<?=$this->Form->submit("送信") ?>
<?=$this->Form->end(); ?>

生成される HTML コード

<form method="post" accept-charset="utf-8" action="/hello"><div style="display:none;"><input type="hidden" name="_method" value="POST"/></div><input type="hidden" name="HelloForm[check1]" value="0"/><input type="checkbox" name="HelloForm[check1]" value="1" checked="checked">checkbox
<div class="submit"><input type="submit" value="送信"/></div></form>

この他にも

  • ラジオボタン
  • ラベル
  • 選択リスト

など様々なものを生成出来ます。

日時の入力

  • 年月日時分をまとめて生成する dateTime( 名前, オプション);
  • 年月日を生成する date( 名前, オプション);
  • 時分を生成する time( 名前, オプション);
  • 年を生成する year( 名前, オプション);
  • 月を生成する month( 名前, オプション);
  • 日を生成する day( 名前, オプション);
  • 時を生成する hour( 名前, オプション);
  • 分を生成する minute( 名前, オプション);
  • 午前午後(AM/PM)の表示を生成する meridian( 名前, オプション); オプションで用意できる項目
  • monthNames
    • 月名。12 ヶ月の月名を配列として用意できる。
  • minYear/maxYear
    • 年表示の開始年と終了年。
  • interval
    • 分の表示で一定間隔ごとに数値を表示させるためのもの。
  • empty
    • 未選択時の表示。
  • default
    • デフォルトの値。
  • timeFormat
    • 1224 時間制の設定。12 または 24 に設定することができる。
  • second
    • 秒の表示。true にすると date/dateTime で秒表示が用意される。

次回の記事

https://yoshikawataiki.net/posts/cake5/