Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to sync multiple values of the same Attribute in Laravel?

I developing an eCommerce ( with Multiple Product Attributes feature ) website using Laravel 5.4. Everything is working fine. But When I try to sync multiple values of the same Attribute in Pivot table. Laravel ignores the duplicate pares. For example, I've an Attribute called "Network" which has 3 values: 2G, 3G, 4G. A mobile supports 3G and 4G network. I want to sync 3G and 4G value in database. Laravel ignores one of them.

Products Table: 

ID - Product Name
1  - Test Mobile



Attributes Table

ID - AttributeName
1 - Network



AttributeValues Table

ID - AttributeID - AttributeValue
1  - 1           - 2G
2  - 1           - 3G
3  - 1           - 4G



ProductAttributes Table

ID - AttributeID - ProductID - AttributeValue
1  - 1           - 1         - 3G
1  - 1           - 1         - 4G

I want to store the Product Attributes in "ProductAttributes" table something like that. But Laravel Ignore one of them.

I am saving the data like that:

    $product = Product::create([
        'name' => 'Test Mobile'
    ]);

    $product->attributes()->sync([
        1 => ['AttributeValue' => '3G'], 
        1 => ['AttributeValue' => '4G']
    ]);

Any suggestions, Ideas?

like image 885
Rizwan Khan Avatar asked Feb 07 '17 14:02

Rizwan Khan


People also ask

What is the difference between attach and sync in laravel?

The attach function only adds records to the Pivot table. The sync function replaces the current records with the new records. This is very useful for updating a model.

What is syncWithoutDetaching?

syncWithoutDetaching() will remove all existed relations except provided in current process, then the pre-existed rows won't get new ids.


2 Answers

I know this is two years late, but I was dealing with the same issue today, and figured I may leave the solution here, in case anyone looks for it in the future. If you use your original code:

    $product->attributes()->sync([
        1 => ['AttributeValue' => '3G'], 
        1 => ['AttributeValue' => '4G']
    ]);

the second item in the array will overwrite the first one, so in the end, there will only be a "4G" entry in the database. This is not really a laravel issue, it is how PHP associative arrays are implemented - you basically cannot have two items in the array on the same index.

There are actually two ways to solve this issue

1) first one is very inefficient, but it is functional. I am leaving it here only for the record, because that was the original way I solved the issue. Instead of your code, you would need something like this

    $product->attributes()->sync([]);  // empty relation completely

    // add first item
    $product->attributes()->syncWithoutDetaching([
        1 => ['AttributeValue' => '3G'],
    ]);

    // add second item without detaching the first one
    $product->attributes()->syncWithoutDetaching([
        1 => ['AttributeValue' => '4G'], 
    ]);

this is EXTREMELY inefficient, because it needs one query to delete all existing data from the relation, and then add new items one by one. You could also run the syncWithoutDetaching inside a loop, and overall inefficiency would greatly depend on how many items you need to sync.

2) the first solution was not good enough, so I kept digging and experimenting, and figured out a way how to make this happen. Instead of putting your items on specific index in the array, you can send array without specific indexes given, and put the ID in the array itself. Something like this

    $product->attributes()->sync([
        ['AttributeID' => 1, 'AttributeValue' => '3G'], 
        ['AttributeID' => 1, 'AttributeValue' => '4G']
    ]);

by doing it this way, you can actually send two items with the same AttributeID to the sync() method, without one overwriting the other one

like image 78
Becquerel Avatar answered Sep 21 '22 12:09

Becquerel


For now,

$product->attributes()->sync([]);

$product->attributes()->sync([
    ['AttributeID' => 1, 'AttributeValue' => '3G'], 
    ['AttributeID' => 1, 'AttributeValue' => '4G']
]);
like image 21
Harat Avatar answered Sep 20 '22 12:09

Harat