Reputation: 1670
I have two models with a many-to-many relationship: Project
and User
.
When creating or editing a User
the below multi select field partial is included in the view.
It loops through the projects and checks if there is data in old(). If there is data in the old() it compares the project $id
to the array in old() and adds the selected attribute if it exists. If there is no data in old it plucks the existing $user's project ids into an array - applying the selected attribute if it exists.
<select class="form-control" id="projects" name="projects[]" multiple>
@foreach($projects as $id => $name)
<option value="{{ $id }}"
@if(old("projects"))
@if(in_array($id, old("projects")))
selected="selected"
@endif
@else
@if(in_array($id, $user->projects->pluck('id')->toArray()))
selected="selected"
@endif
@endif > {{ $name }}</option>
@endforeach
</select>
So this works - but I am wondering if there is a better way to do it. The nested loops feel messy.
Is there a cleaner and more efficient way to accomplish this using blade?
Upvotes: 1
Views: 261
Reputation: 4246
You could use a combination of code factoring using one-liners, Laravel collection, and Blade directive @php
to add an extra separation layer between the logic and the display:
<select class="form-control" id="projects" name="projects[]" multiple>
@foreach($projects as $id => $name)
@php
$selected = collect(old('projects'))->contains($id) || $user->projects->pluck('id')->contains($id) ? 'selected="selected"' : '';
@endphp
<option value="{{ $id }}" {{ $selected }}> {{ $name }}</option>
@endforeach
</select>
Blade directive @php @endphp
let you work as you were at home.
Laravel collect
can also work with null
values, so if your old('projects')
is null, the collection will be an empty array and it will not pass the ->contains($id)
statement. This saves you another check.
Hope it inspires you.
Upvotes: 3