Merklin's avatar

Many to Many

Hello.

I am quite new to Laravel and I'm not sure how to approach this problem.

Let's say that I have Users and Tasks models and controllers with their respective tables. One user can have many tasks and one task can have many users. The question that arises is how to store and define these relationships.

  1. Option one is to have a field in the Tasks table that holds serialized data of users' ids. But in this case, how to properly use this:

return $this->belongsToMany(User::class, 'user_id');

And then, how can I check if a user is within that serialized data to show him the task? What I mean is how to alter this:

$task = Tasks::where('user_id');

to return a view with all tasks that belong to this user?

  1. The second option is to have another table with 2 fields: user_id and task_id. I think this is better, but: 2.1. Do I need another controller and model, named for example UserTasks? I guess so 2.2. How to define relationships in this case? 2.3. Again, how to return a view with all tasks that belong to this user?

Any help and advice is much appreciated.

Link to an example code with a similar situation that I can study is also an option. :)

0 likes
8 replies
vincent15000's avatar
Level 63

The second option is the right one : you need a pivot table. You don't need any other controller or model. The relationship is defined like you did for the first option.

// User model
public function tasks()
{
		return $this->belongsToMany(Task::class);
}

To retrieve all tasks belonging to a user, you just have to use the defined relationship.

$tasks = $user->tasks;

And you can do the same for the Task model.

// Task model
public function users()
{
		return $this->belongsToMany(User::class);
}

To retrieve all users for a specific task, you just have to use the defined relationship.

$users = $task->users;

You will probably have to customize the table name.

return $this->belongsToMany(Task::class, 'user_task');
...
return $this->belongsToMany(User::class, 'user_task');
1 like
Merklin's avatar

@vincent15000

In this case, how to store the relationship (user_id and task_id) in the custom table, and how to use it in the view i.e. the equivalent of

$tasks = Task::where(???);

return view('tasks', compact('tasks'));
1 like
vincent15000's avatar

@Merklin It's very easy.

$userIds = [1, 4, 9];

$tasks = Task::
	whereHas('users', $closure = function ($query) use ($userIds) {
		query->whereIn('id', $userIds);
	})
	->with(['users' => $closure])
	->get();

whereHas() is to filter the tasks matching the condition on the users ids and with() is to eager load the users attached to the tasks to avoid N+1 query problems.

I have named put the callback function into a variable $closure just to avoid repeating twice the same callback in whereHas() and in with().

Merklin's avatar

@vincent15000

Maybe my question is not very clear.

  1. When creating a task, hot to store the user_id and the task_id in the custom table user_task

My store method now is:

public function store(Request $request): RedirectResponse
    {
        $task = Task::create(
            [
                'name'       => $request->name,
                'start_date' => $request->start_date,
                'end_date'   => $request->end_date,
            ]
        );

		// I guess here I must have a code like this
        DB::table('user_task')->insert(
            [
                [
                    'user_id'  => auth()->id(),
                    'task_id'  => $task->id(),
                ],
            ]
        );

        return redirect(route('task'));
    }
  1. How to get all the tasks that belong to a user using this custom table so I can pass them to the view?
1 like
vincent15000's avatar

@Merklin No not like this.

public function store(Request $request): RedirectResponse
{
	$task = Task::create(...);

	auth()->user()->tasks()->attach($task);

	return redirect()->route('task');
}
1 like
vincent15000's avatar

@Merklin And to retrieve all the tasks for a specific user, I already have answered.

Then you just have to pass the tasks variable to the route to display the tasks page.

public function index()
{
	$tasks = auth()->user()->tasks;

	return view('tasks.index', compact('tasks'));
}
1 like

Please or to participate in this conversation.