För att kontrollera om en given modell är relaterad till en annan, vilket är vad du vill ha om jag förstår dig rätt, behöver du bara den här lilla metoden som gör det bästa av Eloquent
:
(Implementera det i BaseModel
, Entity
eller en omfattning, vad som än passar dig)
// 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);
Magin händer här:
/**
* 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);
}
Hej, men vad händer där?
isRelatedTo()
fungerar så här:
-
kontrollera om
$id
har gått igenom är en modell eller bara ett id, och förbereder$related
modell och dess$id
för användning vid återuppringning. Om du inte skickar ett objekt måste Eloquent instansiera alla relaterade modeller på$relations
(relation1.relation2.relation3...
) kedja för att få den vi är intresserade av - det är vad som händer igetNestedRelated()
, ganska okomplicerat. -
då måste vi göra något sånt här:
// 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)
-
eftersom vi inte vet hur djupt relationen är kapslad måste vi använda upprepning. Men vi skickar en stängning till
whereHas
, så vi måste använda lite knep för att kalla sig inuti sin kropp (i själva verket kallar vi det inte, utan skickar det som$callback
tillwhereHas
metod, eftersom den senare förväntar sig en stängning som 2:a param) - det här kan vara användbart för de obekanta Anonyma rekursiva PHP-funktioner :// save it to the variable and pass it by reference $callback = function () use (&$callback) { if (...) // call the $callback again else // finish; }
-
vi går också över till stängningen
$relations
(som en array nu) genom referens för att avskifta dess element, och när vi fick dem alla (vilket betyder att vi kapsladewhereHas
), lägger vi till slutwhere
sats istället för en annanwhereHas
, för att söka efter vår$related
modell. -
låt oss slutligen returnera
bool