'Laravel attach pivot to table with multiple values

Background

I'm creating a database revolving around food allergies and I have a many to many relationship between foods and allergies. There is also a pivot value called severity which has a numerical number representing the severity of the allergy for that food item.

This link table looks like this;

food_id|allergy_id|severity
-------|----------|--------
     1 |        1 |      3
     1 |        4 |      1
     2 |        2 |      1

The problem

When trying to update the link table with Eloquent (where $allergy_ids is an array)

$food->allergies()->attach($allergy_ids);

How would I go about adding multiple values to this pivot table at once along with the pivot values?

I can add all the allergy_id's for a particular food item in one go using the above line, but how can I also add in the severity column at the same time with an array of various severity values? Maybe something like

$food->allergies()->attach($allergy_ids, $severity_ids);

Edit: There could be between 0-20 allergies for a specific food item, and a severity rating from 0-4 for each allergy, if this helps at all.



Solution 1:[1]

You can.

From this example in Docs (4.2, 5.0):

$user->roles()->sync(array(1 => array('expires' => true)));

Hardcoded version for the first two rows:

$food = Food::find(1);
$food->allergies()->sync([1 => ['severity' => 3], 4 => ['severity' => 1]]);

Dynamically, with your arrays $allergy_ids and $severities in a compatible state (size and sort), you shall prepare your sync data before. Something like:

$sync_data = [];
for($i = 0; $i < count($allergy_ids); $i++))
    $sync_data[$allergy_ids[$i]] = ['severity' => $severities[$i]];

$food->allergies()->sync($sync_data);

Solution 2:[2]

You can't do it like you' like so I suggest a simple loop:

foreach ($allergy_ids as $key => $id)
{
  $food->allergies()->attach($id, array_get($severity_ids, $key));
  // should you need a sensible default pass it as a 3rd parameter to the array_get()
}

workaround However if you wanted to attach multiple allergies with single severity level/id then you could do this:

$food->allergies()->attach($allergy_ids, array('severity' => $singleSeverityValue));

Solution 3:[3]

From version 5.1 of Laravel (Currently in Laravel 9.x) onwards it is possible to pass an array as a second argument with all the additional parameters that need to be saved in the intermediate table. As you can read in the documentation

When attaching a relationship to a model, you may also pass an array of additional data to be inserted into the intermediate table:

$user->roles()->attach($roleId, ['expires' => $expires]);

For convenience, attach and detach also accept arrays of IDs as input:

$user->roles()->attach([1 => ['expires' => $expires], 2, 3]);

Then you can simply do

$food->allergies()->attach([1 => ['severity' => 3], 4 => ['severity' => 1]]);

Solution 4:[4]

Easiest indeed is to attach with the extra data, like so:

$retailer->paymentmethods()->attach($paymentmethod, array('currency' => $paymentmethod->currency));

change out the values for food allergy severity, but you get the hint... :-)

Solution 5:[5]

So, on Laravel 9, passing the ids in the array worked for me. Likeso,

$user->roles()->attach([$a->id,$b->id,$c->id]); and so on.

I guess instead of passing the string. We can pass just the id or else convert the string into array.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nuno Rafael Figueiredo
Solution 2 Jarek Tkaczyk
Solution 3
Solution 4 My Brain
Solution 5 Usama Munir