场景
. 多对多的关系中经常需要用到的一种方式
. 实例 Question Model and Follower Model(问题关注者)
假设点击一个按钮,则如果用户已经是关注者,则取消关注 否则成为关注者
分析
1. belongsToMany 在哪里实现了toggle? 内部trait实现
class BelongsToMany extends Relation
{
use Concerns\InteractsWithPivotTable;
}
2. toggle实现的过程 :
. 将传入的参数全部转成Key为id的数组 (传入的参数可能是Model Collection BaseCollection 或者本身就是id 数组或者字符串)
. 和已经存在的关系(id array)找交集 $detch,交集执行detach
. 和已经存在的关系(id array)找相对补集 执行attach
这里的实现有点意思 采用的方式是在 解析出来的id array中找$deatch补集
$attach = array_diff_key($records, array_flip($detach));
/**
* Toggles a model (or models) from the parent.
*
* Each existing model is detached, and non existing ones are attached.
*
* @param mixed $ids
* @param bool $touch
* @return array
*/
public function toggle($ids, $touch = true)
{
$changes = [
'attached' => [], 'detached' => [],
];
$records = $this->formatRecordsList($this->parseIds($ids));
// Next, we will determine which IDs should get removed from the join table by
// checking which of the given ID/records is in the list of current records
// and removing all of those rows from this "intermediate" joining table.
$detach = array_values(array_intersect(
$this->newPivotQuery()->pluck($this->relatedPivotKey)->all(),
array_keys($records)
));
if (count($detach) > 0) {
$this->detach($detach, false);
$changes['detached'] = $this->castKeys($detach);
}
// Finally, for all of the records which were not "detached", we'll attach the
// records into the intermediate table. Then, we will add those attaches to
// this change list and get ready to return these results to the callers.
$attach = array_diff_key($records, array_flip($detach));
if (count($attach) > 0) {
$this->attach($attach, [], false);
$changes['attached'] = array_keys($attach);
}
// 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 ($touch && (count($changes['attached']) ||
count($changes['detached']))) {
$this->touchIfTouching();
}
return $changes;
}