Populating a Laravel Form Multiple Select Box with hasMany Relation Values

  • August 25, 2015

Welcome to the incredibly popular Easy Laravel 5 companion blog. To celebrate the new edition's release (updated for Laravel 5.5!) use the discount code easteregg to receive 20% off the book or book/video package! » Buy the book

Laravel's model relation capabilities are undoubtedly one of the framework's most powerful and convenient features. While creating and managing basic model relationships (belongsTo, hasMany) is pretty straightforward, there are certain nuances associated with more advanced capabilities that can be more difficult to unravel. One such nuance is how one goes about populating a multiple select box with the values found in one side of a many-to-many relationship. Read on to learn how this is accomplished.

Suppose you defined a many-to-many relationship between a Post (as in blog post) and Category model. This means each Post record could be associated with multiple categories, and each Category record could be associated with multiple posts. This means each Post could belong to one or more categories, and likewise each Category could belong to one or more categories (whether actually defining the feature as a requirement depends upon the application).

To manage this sort of relationship you'll create a join table called category_post. This table would contain a category_id field and a post_id field, both of which are identified as foreign keys pointing to the categories and posts tables, respectively. Next, you'll define the respective model relationships. For instance here is how the Post model relationship with the Category model would be defined:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{

    public function categories()
    {
        return $this->belongsToMany('App\Category');
    }

}

Next you would define the Category model's relationship with Post like so:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Category extends Model
{

    public function posts()
    {
        return $this->belongsToMany('App\Post');
    }

}

Now suppose you wanted to create a multiple select box that would allow the user to choose one or more categories when creating a new blog post. In the BlogController#create view you'll use the lists method to retrieve the category names and IDs:

$categories = Costcenter::lists('name', 'id');

The $categories variable would be passed into the Blog controller's create.blade.php view, and subsequently passed into the Form::select method like so:

<div class="form-group">
    {!! Form::label('Categories') !!}<br />
    {!! Form::select('categories[]', 
    $categories, 
    null, 
    ['class' => 'form-control', 
    'multiple' => 'multiple']) !!}
</div>

Notice how the form field name is set to categories[] rather than categories. This is because when set like so, the field will be passed along as an array, meaning multiple category IDs could be selected and passed.

In your BlogController#store action you'll save the new post record, and then use the sync() method to save the $post ID and category IDs to the category_post table:

    $post->save();

    $post->categories()->sync($request->get('categories'));

The user may wish to later update the associated categories. To do so you'll pass the set of available categories into the Blog controller's edit.blade.php view, but this time you'll set the Form::select's default value to $post->categories->lists('id')->toArray():

<div class="form-group">
    {!! Form::label('Categories') !!}<br />
    {!! Form::select('categories[]', $cost_centers, 
        $post->categories->lists('id')->toArray(), 
        ['class' => 'form-control', 
        'multiple' => 'multiple']) !!}
</div>

Once done, not only will the Form::select field contain a list of all available categories, but additionally the categories that had been previously associated with the post will be pre-selected!