😸
基礎から学ぶ Laravel P199修正
基礎から学ぶ Laravel P199修正
佐野大樹『基礎から学ぶLaravel』C&R研究所 2003のP199に誤りがあります。
誤 syncメソッドは、現在書籍に紐づいている著者IDをすべて削除してから、引数に指定されている著者IDを新しく関連付け直します。
正 syncメソッドは、引数に指定されている著者IDに関連付け直します。
試しに著者を1人追加すると、SQLは以下のようになっていました。
[2023-07-10 06:42:23] local.INFO: SQL Query Log: [{"query":"select * from `author_book` where `author_book`.`book_id` = ?","bindings":[10],"time":0.25},{"query":"insert into `author_book` (`author_id`, `book_id`, `created_at`, `updated_at`) values (?, ?, ?, ?)","bindings":[2,10,"2023-07-10 06:42:23","2023-07-10 06:42:23"],"time":0.66}]
また、著者を1人削除すると、SQLは以下のようになっていました。
[2023-07-10 06:42:39] local.INFO: SQL Query Log: [{"query":"select * from `author_book` where `author_book`.`book_id` = ?","bindings":[10],"time":0.58},{"query":"delete from `author_book` where `author_book`.`book_id` = ? and `author_book`.`author_id` in (?)","bindings":[10,2],"time":1.28}]
現在書籍に紐づいている著者IDと、引数に指定されている著者IDの差分をとって、INSERTが必要な行だけINSERTし、DELETEが必要な行だけDELETEしています。
syncメソッドのソースコードは以下のようになっています。
/**
* Sync the intermediate tables with a list of IDs or collection of models.
*
* @param \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|array $ids
* @param bool $detaching
* @return array
*/
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;
}
attachNew()の実装は以下の通りです。
/**
* Attach all of the records that aren't in the given current records.
*
* @param array $records
* @param array $current
* @param bool $touch
* @return array
*/
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;
}
現在書籍に紐づいている著者ID($current)と、引数に指定されている著者ID($ids)の差分をとって$changesに格納しています。
※しかしどこで$changesに格納した値を使ってSQLを実行しているのか分かりませんでした…
Discussion