Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

rjruiz's avatar

How to save a belongsToMany relationship?

I have a belongsToMany relationship between part and tool with a pivot table.

A piece is manufactured with one or more tools. What I intend to achieve at this moment is to associate, relate all those tools that I dynamically enter in my form with their corresponding piece.

This is my dynamic table in my form

<div class="box box-primary">
            <div class="box-header with-border">
                <h3 class="box-title">Detalles de herramientas</h3>                
            </div>
            <div class="box-body">  

            <table class="table table-striped table-bordered table-condensed table-hover" id="dynamicTable">  
                <tr>
                    <th>Posición</th>
                    <th>Herramienta</th>                 
                    <th>Inserto</th>                 
                    <th>Calidad</th>                 
                    <th>Action</th>
                </tr>
                <tr>  
                    <td><input type="text" name="position[]" placeholder="Posicion" class="form-control select2" /></td>  
                    <td><input type="text" name="code_tool[]" placeholder="Codigo" class="form-control" /></td> 
                    <td><input type="text" name="code_insert[]" placeholder="Codigo" class="form-control" /></td> 
                    <td><input type="text" name="quality[]" placeholder="Calidad" class="form-control" /></td>                      
                    <td><button type="button" name="add" id="add" class="btn btn-success"><i class="fa fa-plus-square"></i></button></td>  
                </tr>  
            </table> 

            </div>
        </div>

In the store method I basically do the following:

I capture the data entered dynamically from my form that come in array format, these are insert and tool, a tool can have one or more inserts, then I capture the data entered, I associate the insert with its tool, then I intend to associate those tools entered to his corresponding piece, but I haven't achieved it yet

I've tried this with no effect:

$piece->tools()->sync($request->$tools);

This is my controller: store method

public function store(Request $request)
    {

        if ($request->ajax()){
            try {
                //  Transacciones
                DB::beginTransaction();              


                // dd($request->all());
               // dynamic fields
                $position    = $request->position;
                $code_tool   = $request->code_tool;
                $code_insert = $request->code_insert;
                $quality     = $request->quality;                 

                 // I walk the array, I create the insert then I associate it with its corresponding tool
                for($count = 0; $count < count($position); $count++)
                {
                    $insert = array(                        
                        'code_insert' => $code_insert[$count],
                        'quality'     => $quality[$count]

                    );

                    $insert = new Insert();
                    $insert->code_insert =  $code_insert[$count];
                    $insert->quality = $quality[$count];

                    if($insert->save()){
                        $tool = array(
                        'position'    => $position[$count],
                        'code_tool'   => $code_tool[$count],
                        'insert_id'   => $insert->id
                        );
                        $tool_data[] = $tool;
                    }                 
                }
                // dd($tool_data);             

                $tools = Tool::insert($tool_data);

                $gag     = Gag::create($request->all());
                $program = Program::create($request->all()); 

                $piece = $program->piece()->create([
                    'denomination' => $request['denomination'],
                    'code'         => $request['code'],
                    'time'         => $request['time'],
                    'part_piece'   => $request['part_piece'],
                    'gag_id'       => $gag->id
                ]);
                dd($piece);

                // at this point you should associate all the tools entered to their corresponding part
                $piece->tools()->sync($request->$tools);

                DB::commit();

            } catch (Exception $e) {
                // anula la transacion
                DB::rollBack();
            }
        }    
    }

just doing this before associating it

dd($piece);
$piece->tools()->sync($request->$tools);

I get the following:

Piece {#1062
  #fillable: array:7 [
    0 => "program_id"
    1 => "gag_id"
    2 => "denomination"
    3 => "code"
    4 => "part_piece"
    5 => "time"
    6 => "observation"
  ]
  #connection: "mysql"
  #table: "pieces"
  #primaryKey: "id"
  #keyType: "int"
  +incrementing: true
  #with: []
  #withCount: []
  #perPage: 15
  +exists: true
  +wasRecentlyCreated: true
  #attributes: array:9 [
    "denomination" => "prueba6"
    "code" => "prueba6"
    "time" => "01:46:18"
    "part_piece" => "1"
    "gag_id" => 9
    "program_id" => 8
    "updated_at" => "2020-01-29 21:53:14"
    "created_at" => "2020-01-29 21:53:14"
    "id" => 8
  ]
  #original: array:9 [
    "denomination" => "prueba6"
    "code" => "prueba6"
    "time" => "01:46:18"
    "part_piece" => "1"
    "gag_id" => 9
    "program_id" => 8
    "updated_at" => "2020-01-29 21:53:14"
    "created_at" => "2020-01-29 21:53:14"
    "id" => 8
  ]
  #changes: []
  #casts: []
  #dates: []
  #dateFormat: null
  #appends: []
  #dispatchesEvents: []
  #observables: []
  #relations: []
  #touches: []
  +timestamps: true
  #hidden: []
  #visible: []
  #guarded: array:1 [
    0 => "*"
  ]
}

How should I associate the tools used in the manufacture of the piece?

these are my models model Piece

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Piece extends Model
{
    protected $fillable = [
        'program_id', 'gag_id', 'denomination', 'code', 'part_piece', 'time', 'observation'
    ];    

    public function program()
    {
         return $this->belongsTo(Program::class);
    }  

    public function gag()
    {
        return $this->belongsTo(Gag::class);
    } 

    public function tools() 
    {        
        return $this->belongsToMany(Tool::class)->withTimestamps();
    }   

}

model Tool

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Tool extends Model
{
    protected $fillable = [
        'insert_id', 'position', 'code_tool', 'type', 'category', 'status', 'description', 'reason'
    ];   

    public function inserts()
    {
        return $this->hasMany(Insert::class);
    }    

    public function pieces()
    {
        return $this->belongsToMany(Piece::class)->withTimestamps();
    }
}

I need your help, thanks

0 likes
10 replies
ismaile's avatar

Do you get any error ? Could you please share your form code ?

rjruiz's avatar

@ismaile a moment ago I updated my publication I have only uploaded my dynamic table that is in my form, error code does not throw, it simply does not relate the tools with its corresponding piece, I can see it in the pivot table

rjruiz's avatar

@ismaile It is only a fragment of my form, precisely the dynamic table to observe the fields in which I enter the information

ismaile's avatar
Edited to stay relevant [since the initial post got updated with new information]

Here is what's most likely happening with your code. In $piece->tools()->sync($request->$tools);, $request->tools is null. So, sync will detach any existing relationships. But if there are no existing relationships, it won't have any effect. In any case, no errors.

Now, 2 things to keep in mind:

  • If the tools already exist, you should directly pass an array of ids to the sync method as described in the documentation: https://laravel.com/docs/6.x/eloquent-relationships#updating-many-to-many-relationships. Besides, when debugging, you should focus on what you are passing to the sync method. In this case, dd($request->tools) would returnnull since $request properties come from the view inputs and there is no tools input.
  • sync has a special behaviour, "Any IDs that are not in the given array will be removed from the intermediate table" as specified in the documentation. If you need to keep previous relationships, you should reach for attach.

I hope this clarifies. So, there are 2 next steps:

  • Deciding if you use sync or attach
  • Modifying the data passed to the sync or attach method
rjruiz's avatar

@ismaile Your observation is correct, I have done this: dd($request->$tools); as a result i get null, In other words, how do I correct this?

would you be suggesting this change

$piece->tools()->sync($request->$tools);

For this:

$piece->tools()->attach($tools);

specifically what do you mean by Modifying the view to pass the proper data?

ismaile's avatar

Using $tools seems more appropriate. Besides, if you don't want to lose existing relationships, then attach is the method to use. In this case, yes, you can try:

$piece->tools()->attach($tools);

If you have any issue, try to insert dd($tools) before the attach call to make sure $tools has the right format.

rjruiz's avatar

@ismaile I briefly told you what happened with the modification you have indicated: in my piece table its program, its jaw is correctly inserted, but I can't associate their tools, I can see in my piece_tool pivot table. the next

id  | piece_id |tool_id
9   |   11         | 1
10 |   12         | 1


For example, for the piece with id 11, I used 2 tools. instead you would have to get this:

    
id  | piece_id |tool_id
9   |   11         | 1
10 |   11         | 2


What I mean by this: if 2 tools are used for the manufacture of a piece in my pivot table, I only get the id of the piece but I don't get the id of the tools. as tool id i always get as id to 1

ismaile's avatar

I think your issue comes from this line:

$tools = Tool::insert($tool_data);

Indeed, Tool::insert($tool_data); is going to return true which resolves to 1.

The quickest way to deal with this is to remove that line and replace:

$tool_data[] = $tool;

with:

$tools[] = Tool::insertGetId($tool);
1 like
rjruiz's avatar

@ismaile Great to perform a test associating a piece with its two tools is working perfectly. My store method has looked like this:

method store:

 public function store(Request $request)
    {
       
        if ($request->ajax()){
            try {
                //  Transacciones
                DB::beginTransaction();              
               
                // $this->authorize('create', new Order);                               
                // dd($request->all());
                $position    = $request->position;
                $code_tool   = $request->code_tool;
                $code_insert = $request->code_insert;
                $quality     = $request->quality;                 

                for($count = 0; $count < count($position); $count++)
                {
                    $insert = array(                        
                        'code_insert' => $code_insert[$count],
                        'quality'     => $quality[$count]

                    );
                    
                    $insert = new Insert();
                    $insert->code_insert =  $code_insert[$count];
                    $insert->quality = $quality[$count];

                    if($insert->save()){
                        $tool = array(
                        'position'    => $position[$count],
                        'code_tool'   => $code_tool[$count],
                        'insert_id'   => $insert->id
                        );
                        // $tool_data[] = $tool;
                        $tools[] = Tool::insertGetId($tool);
                    }                 
                }
                // dd($tools[]);             
                         
                $gag     = Gag::create($request->all());
                $program = Program::create($request->all()); 

                $piece = $program->piece()->create([
                    'denomination' => $request['denomination'],
                    'code'         => $request['code'],
                    'time'         => $request['time'],
                    'part_piece'   => $request['part_piece'],
                    'gag_id'       => $gag->id
                ]);
              
                // dd($tools);
                $piece->tools()->attach($tools);
                              
                DB::commit();

            } catch (Exception $e) {
                // anula la transacion
                DB::rollBack();
            }
        }    
    }

One last thing, I had several problems to achieve this, and I noticed several lines of code in my store method in other words, I think there is another cleaner way to get the same result, from capturing dynamically entered data, traversing the array and associating them to a bolongsToMany relationship?

If you have an explanation to this, A concrete example would thank you.

Thank you very much for guiding me and correcting my mistakes.

ismaile's avatar

To start with, you can remove this code:

$insert = array(                        
    'code_insert' => $code_insert[$count],
    'quality'     => $quality[$count]
);

Regarding the cleaner way to get the same result, let's think of it in 2 ways:

  • Cleaning the solution itself: I don't have the whole context but your code looks somehow similar to a To-do list with an Add task or + button. Instead of tasks, you have tools. The good news is you can find so many articles and lessons about To-do lists. Anyway, the quick answer is to separate things. You can use JavaScript to add existing tools to the list. Tools would appear in a multiple select with autocomplete and the user can select existing tools. If the tool doesn't exist, it could be created first along with its associated "insert". You would either do the creation in the same page with Ajax or in another page if you want to keep things separate. In this case, a link to the tool creation page would always be there if a tool doesn't exist yet. Once the list of tools is ready, the user can submit the list of tools so that they are attached to the piece.
  • Cleaning the code: as of now, the store method is rather big, so you can start by looking at thin controllers: https://laraveldaily.com/taylor-otwell-thin-controllers-fat-models-approach/

Hope this helps.

Please or to participate in this conversation.