Pour vérifier si un modèle donné est lié à un autre, ce que vous voulez si je vous ai bien compris, tout ce dont vous avez besoin est cette petite méthode tirant le meilleur parti de Eloquent
:
(Implémentez-le dans BaseModel
, Entity
ou une portée, ce qui vous convient)
// 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);
La magie opère ici :
/**
* 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);
}
Hé, mais que se passe-t-il ?
isRelatedTo()
fonctionne comme ceci :
-
vérifier si
$id
est passé est un modèle ou juste un identifiant, et prépare$related
modèle et son$id
à utiliser dans le rappel. Si vous ne transmettez pas d'objet, Eloquent doit instancier tous les modèles associés sur les$relations
(relation1.relation2.relation3...
) pour obtenir celle qui nous intéresse - c'est ce qui se passe dansgetNestedRelated()
, assez simple. -
alors nous devons faire quelque chose comme ceci :
// 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)
-
puisque nous ne savons pas à quel point la relation est imbriquée, nous devons utiliser la récurrence. Cependant nous passons une Closure au
whereHas
, nous devons donc utiliser une petite astuce pour s'appeler à l'intérieur de son corps (en fait, nous ne l'appelons pas, mais le passons plutôt comme$callback
auwhereHas
méthode, puisque ce dernier attend une fermeture comme 2ème paramètre) - cela pourrait être utile pour ceux qui ne sont pas familiers Fonctions PHP récursives anonymes :// save it to the variable and pass it by reference $callback = function () use (&$callback) { if (...) // call the $callback again else // finish; }
-
on passe aussi à la fermeture
$relations
(sous forme de tableau maintenant) par référence afin de décaler ses éléments, et quand nous les avons tous (ce qui signifie que nous avons imbriquéwhereHas
), on met enfin lewhere
clause au lieu d'un autrewhereHas
, pour rechercher notre$related
modèle. -
enfin retournons
bool