"BOKU"のITな日常

還暦越えの文系システムエンジニアの”BOKU”は新しいことが大好きです。

はじめてLaravel6.0:単一SELECT(プルダウン)とマルチSELECT項目を追加/入力画面(4)

今回は、入力画面でよく使う部品である、SELECTについて整理しときます。

f:id:arakan_no_boku:20191003220222p:plain

 

はじめに

 

今回は入力画面を作成して、日付入力を付け加えた以下の2つの記事の続きです。

arakan-pgm-ai.hatenablog.com

arakan-pgm-ai.hatenablog.com

ここにSELECTとかチェックボックスラジオボタンを追加して、よく使う入力部品サンプルみたいな感じにしようかと思います。

で。

今回はSELECTを取り上げます。

 

Formファサードはとりあえず使わずにやってみる

 

Laravelには「Formファサード」があります。

これは標準インストールには含まれません。

別途インストールすれば使えます。

それを使えば、例えば、SELECTならこんな感じで書けます。

{{Form::select('size', ['L' => 'Lサイズ', 'S' => 'Sサイズ'], 'S', ['placeholder' => 'null'])}}

便利そうには見えますが、今回は使いません。

理由は「依存するのが怖い気がするから」です。

最新では標準に含まれていないけど、以前は標準に含まれていた・・というパターンの機能に依存して、後で後悔する・・パターンは、さんざん体験してきましたからね。

とりあえず、今回も避けておこうと思います。

さて。

 

まずは画面だけを作ってみる

 

ベース(コントローラとかルートとか)は既に作成してあるので、blade.phpファイルだけ編集して、画面表示から確認します。

まずはベタ書きで。

 

単一選択のSELECT(プルダウン)

 

<div class="form-group row">
   <label for="sel01" class="col-md-4 col-form-label text-md-right">性別</label>
   <div class="col-md-6">
      <select class="form-control" id="sel01" name="sel01">
         <option value="1">男性</option>
         <option value="2" selected>女性</option>
      </select>
   </div>
</div>

表示すると、こうなります。 

f:id:arakan_no_boku:20191104131700p:plain

 

複数選択のSELECT

 

<div class="form-group row">
   <label for="sel02" class="col-md-4 col-form-label text-md-right">選択言語</label>
   <div class="col-md-6">
      <select  multiple class="form-control" id="sel02" name="sel02[]">
         <option value="ja" selected>日本語</option>
         <option value="en">英語</option>
         <option value="de">ドイツ語</option>
         <option value="zh">中国語</option>
      </select>
   </div>
</div>

表示するとこうなります。

f:id:arakan_no_boku:20191104131939p:plain

単一SELECTとことなり、注意することがひとつ。

  <select multiple class="form-control" id="sel02" name="sel02[]"> 

この部分の「name="sel02[]"」のように、multipleの場合は、nameを配列で定義しておく必要があることです。

こうしておかないと、複数選択された結果をコントローラ側で配列で受け取ることができず、一番最後に選択された値のみになってしまします。

 
これらの選択値をコントローラで受け取る

 

nameを設定しているので、Requestクラスオブジェクトで、選択値を受け取れます。

public function store(Request $request)
{

    $sel01 = $request->sel01;
    $sel02 = $request->sel02; 

}

この各変数に何がはいっているか?です。

まず、単一選択の$sel01には"1"または"2"など、選択されたoptionのvalue値です。

複数選択の$sel02は、選択されたoptionのValue値が配列ではいってきます。

なので、大抵の場合「implode」関数などを使って

$sel02ForSave = implode(',', $sel02)

のようにして「ja,de,en」のような選択されたValue値をカンマつなぎのテキストに変換してDBに保存したりします。

 

もう少し実用的なやり方にしてみる

 

ここまでは良し・・です。

でも、実際に使う時には、上記のようなベタ書きはありえません。

DBに保存したものを読みだして、それを初期値として表示するのが普通です。

なんですが。

DBにマスタテーブルを作って、サービス作って・・とかガッツリやると、そっちのソースのボリュームが増えて、今回の主目的である「SELECT」の使い方サンプルという部分がぼけてしまいます。

なので、今回はコントローラクラスで、「DBから読み込んだ体・・」でデータを変数うにせっとしたところからやってみます。

 

コントローラクラスのソースです。

 

Http\Controllers\DummyController.php

public function index()
{ 
    $sel01Mock = "2";
    $sel01Datas = [
        "1" => "男性",
        "2" => "女性"
    ];
    $sel01Selected = [
        "1" => "",
        "2" => ""
    ];
        $sel01Selected[$sel01Mock] = "selected";
        
    $sel02Mock = preg_split("/,/", "en,zh");
    print_r($sel02Mock);
    $sel02Datas = [
        "ja" => "日本語",
        "en" => "英語",
        "de" => "ドイツ語",
        "zh" => "中国語"
    ];
    $sel02Selected = [
        "ja" => "",
        "en" => "",
        "de" => "",
        "zh" => ""
    ];
    foreach ($sel02Mock as $key => $val) {
        $sel02Selected[$val] = "selected";
    }
    return view('dummy', compact(
        'sel01Datas',
        'sel01Selected',
        'sel02Datas',
        'sel02Selected'
    ));
}

単一SELECTと複数SELECTの両方共、コントローラでoptionの選択肢と、初期選択状態(selected)をセットしてます。

補足していきます。

 

まず「単一SELECT」の方から

 

こちらの「DBから取得した体」は「$sel01Mock = "2";」です。

前半の例のとおり、コントローラクラスではRequestオブジェクトから、選択された「Value値」が取得できますから、DBに保存するとしたら、その値です。

つまり、"2"の女性が選択されていたものを保存して、DBから取得した想定です。

データと同じキーで「 $sel02Selected」配列を用意して、$sel01Mockの値をキーにして「selected」をセットするわけです。

 

今度は「複数SELECT」です

 

こちらの「DBから取得した体」は「$sel02Mock = preg_split("/,/", "en,zh");」です。

前半の例で、Multipleの場合は選択されたValue値が配列でわたってくるので、DBに保存するときには「カンマつなぎ」の文字列にして保存すると書きました。

こちらの例はその想定で、enとzhが選択されてDBに保存されていたものを読み出し、カンマで分割して、配列に戻しているわけです。

なので。

foreach ($sel02Mock as $key => $val) {
    $sel02Selected[$val] = "selected";
}

 こんな感じで、$sel02Selected配列を用意して、配列の値をキーにして「selected」をセットすれば良いわけです。

 

ルートの修正

 

index()を追加したので、ルートも修正しておきます。

routes\web.php

以下のようにしていたのを

Route::get('/dummy', function () {
     return view('dummy');
});

index()を参照するように、以下に修正します。

Route::get('/dummy', 'DummyController@index')->name('dummy');

 

Bladeテンプレート

 

上記のコントローラで設定し受け渡した配列変数を処理するVIEW側です。

 

単一SELECTの場合

 

<div class="form-group row">
   <label for="sel01" class="col-md-4 col-form-label text-md-right">性別</label>
   <div class="col-md-6">
      <select class="form-control" id="sel01" name="sel01">
         @foreach($sel01Datas as $key01 => $val01)
            <option value="{{$key01}}"
               @if(empty(old())) {{ $sel01Selected[$key01] }}
               @elseif(old('sel01')==$key01) selected
               @endif
            >{{$val01}}</option>
         @endforeach
      </select>
   </div>
</div>

SELECTのプルダウンリストに出力する表示項目情報は以下のループで取り出します。

 @foreach($sel01Datas as $key01 => $val01)

ここで、$sel01Datasにはコントローラで

$sel01Datas = [ "1" => "男性", "2" => "女性" ];

のようにセットされているので、それを1セットずつ取り出して<option>タグを組み立てればよいわけです。

でも。

それだけでは初期表示時に明示的に「選択された状態」にすることはできません。

選択された状態にするポイントは、以下の@if~ @endifの部分です。 

@if(empty(old())) {{ $sel01Selected[$key01] }}
@elseif(old('sel01')==$key01) selected
@endif 

表示するケースとして

  • 初期表示時にSELECTED(選択状態)にする。
  • バリデーションエラーで戻った時に直前の選択状態を復元する 

 の2つがあります。

初期表示時は「old()」が空(empty)になりますから、それを利用して。

@if(empty(old())) {{ $sel01Selected[$key01] }}

 で、コントローラでセットした「SELECTED」文字列を適用する・しないで制御することができます。

逆に、バリデーションエラーで戻った時は「old('sel01')」で直前に選択されていたValue値が取得できますから、それと一致するものに「SELECTED」をつけることで復元することができます。

 

マルチSELECT

 

マルチSELECTの場合も理屈は同じです。

ただ、SELECTEDになる項目が1つではないので、そこだけ配慮がいります。

ソースはこんな感じです。

<div class="form-group row">
   <label for="sel02" class="col-md-4 col-form-label text-md-right">選択言語</label>
   <div class="col-md-6">
       <select  multiple class="form-control" id="sel02" name="sel02[]">
           @foreach($sel02Datas as $key02 => $val02)
              <option value="{{$key02}}"
                 @if(empty(old())) {{$sel02Selected[$key02]}}
                 @elseif(in_array($key02,old('sel02'))) selected
                 @endif
              >{{$val02}}</option>
           @endforeach
      </select>
   </div>
</div>

表示項目のセットや、初期表示字のSELECTED(選択状態)の指定はほぼ同じです。 

ただ。

@if(empty(old())) {{$sel02Selected[$key02]}}
@elseif(in_array($key02,old('sel02'))) selected
@endif

の「バリデーションエラーで戻った場合の直前の選択状態の復元」時、「 old('sel02')」で返されるのは「配列」であるという点が違います。

そのため単純に「==」などで比較はできません。

PHP関数の「in_array()」を使って、$key02の値が「 old('sel02')」で返される配列に含まれているかどうかで判断して、SELECTEDをつけています。

 

ためしに実行してみます。

 

上記のコントローラ部分とVIEW部分の記述を、以下の記事のソースに追記して、実行してみます。 

arakan-pgm-ai.hatenablog.com

arakan-pgm-ai.hatenablog.com

MariaDBMySQL)を起動し、ビルトインサーバーを動かします。

php artisan serve

それで以下のURLで動かしてみます。 

http://localhost:8000/dummy

 初期表示は

f:id:arakan_no_boku:20191106003447p:plain

2種類のSElECTで項目表示と初期選択はできています。

これを、「男性」「日本語」「ドイツ語」を選択に変更したうえで、コード・名前・カナを適当に入力して、バリデーションエラーを発生させます。

f:id:arakan_no_boku:20191106003702p:plain

バリデーションエラーの直前の状態が、SELECTでも復元されてます。

OKそうですね。

このパターンを覚えておけば、SELECTはいけそうですね。

今回はこんなところで。

ではでは。