Mysql
 sql >> Base de données >  >> RDS >> Mysql

Autorisation d'affichage/de mise à jour/de gestion des ressources imbriquées MySQL (tableaux croisés dynamiques)

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 :

  1. 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 dans getNestedRelated() , assez simple.

  2. 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)
    
  3. 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 au whereHas 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;
    }
    
  4. 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 le where clause au lieu d'un autre whereHas , pour rechercher notre $related modèle.

  5. enfin retournons bool