LoginSignup
16
10

More than 3 years have passed since last update.

ユーザが選択した値を保持するoptionタグ作成方法 - foreach / old() / Null合体演算子 -【Laravel】

Last updated at Posted at 2021-04-05

まえがき

・先日、検索機能実装のお仕事を頂いたのですが、その中で結構時間がかかった部分を紹介したいと思います。
「セレクトボックス(optionタグ)で選択した値を、ページ遷移先でも選択状態にする」

「え?なにが難しいの?」
そう思う方もいらっしゃるでしょう…

「以下のようなコードで実現できますよ!!」

なんてうたってるサイト上に数あるコード、片っ端から全て試しましたが出来ませんでした。
問題となったコード3つと解決したコード1つの計4つのコードで発生した問題と解決方法を解説していきたいと思います。

目次

  • 環境
  • 要件定義
  • 発生した問題点
  • 問題となったコード(時系列順)
  • 解決したコード
  • 解説
  • 参考文献

環境

  • PHP 7.4.15
  • Laravel 6.20.16

要件定義

以下の要件定義を実現する上で詰まりました

  • configディレクトリに定数用のディレクトリ・ファイルを作成、そこにセレクトボックス用のデータ配列を定義
  • 検索結果を表示した時に、検索条件を保持し、選択されたセレクトボックスの文字列を保持
  • ページネーションで他ページに遷移した場合も検索条件、検索結果を保持
    • その他
      • 未選択時は null
        • Undefined variable エラーを回避

発生した問題点

1. ページ遷移時に選択した値が保持されず空欄になる
2. 空欄ではないが選択した値とは異なる値が表示される
3. 表示に成功したがユーザが選択した値を if 文の条件に使用しているため、値の未選択時に Undefined variable エラー

問題となったコード(時系列順)

1. ページ遷移時に選択した値が保持されず空欄になる

blade.php
<select name="term">
    @foreach (config('const.term') as $termNumber)
        <option value="{{ $termNumber }}" @if(old('term') == $termNumber) selected @endif>{{ $termNumber }}</option>
    @endforeach
</select>

2. 空欄ではないが選択した値とは異なる値が表示される

blade.php
<select name="term">
    @foreach (config('const.term') as $index => $termNumber)
        <option value="{{ $termNumber }}" @if(old('term', $index) == $termNumber) selected @endif>{{ $termNumber }}</option>
    @endforeach
</select>

3. 表示に成功したが、ユーザが選択した値を if 文の条件に使用しているために未選択時に Undefined variable エラー

blade.php
<select name="term" id="term" class="mr-2">
    @foreach (config('const.term') as $termNumber)
        <option value="{{ $termNumber }}" @if(old('term', $inputTerm) == $termNumber) selected @endif>{{ $termNumber }}</option>
    @endforeach
</select>

@foreach (config('const.term') as $index)で使用している
config('const.term')は下記のコードです。

config/const.php
<?php
    return [
        'term' => [
            '' => '', 
            '1' => '1', 
            '2' => '2', 
            '3' => '3', 
            '4' => '4', 
            '5' => '5', 
            '6' => '6', 
            '7' => '7', 
            '8' => '8', 
            '9' => '9', 
            '10' => '10', 
            '11' => '11', 
            '12' => '12', 
            '13' => '13', 
            '14' => '14', 
            '15' => '15', 
            '16' => '16', 
            '17' => '17', 
            '18' => '18', 
            '19' => '19', 
            '20' => '20'
        ],
    ];

@if(old('term', $inputTerm)$inputTermはセレクトボックスで選択したtermの値を request オブジェクト内から取得して変数に代入したものです。以下の controller 内のメソッドにて定義し blade に渡しています。

controller
    /**
     *  検索結果画面表示
     * 
     * @param  App\Article  $article
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function search(Article $article, Request $request)
    {
        $inputTerm = $request->term;
        $inputCategory = $request->category;
        $inputWord = $request->word;
        中略
        $articles = $article->getBySearchParameters($inputTerm, $inputCategory, $inputWord);
        return view('articles.index', compact('inputTerm', 'inputCategory', 'inputWord', 'articles'));
    }

解決したコード

blade.php
<select name="term" id="term" class="mr-2">
    @foreach (config('const.term') as $termNumber)
        <option value="{{ $termNumber }}" @if(old('term', $inputTerm ?? '') == $termNumber) selected @endif>{{ $termNumber }}</option>
    @endforeach
</select>

コードの変遷と変更箇所(2箇所)は以下

blade.php

1. ページ遷移時に選択した値が保持されず空欄になる
@foreach (config('const.term') as $termNumber)
2. 空欄ではないが選択した値とは異なる値が表示される
@foreach (config('const.term') as $index => $termNumber)
3. 表示に成功したがユーザが選択した値を if 文の条件に使用しているため未選択時に Undefined variable エラー
4. 解決したコード
@foreach (config('const.term') as $termNumber)


1. ページ遷移時に選択した値が保持されず空欄になる
<option value="{{ $termNumber }}" @if(old('term') == $termNumber) selected @endif>{{ $termNumber }}</option>
2. 空欄ではないが選択した値とは異なる値が表示される
<option value="{{ $termNumber }}" @if(old('term', $index) == $termNumber) selected @endif>{{ $termNumber }}</option>
3. 表示に成功したがユーザが選択した値を if 文の条件に使用しているため未選択時に Undefined variable エラー
<option value="{{ $termNumber }}" @if(old('term', $inputTerm) == $termNumber) selected @endif>{{ $termNumber }}</option>
4. 解決したコード
<option value="{{ $termNumber }}" @if(old('term', $inputTerm ?? '') == $termNumber) selected @endif>{{ $termNumber }}</option>

解説

1. ページ遷移時に選択した値が保持されず空欄になる

option 毎に
「この選択肢を選んでいれば、それをselectedにする」
という if 文条件を記述していますが、結果は空欄。
繰り返し処理で出力された値とユーザが選択した値が一致することで、その値を selected に出来ると思ったのですが、上手いこと一致しませんでした。

blade.php
<select name="term">
    @foreach (config('const.term') as $termNumber)
        <option value="{{ $termNumber }}" @if(old('term') == $termNumber) selected @endif>{{ $termNumber }}</option>
    @endforeach
</select>

2. 空欄ではないが選択した値とは異なる値が表示される

Laravel 5.8 ヘルパ old()

old()
old関数はセッションにフラッシュデーターとして保存されている直前の入力値を取得します。

old関数は第2引数に記述した値を、初期値として設定できます。つまり、
name 属性に設定された値が old() に存在すれば value に入力され、nullの場合は第2引数に記述した値がされます。これでいける気がするのですが、保持される値は、何を選んでも「20」でした。正直どんな処理でこの結果になるのか未だによく分かりません…

blade.php
<select name="term">
    @foreach (config('const.term') as $index => $termNumber)
        <option value="{{ $termNumber }}" @if(old('term', $index) == $termNumber) selected @endif>{{ $termNumber }}</option>
    @endforeach
</select>

3. 表示に成功したがユーザが選択した値を if 文の条件に使用しているために未選択時に Undefined variable エラー

ユーザが選択した値を request オブジェクトから取得し old関数の第2引数に設定します。
これこそユーザが選択した値です。
ユーザが選択した値と繰り返し処理で順番に巡ってきた値とが == で true になればユーザが選択した値がselected されます!
やったー!!できたー!!と思いましたがまだ不完全です。

「ユーザが選択した値がない場合」
つまり
「ユーザが値を選択しなかった場合」

では$inputTermは 未定義で null ですらありません。Undefined variable 、いわゆる未定義エラーが発生します。
どういった場面かというと、サイト接続時の初回画面表示時です。
初めて訪問するページでは、old関数で初期値に設定した値が未定義状態になるコードです。

blade.php
<select name="term" id="term" class="mr-2">
    @foreach (config('const.term') as $termNumber)
        <option value="{{ $termNumber }}" @if(old('term', $inputTerm) == $termNumber) selected @endif>{{ $termNumber }}</option>
    @endforeach
</select>

4. 解決したコード

上記で発生した Undefined variabl エラーを解消するためには、初期値として設定した$inputTermが未定義の際、
代わりに null になるようにすれば良いのです。
これを実現したのが

「null合体演算子」

です。

$inputTermが falsy (null or 0 or undefined or false) な値の場合、??以降の値にしてくれます。
ここは三項(エルビス)演算子では対応できません。Undefined variable が発生します
また、以下のような特徴もあります。

  • A ?? Bの A の存在チェックもしてくれます。issetいらなくなるのかな?
  • A が falsy の場合のみBを参照するので、??は falsy なものだけをフィルタリングしてくれる様なイメージです。
  • ネストすることも可能。1つ目がなければ2つ目、2つ目がなければ3つ目のような記述
    • (old('term', $inputTerm ?? '$inputWord' ?? '$inputCategory' ?? '')

参考はこちら:三項演算子 Null 合体演算子

blade.php
<select name="term" id="term" class="mr-2">
    @foreach (config('const.term') as $termNumber)
        <option value="{{ $termNumber }}" @if(old('term', $inputTerm ?? '') == $termNumber) selected @endif>{{ $termNumber }}</option>
    @endforeach
</select>

これで要件定義を満たすことが出来ました!

参考文献

16
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
10