Para verificar si el modelo dado está relacionado con otro, que es lo que quieres si te entiendo bien, todo lo que necesitas es este pequeño método que aprovecha al máximo Eloquent
:
(Impleméntelo en BaseModel
, Entity
o un alcance, lo que más le convenga)
// 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 magia sucede aquí:
/**
* 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);
}
Oye, pero ¿qué está pasando ahí?
isRelatedTo()
funciona así:
-
verificar si pasó
$id
es un modelo o simplemente una identificación, y prepara$related
modelo y su$id
para su uso en la devolución de llamada. Si no pasa un objeto, Eloquent necesita instanciar todos los modelos relacionados en$relations
(relation1.relation2.relation3...
) cadena para obtener el que nos interesa; eso es lo que sucede engetNestedRelated()
, bastante sencillo. -
entonces tenemos que hacer algo como esto:
// 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)
-
dado que no sabemos qué tan profundamente está anidada la relación, necesitamos usar la recurrencia. Sin embargo, pasamos un Closure al
whereHas
, por lo que necesitamos usar un pequeño truco para llamarse a sí mismo dentro de su cuerpo (de hecho, no lo llamamos, sino que lo pasamos como$callback
alwhereHas
método, ya que este último espera un Cierre como segundo parámetro) - esto podría ser útil para aquellos que no están familiarizados con Funciones PHP recursivas anónimas :// save it to the variable and pass it by reference $callback = function () use (&$callback) { if (...) // call the $callback again else // finish; }
-
también pasamos al cierre
$relations
(como una matriz ahora) por referencia con el fin de cambiar sus elementos, y cuando los obtuvimos todos (lo que significa que anidamoswhereHas
), finalmente ponemos elwhere
cláusula en lugar de otrowhereHas
, para buscar nuestro$related
modelo. -
finalmente regresemos
bool