Loading

Laravel 模型关联 sync 方法中的性能问题记录

Laravel 模型关联 sync 方法中的性能问题记录

vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php​ 中的 sync​ 方法:

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

    // 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->getCurrentlyAttachedPivots()
                    ->pluck($this->relatedPivotKey)->all();

    $records = $this->formatRecordsList($this->parseIds($ids));

    // 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
    // array of the new IDs given to the method which will complete the sync.
    if ($detaching) {
        $detach = array_diff($current, array_keys($records));

        if (count($detach) > 0) {
            $this->detach($detach);

            $changes['detached'] = $this->castKeys($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)
    );

    // Once we have finished attaching or detaching the records, we will see if we
    // have done any attaching or detaching, and if we have we will touch these
    // relationships if they are configured to touch on any database updates.
    if (count($changes['attached']) ||
        count($changes['updated']) ||
        count($changes['detached'])) {
        $this->touchIfTouching();
    }

    return $changes;
}

sync​ 中调用 attachNew​ 方法,attachNew​中循环 $records​ 调用 attach​,

protected function attachNew(array $records, array $current, $touch = true)
{
    $changes = ['attached' => [], 'updated' => []];

    foreach ($records as $id => $attributes) {
        // If the ID is not in the list of existing pivot IDs, we will insert a new pivot
        // record, otherwise, we will just update this existing record on this joining
        // table, so that the developers will easily update these records pain free.
        if (! in_array($id, $current)) {
            $this->attach($id, $attributes, $touch);

            $changes['attached'][] = $this->castKey($id);
        }

        // Now we'll try to update an existing pivot record with the attributes that were
        // given to the method. If the model is actually updated we will add it to the
        // list of updated pivot records so we return them back out to the consumer.
        elseif (count($attributes) > 0 &&
            $this->updateExistingPivot($id, $attributes, $touch)) {
            $changes['updated'][] = $this->castKey($id);
        }
    }

    return $changes;
}

attach​ 中通过 $this->newPivotStatement()->insert​ 插入到数据库:

public function attach($id, array $attributes = [], $touch = true)
{
    if ($this->using) {
        $this->attachUsingCustomClass($id, $attributes);
    } else {
        // Here we will insert the attachment records into the pivot table. Once we have
        // inserted the records, we will touch the relationships if necessary and the
        // function will return. We can parse the IDs before inserting the records.
        $this->newPivotStatement()->insert($this->formatAttachRecords(
            $this->parseIds($id), $attributes
        ));
    }

    if ($touch) {
        $this->touchIfTouching();
    }
}
posted @ 2025-04-25 17:23  zhpj  阅读(25)  评论(0)    收藏  举报