6

我使用 Laravel 5.4 开发了一个电子商务(具有多个产品属性功能)网站。一切正常。但是当我尝试在数据透视表中同步同一属性的多个值时。Laravel 会忽略重复的部分。例如,我有一个名为“Network”的属性,它有 3 个值:2G、3G、4G。一部手机支持3G和4G网络。我想在数据库中同步 3G 和 4G 值。Laravel 忽略了其中一个。

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

我想将产品属性存储在类似的“ProductAttributes”表中。但是 Laravel 忽略了其中之一。

我正在保存这样的数据:

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

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

任何建议,想法?

4

5 回答 5

15

我知道这已经晚了两年,但我今天正在处理同样的问题,并认为我可能会将解决方案留在这里,以防将来有人寻找它。如果您使用原始代码:

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

数组中的第二项将覆盖第一项,因此最后,数据库中将只有一个“4G”条目。这不是一个真正的 laravel 问题,它是 PHP 关联数组的实现方式 - 您基本上不能在同一索引上的数组中有两个项目。

实际上有两种方法可以解决这个问题

1)第一个效率很低,但它很实用。我把它留在这里只是为了记录,因为这是我解决问题的原始方式。而不是你的代码,你需要这样的东西

    $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'], 
    ]);

这是非常低效的,因为它需要一个查询来从关系中删除所有现有数据,然后一个一个地添加新项目。您也可以syncWithoutDetaching在循环内部运行,总体效率低下很大程度上取决于您需要同步多少项目。

2)第一个解决方案不够好,所以我一直在挖掘和试验,并想出了一个方法来实现这一点。您可以在不给定特定索引的情况下发送数组,而不是将您的项目放在数组中的特定索引上,并将 ID 放在数组本身中。像这样的东西

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

通过这样做,您实际上可以将两个相同的项目发送AttributeID到 sync() 方法,而不会覆盖另一个

于 2019-04-05T05:21:50.563 回答
2

目前,

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

$product->attributes()->sync([
    ['AttributeID' => 1, 'AttributeValue' => '3G'], 
    ['AttributeID' => 1, 'AttributeValue' => '4G']
]);
于 2021-05-04T20:11:50.887 回答
1

查看 Becquerel 4 月 5 日的回复,回复 #2,并在阅读(我认为是 Laravel 2.4)中sync()方法的源代码时/laravel/framework/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php,我没有看到(或无法识别)支持这个“数组”的代码的数组”功能。实际上,这是整个方法的源代码:

public function sync($ids, $detaching = true)
{
    $changes = [
        'attached' => [], 'detached' => [], 'updated' => [],
    ];

    if ($ids instanceof Collection) {
        $ids = $ids->modelKeys();
    }

    // First we need to attach any of the associated models that are not currently
    // in this joining table. We'll spin through the given IDs, checking to see
    // if they exist in the array of current ones, and if not we will insert.
    $current = $this->newPivotQuery()->pluck($this->otherKey);

    $records = $this->formatSyncList($ids);

    $detach = array_diff($current, array_keys($records));

    // Next, we will take the differences of the currents and given IDs and detach
    // all of the entities that exist in the "current" array but are not in the
    // the array of the IDs given to the method which will complete the sync.
    if ($detaching && count($detach) > 0) {
        $this->detach($detach);

        $changes['detached'] = (array) array_map(function ($v) {
            return is_numeric($v) ? (int) $v : (string) $v;
        }, $detach);
    }

    // Now we are finally ready to attach the new records. Note that we'll disable
    // touching until after the entire operation is complete so we don't fire a
    // ton of touch operations until we are totally done syncing the records.
    $changes = array_merge(
        $changes, $this->attachNew($records, $current, false)
    );

    if (count($changes['attached']) || count($changes['updated'])) {
        $this->touchIfTouching();
    }

    return $changes;
}

现在,Laravel 充满了依赖注入和其他 Magick(显然类似于 Perl 的“映射?”概念),但我在这里看不到任何能像 Becquerel 所说的那样做的事情。而且,一般来说,Laravel 的文档并没有说它对多对多关系中的重复值做了什么,或者它是否完全意识到它们

我还注意到,如上所示的方法的实现实际上与他所说的“效率极低”的“替代方案#1”非常相似。似乎将键分为三个桶......通过我的阅读,似乎永远不允许重复......然后根据需要执行插入,更新和删除操作。 (我可以看到,任何地方都没有 SQL“事务”,这也让我感到非常惊讶……它们是否以某种方式“神奇地”存在?)

我根本无法确定 Laravel 是否在外部表(相关记录集)中出现不止一次的值时,是否会执行任何明智的操作,例如将它们作为数组返回。

我做了这个非常冗长的回应,希望能引起进一步的评论。谢谢。

于 2019-11-14T15:15:31.867 回答
0

使用 sync 方法使用关系在 Controller 中存储/更新数据:

$product-> attribute()->sync($request->input('product_ids', []));
于 2018-08-09T02:36:39.693 回答
-1

sync()Laravel 中的函数自动读取重复项。你可以强制它

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

祝你好运!

于 2017-02-07T14:20:04.587 回答