Laravel - Form Input - Multiple select for a one to many relationship
Asked Answered
T

7

33

One of the requirements in an application I am building is for a form input that takes in a varying number of items for a single field. For instance, the sports that I play are ('Soccer','Tennis','Croquet').

There are a finite number of sports one can play (arguably), so these items should be selected from a "drop down" type list in the form input.

Downstream of this form will be two tables which have a one-to-many relationship. So from the above, the "user" table would have a single row, while the "user_sports" table would have three rows. These would then be linked by the id field in the user table.


I have not been able to find the sort of functionality where this can be achieved in the documentation (perhaps I am not searching for the correct thing). Below is the closest that I found, but is only for selecting a single item from a drop-down list.

http://laravel.com/docs/html#drop-down-lists


Is there a workaround out there that will enable me to get this form element up and running using the Laravel framework?

Alternatively, are there other ways that this sort of functionality can be achieved, without damaging the user experience?

Turoff answered 8/7, 2014 at 9:9 Comment(0)
E
69

I agree with user3158900, and I only differ slightly in the way I use it:

{{Form::label('sports', 'Sports')}}
{{Form::select('sports',$aSports,null,array('multiple'=>'multiple','name'=>'sports[]'))}}

However, in my experience the 3rd parameter of the select is a string only, so for repopulating data for a multi-select I have had to do something like this:

<select multiple="multiple" name="sports[]" id="sports">
@foreach($aSports as $aKey => $aSport)
    @foreach($aItem->sports as $aItemKey => $aItemSport)
        <option value="{{$aKey}}" @if($aKey == $aItemKey)selected="selected"@endif>{{$aSport}}</option>
    @endforeach
@endforeach
</select>
Ewell answered 8/7, 2014 at 13:26 Comment(0)
M
47

@SamMonk your technique is great. But you can use laravel form helper to do so. I have a customer and dogs relationship.

On your controller

$dogs = Dog::lists('name', 'id');

On customer create view you can use.

{{ Form::label('dogs', 'Dogs') }}
{{ Form::select('dogs[]', $dogs, null, ['id' => 'dogs', 'multiple' => 'multiple']) }}

Third parameter accepts a list of array a well. If you define a relationship on your model you can do this:

{{ Form::label('dogs', 'Dogs') }}
{{ Form::select('dogs[]', $dogs, $customer->dogs->lists('id'), ['id' => 'dogs', 'multiple' => 'multiple']) }}

Update For Laravel 5.1

The lists method now returns a Collection. Upgrading To 5.1.0

{!! Form::label('dogs', 'Dogs') !!}
{!! Form::select('dogs[]', $dogs, $customer->dogs->lists('id')->all(), ['id' => 'dogs', 'multiple' => 'multiple']) !!}
Massie answered 3/11, 2014 at 10:24 Comment(6)
I don't know if you can pass a Collection as the first argument. Shouldn't you be using $customer->dogs->lists('id')->toArray()?Reorientation
This answer was given before the release of Laravel 5. At that time(v. 4.2) the lists() returned array by default.Massie
I think this should be marked as the correct answer.Kleeman
You can also create an Accessor on the Dog Model which is a bit cleaner in my opinion. public function getDogListAttribute() { return $this->dogs->lists('id')->all(); } Then in the view, use the dog_list[] attribute with the array brackets in the first parameter of the select form helper (and a null third parameter).Clearance
This should be the selected answer. Much cleaner.Canea
Please note that lists has been renamed to pluck. The method signatures are the same so for laravel 5.4 it would be $customer->dogs->pluck('id')->all().Kaph
C
11

Laravel 4.2

@SamMonk gave the best alternative, I followed his example and build the final piece of code

<select class="chosen-select" multiple="multiple" name="places[]" id="places">
    @foreach($places as $place)
        <option value="{{$place->id}}" @foreach($job->places as $p) @if($place->id == $p->id)selected="selected"@endif @endforeach>{{$place->name}}</option>
    @endforeach
</select>

In my project I'm going to have many table relationships like this so I wrote an extension to keep it clean. To load it, put it in some configuration file like "app/start/global.php". I've created a file "macros.php" under "app/" directory and included it in the EOF of global.php

// app/start/global.php
require app_path().'/macros.php';

// macros.php
Form::macro("chosen", function($name, $defaults = array(), $selected = array(), $options = array()){

    // For empty Input::old($name) session, $selected is an empty string
    if(!$selected) $selected = array();

    $opts = array(
        'class' => 'chosen-select',
        'id' => $name,
        'name' => $name . '[]',
        'multiple' => true
    );
    $options = array_merge($opts, $options);
    $attributes = HTML::attributes($options);

    // need an empty array to send if all values are unselected
    $ret = '<input type="hidden" name="' . HTML::entities($name) . '[]">';
    $ret .= '<select ' . $attributes . '>';
    foreach($defaults as $def) {
        $ret .= '<option value="' . $def->id . '"';
        foreach($selected as $p) {
            // session array or passed stdClass obj
            $current = @$p->id ? $p->id: $p;
            if($def->id == $current) {
                $ret .= ' selected="selected"';
            }
        }
        $ret .= '>' . HTML::entities($def->name) . '</option>';
    }
    $ret .= '</select>';
    return $ret;
});

Usage

List without pre-selected items (create view)

{{ Form::chosen('places', $places, Input::old('places')) }}

Preselections (edit view)

{{ Form::chosen('places', $places, $job->places) }}

Complete usage

{{ Form::chosen('places', $places, $job->places, ['multiple': false, 'title': 'I\'m a selectbox', 'class': 'bootstrap_is_mainstream']) }}
Cancer answered 4/11, 2014 at 1:53 Comment(1)
How about this: @foreach($tags as $key=>$value) <option value="{{ $key }}" {{ in_array($key, array_values($feature->tags->pluck('id')->toArray())) ? 'selected' : '' }} > {{ $value }}</option> @endforeachSyphon
H
6

A multiple select is really just a select with a multiple attribute. With that in mind, it should be as easy as...

Form::select('sports[]', $sports, null, array('multiple'))

The first parameter is just the name, but post-fixing it with the [] will return it as an array when you use Input::get('sports').

The second parameter is an array of selectable options.

The third parameter is an array of options you want pre-selected.

The fourth parameter is actually setting this up as a multiple select dropdown by adding the multiple property to the actual select element..

Husking answered 8/7, 2014 at 11:56 Comment(0)
S
3

This might be a better approach than top answer if you need to compare 2 output arrays to each other but use the first array to populate the options.

This is also helpful when you have a non-numeric or offset index (key) in your array.

<select name="roles[]" multiple>
    @foreach($roles as $key => $value)
        <option value="{{$key}}" @if(in_array($value, $compare_roles))selected="selected"@endif>
            {{$value}}
        </option>
    @endforeach
</select>
Stilton answered 20/6, 2018 at 10:7 Comment(0)
M
1

My solution, it´s make with jquery-chosen and bootstrap, the id is for jquery chosen, tested and working, I had problems concatenating @foreach but now work with a double @foreach and double @if:

  <div class="form-group">
    <label for="tagLabel">Tags: </label>
    <select multiple class="chosen-tag" id="tagLabel" name="tag_id[]" required>
      @foreach($tags as $id => $name)
        @if (is_array(Request::old('tag_id')))
                <option value="{{ $id }}" 
                @foreach (Request::old('tag_id') as $idold)
                  @if($idold==$id)
                    selected
                  @endif 
                @endforeach
                style="padding:5px;">{{ $name }}</option>
        @else
          <option value="{{ $id }}" style="padding:5px;">{{ $name }}</option>
        @endif
      @endforeach
    </select>
  </div>

this is the code por jquery chosen (the blade.php code doesn´t need this code to work)

    $(".chosen-tag").chosen({
  placeholder_text_multiple: "Selecciona alguna etiqueta",
  no_results_text: "No hay resultados para la busqueda",
  search_contains: true,
  width: '500px'
});
Modica answered 12/2, 2016 at 21:59 Comment(0)
M
0

Just single if conditions

<select name="category_type[]" id="category_type" class="select2 m-b-10 select2-multiple" style="width: 100%" multiple="multiple" data-placeholder="Choose" tooltip="Select Category Type">
 @foreach ($categoryTypes as $categoryType)
  <option value="{{ $categoryType->id }}"
    **@if(in_array($categoryType->id,
     request()->get('category_type')??[]))selected="selected"
    @endif**>
     {{ ucfirst($categoryType->title) }}</option>
     @endforeach
 </select>
Marteena answered 1/5, 2020 at 12:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.