Laravel – making many-to-many relationships easy

It’s times like this that I want to kiss +Taylor Otwell (and +Shawn McCool for his form-base-model bundle).

$interests = RegistrationForm::get('interests');
$user->interests()->sync($interests);

How beautifully simple and yet it hides such wonderful functionality.

What’s going on?

I’m building a registration form where users can select multiple checkboxes to indicate their interest in multiple items.

This requires a few tables and some incredibly simple models.

The Interests table migration

class Create_Interests_Table {

	public function up()
    {
		Schema::create('interests', function($table) {
			$table->increments('id')->unsigned();
			$table->integer('category_id')->unsigned();
			$table->string('name');
			$table->string('slug')->unique();
		});
	}
}

We can ignore the category_id here as I’m using it for something else, the other columns are nice and simple though. We have our incrementing id, the name of our interest, and a slug for the interest (which is going to be used later on for SEO friendly filtering URLs).

The Interest_user table migration

class Create_Interest_User_Table {

	public function up()
    {
		Schema::create('interest_user', function($table) {
			$table->increments('id')->unsigned();
			$table->integer('user_id')->unsigned();
			$table->integer('interest_id')->unsigned();
			$table->timestamps();
		});
	}
}

This is all pretty straight forward as well. An id, and then the relational user_id and interest_id.

The user model

This is a small excerpt of my User model.

public function interests()
{
	return $this->has_many_and_belongs_to('Interest');
}

Yes, it’s that beautiful.

My form (a little excerpt)

I can build the checkboxes nice and easily with:

foreach ($interest_category['interests'] as $interest_id => $interest_name) {
	echo Form::checkbox('interests[]', $interest_id, in_array($interest_id, (array) RegistrationForm::old('interests'))) . ' ' . __('interests.' . $interest_id);
}

There is a little bit of other stuff going on before this, but ultimately I’m grabbing an array of interests from the database and feeding them out to checkboxes. I still have to arrange the label, but I’m just testing this out at the moment. I’m building a multilingual site so I’m having to do some interesting things to ensure correctly localised labels are returned.

The registration controller

So I have my users table, my interests table and my interest_user table. The user has been created with:

// Save the User
$user = new User($user_data);
$user->save();

So I now have a user object and I can so easily sync the interests to the user and Laravel takes care of the rest. It populates the relational table with the user id and each interest id.

// Save the interests
$interests = RegistrationForm::get('interests');
$user->interests()->sync($interests);

Isn’t that just fantastic! If you’d like more information on building many-to-many relationships in Laravel check out the official documentation. What’s that RegistrationForm class? Check out the form-base-model bundle on github.

Comments

  • Looks really great, need to test that myself. Thx for sharing!

  • Hey, any update for L4?

    • Hey Kaspar,

      I haven’t been using L4 yet, but I imagine the syncing of related many_to_many is well documented?

      Cheers!
      Alex

  • Stella Lie

    What if instead using ‘id’ as primary key on my User table ’email’ is used? Would you think I should remove ‘id’ column altogether?

    • Hey Stella,

      I don’t see why there’d be any advantage to removing the ID column. Never hurts to have it there and isn’t causing any overhead.

      I’m not sure why you’d use email as your primary key though – that could make a lot more hassle with other relationships and would mean that say a user has_many messages then each message would require their email stored in a user_email column instead of a shorter ID column. It will probably all still work, but Laravel tends to assume certain things based on ID rather than another column, and if you change that then you need to specify that you’re not using ID.

      I think.. (I’m no Laravel pro yet!).

      Cheers!
      Alex