113

Official Laravel documentation has this on sync() function:

$user->roles()->sync( array( 1, 2, 3 ) );

You may also associate other pivot table values with the given IDs:

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

In the latter example only a single pivot row is being added. What I don't understand is how to associate other pivot table records if there are more than one rows to be synced?

starball
  • 20,030
  • 7
  • 43
  • 238
Томица Кораћ
  • 2,542
  • 7
  • 35
  • 57

9 Answers9

185

In order to sync multiple models along with custom pivot data, you need this:

$user->roles()->sync([ 
    1 => ['expires' => true],
    2 => ['expires' => false],
    ...
]);

Ie.

sync([
    related_id => ['pivot_field' => value],
    ...
]);

edit

Answering the comment:

$speakers  = (array) Input::get('speakers'); // related ids
$pivotData = array_fill(0, count($speakers), ['is_speaker' => true]);
$syncData  = array_combine($speakers, $pivotData);

$user->roles()->sync($syncData);
Cyril Graze
  • 3,881
  • 2
  • 21
  • 27
Jarek Tkaczyk
  • 78,987
  • 25
  • 159
  • 157
  • 1
    Jarek but what if I have for example `$speakers = \Input::get( 'speakers' )` (where $speakers becomes an array), and then want to pass `$speakers` along with `=>array( 'is_speaker' => true)`? – Томица Кораћ Dec 01 '14 at 14:56
  • 1
    Not what if. You create sync array like shown above. Build your array with `ids` as keys, or make it this way in your HTML form. – Jarek Tkaczyk Dec 01 '14 at 15:07
  • Jarek, my \Input::get( 'speakers' ) is returning an array of ids. Would something like this work: `$training->users()->sync( array( $speakers => array( 'is_speaker' => true ) ) )` – Томица Кораћ Dec 01 '14 at 15:10
  • First off, try and you will learn it won't work. Like I said you need to build the array, so check the edit for an example. – Jarek Tkaczyk Dec 01 '14 at 15:57
  • OK, I got it in the end. Sorry, I didn't quite understand what you wrote. But I got it on my own. Thanks. – Томица Кораћ Dec 01 '14 at 15:59
  • 1
    @JarekTkaczyk Thanks! The answer to the comment was just what I was looking for! – Pathros Jan 27 '16 at 17:45
  • Thank you for this. It helps me a lot – XxXk5XxX Mar 02 '17 at 04:38
  • 1
    @JarekTkaczyk looks nice, but each item is updated\inserted with a separate query, which is a bit disappointing, as it will effect performance. – tylik Nov 10 '18 at 01:22
  • @tylik 1 that's how AR will work pretty much always, there's cost to using it. 2 you're ofc right about performance, but throwing such statement without any context is pointless - operation like this alone won't be a bottleneck – Jarek Tkaczyk Nov 22 '18 at 07:56
  • @JarekTkaczyk ```$model->detach($ids); $model->attach($items);``` worked for me. I had 10+ items to sync, so i thought this would be a better approach. – tylik Nov 22 '18 at 21:21
58

This works for me

foreach ($photos_array as $photo) {

    //collect all inserted record IDs
    $photo_id_array[$photo->id] = ['type' => 'Offence'];  

}

//Insert into offence_photo table
$offence->photos()->sync($photo_id_array, false);//dont delete old entries = false
Sincere
  • 477
  • 5
  • 18
stackflow
  • 2,112
  • 6
  • 32
  • 45
  • 3
    Upvoting this one because it's nice and clean, and I suspect it'd be very easy for any developer coming after to understand and modify. – DevBodin Nov 14 '19 at 23:03
  • 3
    You can replace ```->sync($photo_id_array, false)``` to ```->syncWithoutDetaching($photo_id_array)``` as well. Upvoting too since this is the most elegant solution. – Musa Apr 27 '20 at 16:14
24

There is now a ->syncWithPivotValues($ids, $pivotValues) method available if you want to set the same pivot value for all synced items.

Example from the doc:

$user->roles()->syncWithPivotValues([1, 2, 3], ['active' => true]);
Robin
  • 9,415
  • 3
  • 34
  • 45
5

Attaching / Detaching

Eloquent also provides a few additional helper methods to make working with related models more convenient. For example, let's imagine a user can have many roles and a role can have many users. To attach a role to a user by inserting a record in the intermediate table that joins the models, use the attach method:

$user = App\User::find(1);

$user->roles()->attach($roleId);

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]);

You can also use Sync if you want to remove old roles and only keep the new ones you are attaching now

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

The default behaviour can be changed by passing a 'false' as a second argument. This will attach the roles with ids 1,2,3 without affecting the existing roles.

In this mode sync behaves similar to the attach method.

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

Reference: https://laravel.com/docs/5.4/eloquent-relationships

2

Add following trait to your project and append it to your model class as a trait. This is helpful, because this adds functionality to use multiple pivots. Probably someone can clean this up a little and improve on it ;)

namespace App\Traits;

trait AppTraits
{
    /**
     * Create pivot array from given values
     *
     * @param array $entities
     * @param array $pivots
     * @return array combined $pivots
     */
    public function combinePivot($entities, $pivots = [])
    {
        // Set array
        $pivotArray = [];
        // Loop through all pivot attributes
        foreach ($pivots as $pivot => $value) {
            // Combine them to pivot array
            $pivotArray += [$pivot => $value];
        }
        // Get the total of arrays we need to fill
        $total = count($entities);
        // Make filler array
        $filler = array_fill(0, $total, $pivotArray);
        // Combine and return filler pivot array with data
        return array_combine($entities, $filler);
    }
}

Model:

namespace App;

use Illuminate\Database\Eloquent\Model;

class Example extends Model
{
    use Traits\AppTraits;
    // ...
}

Usage:

// Get id's
$entities = [1, 2, 3];
// Create pivots
$pivots = [
    'price' => 634,
    'name'  => 'Example name',
];
// Combine the ids and pivots
$combination = $model->combinePivot($entities, $pivots);
// Sync the combination with the related model / pivot
$model->relation()->sync($combination);
  • 1
    I like your thinking-- better to turn it into something reusable, rather than copied the next time this issue comes up-- though I'd recommend making it a helper function so it could be used anywhere and with any model. – DevBodin Nov 14 '19 at 23:10
1

Simply assign your key-value fields to its id, and update the pivot like below:

$user->roles()->sync([
   1 => ['F1' => 'F1 Updated']
]);
X 47 48 - IR
  • 1,250
  • 1
  • 15
  • 28
  • Thanks a lot. I had a problem with id's and differente values, I could resolve my problem with your solution. – Azade Jul 24 '23 at 04:37
0
$data = array();
foreach ($request->planes as $plan) {
 $data_plan = array($plan => array('dia' => $request->dia[$plan] ) );                
 array_push($data,$data_plan);    
}
$user->planes()->sync($data);
  • 9
    When answering an old post, it would be helpful if you could provide some context to your answer rather than just code, as it might make it more useful to others. – David Buck Nov 24 '19 at 13:24
  • See David Bucks comment. Also, include software and/or system and/or library versions; only way to prevent downvoting or deletion of answer. – ZF007 Nov 24 '19 at 19:16
0

Putting this here in case I forget it later and Google it again.

In my case I wanted the extra column to have the same data for each row

Where $syncData is an array of IDs:

$syncData = array_map(fn($locationSysid) => ['other_column' => 'foo'], array_flip($syncData));

or without arrow

$syncData = array_map(function($locationSysid) {
      return ['ENTITY' => 'dbo.Cli_Core'];
   }, array_flip($syncData));

(array_flip means we're using the IDs as the index for the array)

Healyhatman
  • 1,482
  • 1
  • 14
  • 23
0
    foreach ($request->exercise_id as $key => $exercise_id) {
        $data_plan[$exercise_id] = [
            'serie' => $request->serie[$key],
            'observation' => $request->observation[$key],
        ];
    }