Reputation: 69
I have a many to many
relationship.
Quote Model
public function products(){
return $this->belongsToMany(Product::class);
}
Product Model
public function quotes(){
return $this->belongsToMany(Quote::class);
}
I receive details from an input form to my Controller
. The output is as follows:
array:39 [▼
"_token" => "NgcHxCjpGUe1ot8nJr4Z8VFuA3DG9VprKWqRu5yk"
"quote_id" => "52"
20 => "0"
10 => "0"
11 => "0"
12 => "0"
13 => "0"
14 => "0"
15 => "0"
16 => "0"
17 => "0"
The "quote_id"
refers to the quote created in a step preceding the input form mentioned above. The quote_id
is passed to the controller from a hidden input field in the form. In the array above the $key
refers to the product_id
and the $value
refers to the quantity of the product.
I am trying to insert the data into a table
product_quote
.
Schema::create('product_quote', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('product_id');
$table->unsignedBigInteger('quote_id');
$table->integer('qty');
$table->timestamps();
});
Here is the code I have:
$data = $request->toArray();
$quote_id = $data['quote_id'];
unset($data['quote_id']);
$quote = Quote::query()->findOrFail($quote_id);
foreach (array($data) as $key => $value){
$quote->products->sync($data);
$quote->save();
}
There after I still need to add the product quantity but I am getting an error: Call to a member function sync() on null.
What am I doing wrong?
Upvotes: 1
Views: 183
Reputation: 18926
This is a misunderstanding of Collections vs Relations.
In Laravel if you have the relation products in your case, to access the relation you will call.
$quote->products(); // returns belongsToMany relation query
To access the collection you access it as a property.
$quote->products; // returns a collection with the products
In your case, you are accessing it as a Collection. Thou you are getting a null error something else could be wrong, but start by changing it to the following.
Secondly to set pivot fields use key value array on the sync call, using the key and the value from your structure. You should probably also unset the token. Instead i filter it by using except. A better approach is to use validated on a form request.
You will need to adjust your data to fit the expected key value structure on the sync call, sync should also only be called one as it synchronizes the data between each call.
$data = $request->toArray();
$quote = Quote::findOrFail($data['quote_id']); // calling query() is unnecessary
$products = collect($request->except(['_token', 'quote_id']))->
->mapWithKeys(function($item, $key) {
return [$key => ['qty' => $item]];
})->all();
$quote->products()->sync($products);
This solution utilises mapWithKeys, which lets you control the key and the item in the collection, which is a collection method not used that often. The return of the closure should be in the format return ['key' => 'item'];
instead of return 'item';
Upvotes: 1
Reputation: 4684
You need to use the products
relationships rather than the collection.
$quote->products()->sync($data);
Upvotes: 0