场景
一个模仿知乎的网站有三张表 questions topics question_topic
建立了多对多的关系, 然后$question->topic()->sync($topic_list); 竟然无法触发
QuestionTopic Model的deleted事件
分析
sync的确会触发deleted 但是触发的Builder上面的事件 而不是QuestionTopic事件
类似于DB::table(‘question_topic’)->delete(1)不会触发一样
源码
1. trait Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithPivotTable sync
2. function sync中调用了detach方法, detach中进行了delete操作 但是执行的`$query`
$query是什么呢?
sync是怎么工作的呢? 看下面
/**
* Sync the intermediate tables with a list of IDs or collection of models.
*
* @param \Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection|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->newPivotQuery()->pluck(
$this->relatedPivotKey
)->all();
$detach = array_diff($current, array_keys(
$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 && 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'])) {
$this->touchIfTouching();
}
return $changes;
}
detach 进行了delete操作 但是操作的主体是$query
/**
* Detach models from the relationship.
*
* @param mixed $ids
* @param bool $touch
* @return int
*/
public function detach($ids = null, $touch = true)
{
$query = $this->newPivotQuery();
// If associated IDs were passed to the method we will only delete those
// associations, otherwise all of the association ties will be broken.
// We'll return the numbers of affected rows when we do the deletes.
if (! is_null($ids)) {
$ids = $this->parseIds($ids);
if (empty($ids)) {
return 0;
}
$query->whereIn($this->relatedPivotKey, (array) $ids);
}
// Once we have all of the conditions set on the statement, we are ready
// to run the delete on the pivot table. Then, if the touch parameter
// is true, we will go ahead and touch all related models to sync.
$results = $query->delete();
if ($touch) {
$this->touchIfTouching();
}
return $results;
}
$query是查询生成器\Illuminate\Database\Query\Builder
/**
* Create a new query builder for the pivot table.
*
* @return \Illuminate\Database\Query\Builder
*/
protected function newPivotQuery()
{
$query = $this->newPivotStatement();
foreach ($this->pivotWheres as $arguments) {
call_user_func_array([$query, 'where'], $arguments);
}
foreach ($this->pivotWhereIns as $arguments) {
call_user_func_array([$query, 'whereIn'], $arguments);
}
return $query->where($this->foreignPivotKey, $this->parent->{$this->parentKey});
}
解决
感谢`packagist.org` 完美解决问题的包`fico7489/laravel-pivot`
composer require fico7489/laravel-pivot:"*"
案例
https://github.com/carsonlius/zhihu/blob/master/app/Question.php