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

CosminAndrei's avatar

Pivot table with 3 foreign keys

Hi.

I have 3 tables: files, product_types and screen_colors.

In the screen_colors table I have 2 types of colors. I want to be able to assign a product type and a screen color type to each file. I created a pivot table with 3 foreign keys: file_id, product_type_id and screen_color_id and a relationship in File Model.

public function product_types()
     {
         return $this->belongsToMany(ProductType::class)->withPivot('screen_color_id');
     }

How can I implement a product type and screen color to the file?

This is the method I tried but I get error because screen_color can have different colors for different product types

(2/2) QueryException
Array to string conversion....

View:

@foreach($product_types as $product_type)
    <tr>
      <td class="checkbox-input">
        <input type="checkbox" name="product_type[{{$product_type->id}}]" value="{{$product_type->id}}">
      </td>
      <td>{{$product_type->brand}}</td>
      <td>{{$product_type->model}}</td>
      <td> {{$product_type->variant}}</td>
      <td>
          @if(in_array($product_type->model, $screen_color_items))
             @foreach($screen_colors as $screen_color)
             <div>
               <input type="checkbox" name="screen_color[{{$screen_color->id}}]" value="{{$screen_color->id}}">
               <span>{{$screen_color->name}}</span>
             </div>
             @endforeach
          @else
             Standard
          @endif
       </td>
     </tr>
@endforeach

Controller

$last_file_created = File::orderBy('id', 'desc')->first();

$last_file_created->product_types()->attach($request->input(['product_type']), ['screen_color_id' => $request->input(['screen_color'])]);

I am also attaching a reference picture of the table on the page for adding a file. https://ibb.co/52rPw0c

I.E

file_id  | product_type_id |  screen_color_id
---------+-----------------+-----------------
   1     |         2       |        1   
   1     |         2       |        2 
   1     |         3       |        1
   1     |         4       |        2    
0 likes
6 replies
automica's avatar

@cosminandrei

belongsToMany accepts 2 extra arguments beyond the table name, to tell Laravel which columns you are using for your relationship

In this example for User belongsToMany Role

"In addition to customizing the name of the joining table, you may also customize the column names of the keys on the table by passing additional arguments to the belongsToMany method. The third argument is the foreign key name of the model on which you are defining the relationship, while the fourth argument is the foreign key name of the model that you are joining to"

return $this->belongsToMany('App\Models\Role', 'role_user', 'user_id', 'role_id');

so in your case if your join table was called 'foo'

public function screenColors()
     {
         return $this->belongsToMany(ScreenColor::class,'foo', 'file_id', 'screen_color_id');
     }
CosminAndrei's avatar

If i do this, product_type_id is doesn't set anymore. I want to do something like this:

file_id  | product_type_id |  screen_color_id
---------+-----------------+-----------------
   1     |         2       |        1   
   1     |         2       |        2 
   1     |         3       |        1
   1     |         4       |        2    
automica's avatar

@cosminandrei so maybe you need to add the withPivot, if that's how you got all 3 columns saving for your productTypes relationship. (BTW Laravel convention recommends camelCase rather than snake_case for model relationships).

->withPivot('product_type_id');
CosminAndrei's avatar

@automica I think the problem is with this:

$last_file_created->productTypes()->attach($request->input(['product_type']), ['screen_color_id' => $request->input(['screen_color'])]);

$request->input(['screen_color']) return an array of 1 and 2. If i choose 1 for product_type 1, i want to set 'screen_color_id' => 1 and if i choose 2 for another product_type like 2, i want to set 'screen_color_id' => 2 . The final result would be:

file_id  | product_type_id |  screen_color_id
---------+-----------------+-----------------
  1     |         1       |        1   
  1     |         2       |        2   

Maybe some like this but dynamically:

$last_file_created->product_types()->attach([1 => ['screen_color_id' => 1], 2 => ['screen_color_id' => 2]]);

Any ideas?

automica's avatar

@cosminandrei i'm not sure how you are defining your form to save prepare this data, but I assume if you are looking to save multiple product types, you'll be passing in an array of productTypeIds and a matching array of screenColorIds;

if that's the case, we'd have:

    $productTypeIds = $request->product_type_id;
    $screenColorIds = $request->screen_color_id;

You'd then want to iterate through the $productTypeIds and then match that with the respective $screenColorId

$attachValues = [];

    foreach ($productTypeIds as $i => $productTypeId) {
        $attachValues[$productTypeId] = ['screen_color_id' => $screenColorIds[$i]];
    }

$last_file_created->productTypes()->attach($attachValues);
CosminAndrei's avatar

@automica I don't want to set value of productTypeId to screenColorId, i just want to set the checked value of screenColorId checkbox. My form is like: When i create a file i have an option to select from which type of product i want to set this file (can be multiple types). Some product types (id: 2,3,4,5) have this option: screenColorId where i can check 2 options , white (value of 1) and gold (value of 2). If i check for example product type 3 (value of 3) and for product type 3 i check white color (value of 1), i want for this created file (for example file with id 1) to set productTypeId 3 and screenColorId 1. How can i do this? Look on my view table: https://ibb.co/7vsNMK2

Please or to participate in this conversation.