In a previous lesson, we looked at checkboxes, and now it's time to check the Select dropdowns. They are very similar to checkboxes:
Here are our conditions:
And, of course, if the validation fails - we need to pre-select the selected players.
Our validation rules are pretty straightforward:
app/Http/Requests/StoreGameWinnerRequest.php
// ... public function rules(): array{ $gamePlayersCount = $this->route('game')->users->count(); return [ 'players' => ['required', 'array', 'min:' . $gamePlayersCount], 'players.*' => ['required', 'integer', 'distinct', 'min:1', 'max:' . $gamePlayersCount], ];} // ...
Here's what we have:
players
must be present as an array and have a minimum count of our players count of the game.players.*
must be selected as an integer and, must be distinct (no duplicates), must be between 1 and the count of our players.As with checkboxes, we need to have required
on the players
field. Otherwise, the players.*
rule will not be checked.
As a bonus, here we will specify cleaner error messages, as the default ones are not that clear for the end user:
To fix this, we'll use the messages
method:
app/Http/Requests/StoreGameWinnerRequest.php
// ... public function messages(){ return [ 'players.*.required' => 'Please select a winner for each place.', 'players.*.distinct' => 'Please select a different winner for each place.', ];}
This allows us to specify custom messages for each rule. In our case, we care about required and distinct rules. The *
wildcard will apply the message to all items in the players
array, so we don't have to specify each.
Submitting a form with no winners selected will now show a friendly error message:
And in case of duplicate values, we'll see this:
Next is the actual form. We'll use the same approach as with checkboxes, but this time we'll use a select dropdown:
resources/views/games/winners.blade.php
{{-- ... --}} <form method="POST" action="{{ route('game.winners.update', $game->id) }}"> @csrf @foreach($game->users as $user) <div class="mb-4 grid grid-cols-2 gap-4"> <div class="w-1/2">{{ $user->name }}</div> <div class="w-1/2"> <select name="players[{{$user->id}}]" id="players[{{$user->id}}]" class="border-2 border-gray-300 p-2 w-full"> <option value="">Select Place</option> @foreach($places as $place => $name) <option value="{{ $place }}" @selected(old('players.' . $user->id, $user->pivot->place) == $place)>{{ $name }}</option> @endforeach </select> @error('players.'.$user->id) <div class="text-red-500 mt-2 text-sm"> {{ $message }} </div> @enderror </div> </div> @endforeach <div class="mb-4"> <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded font-medium">Update Player Places </button> </div></form> {{-- ... --}}
And here are the important things to see:
players[{{$user->id}}]
as the name of the select. This will create an array of players, where the key is the user ID and the value is the place.@selected()
Laravel helper to check if the current place is selected.old()
helper to pre-select the selected values before the form was submitted.old('players.' . $user->id, $user->pivot->place)
.Once this form is submitted, we should see that the validation worked and our data is correct:
If you want to play around with this example, you can find the code on GitHub at GameWinnerController, StoreGameWinnerRequest and resources/views/games/winners.blade.php
.