如何重构我的嵌套3深foreach循环

时间:2016-05-25 18:55:45

标签: php laravel nested-loops

我正在使用laravel composer包,它允许三个数据透视表,并且它不支持批量插入。所以我在各个数组中循环,以确保三个表中的每个id在我的三个数据透视表中都匹配。我有什么工作,但我想有一些关于如何重构我的函数的建议,所以我不必有这么多嵌套循环。我正在考虑使用数组映射。

delete

1 个答案:

答案 0 :(得分:4)

这是使用Eloquent解决的难题,也许目前是不可能的。首先,当您发现时,它不支持开箱即用的3路数据透视表。其次,您用来添加支持的包看起来会不断地将项目附加到该记录,从而在数据库中创建大量数据重复,因为没有同步。第三,如果您通过在表格中的这些字段上添加唯一索引来强制执行唯一记录,则不支持insert ignore个查询。

考虑到所有这些,为此使用原始sql可能是有用的。因为SQL中没有任何疯狂的事情发生,我认为在这种情况下会很好。

首先,您需要在数据库级别强制实施数据完整性。您这样做的方式取决于您的数据透视表的结构。如果此表已有id列,这是一个自动递增的主键,则需要向3列添加唯一键,其中包含您尝试链接的3个表的ID。如果您此处没有id或其他主键,则可以设置包含这3列的复合主键。

如果已有重复记录,则可能会失败。您需要做的就是删除那些重复项并重新添加密钥。

尽管如此,我还是会重构你的函数,以便它使用批量插入。

function addKeyPhraseToPivotTable($ids) {

    $locations = Location::where('account_id', self::$items[0]['account_id'])->get();
    $urls = Url::where('account_id', self::$items[0]['account_id'])->get();

    $deletes = [];
    if ($locations->count() > 0 && $urls->count() > 0) {
        foreach ($ids as $keyPhraseId) {
            foreach ($locations as $location) {
                foreach ($urls as $url) {
                    $deletes[] = [
                        'location' => $location->id,
                        'key' => $keyPhraseId,
                        'url' => $url->id, // Not sure what the ID is of this item, might need to change it.
                    ];
                }
            }
        }
    }

    $sql = 'insert ignore into accounts (location_id, key_phrase_id, url_id) values ';

    foreach ($deletes as $i => $delete) {
        $sql .= $i == 0 ? '' : ',';
        $sql .= sprintf('(%s, %s, %s)', $delete['location'], $delete['key'], $delete['url']);
    }

    DB::unprepared($sql);
}

这应该尝试插入你拥有的每条记录,因为你使用的是insert ignore,它在尝试插入副本时不会中断。

我相信DB::unprepared()会返回已创建的行数,因此您可以很好地了解尝试插入的项目数量是否重复,如果您需要的话。但是,如果你添加on duplicate key update,请记住,MySQL会为每行更新但未插入影响2行,所以在这种情况下,这个数字可能对你没用。