与えられたモデルが別のモデルに関連しているかどうかを確認するために必要なのは、Eloquent
を最大限に活用するこの小さなメソッドだけです。 :
(BaseModel
に実装します 、Entity
またはスコープ、あなたに合ったものは何でも)
// usage
$task->isRelatedTo('transactions.users', $id);
// or
$template->isRelatedTo('tasks.transactions.users', Auth::user());
// or any kind of relation:
// imagine this: User m-m Transaction 1-m Item m-1 Group
$group->isRelatedTo('items.transaction.users', $id);
魔法はここで起こります:
/**
* Check if it is related to any given model through dot nested relations
*
* @param string $relations
* @param int|\Illuminate\Database\Eloquent\Model $id
* @return boolean
*/
public function isRelatedTo($relations, $id)
{
$relations = explode('.', $relations);
if ($id instanceof Model)
{
$related = $id;
$id = $related->getKey();
}
else
{
$related = $this->getNestedRelated($relations);
}
// recursive closure
$callback = function ($q) use (&$callback, &$relations, $related, $id)
{
if (count($relations))
{
$q->whereHas(array_shift($relations), $callback);
}
else
{
$q->where($related->getQualifiedKeyName(), $id);
}
};
return (bool) $this->whereHas(array_shift($relations), $callback)->find($this->getKey());
}
protected function getNestedRelated(array $relations)
{
$models = [];
foreach ($relations as $key => $relation)
{
$parent = ($key) ? $models[$key-1] : $this;
$models[] = $parent->{$relation}()->getRelated();
}
return end($models);
}
ねえ、でもそこで何が起こっているの?
isRelatedTo()
このように機能します:
-
$id
が渡されたかどうかを確認します はモデルまたは単なるIDであり、$related
を準備します モデルとその$id
コールバックで使用します。オブジェクトを渡さない場合、Eloquentは$relations
で関連するすべてのモデルをインスタンス化する必要があります (relation1.relation2.relation3...
)興味のあるものを取得するためのチェーン-それがgetNestedRelated()
で発生します 、かなり簡単です。 -
次に、次のようなことを行う必要があります:
// assuming relations 'relation1.relation2.relation3' $this->whereHas('relation1', function ($q) use ($id) { $q->whereHas('relation2', function ($q) use ($id) { $q->whereHas('relation3', function ($q) use ($id) { $q->where('id', $id); }); }); })->find($this->getKey()); // returns new instance of current model or null, thus cast to (bool)
-
関係がどの程度深くネストされているかわからないため、再通貨を使用する必要があります。ただし、Closureを
whereHas
に渡します 、したがって、本体内で自分自身を呼び出すには、ちょっとしたトリックを使用する必要があります(実際には、それを呼び出さず、$callback
として渡します。whereHas
へ メソッド、後者は2番目のパラメータとしてクロージャを想定しているため)-これは、なじみのない匿名の再帰的PHP関数 :// save it to the variable and pass it by reference $callback = function () use (&$callback) { if (...) // call the $callback again else // finish; }
-
また、クロージャー
$relations
に渡します (現在は配列として)その要素のシフトを解除するために参照し、すべてを取得したとき(つまり、whereHas
をネストしました )、最終的にwhere
を配置します 別のwhereHas
の代わりに句 、$related
を検索するには モデル。 -
最後に、
bool
を返しましょう