Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Berechtigung für verschachtelte MySQL-Ressourcen (Pivot-Tabellen) zum Anzeigen/Aktualisieren/Verwalten

Um zu überprüfen, ob ein bestimmtes Modell mit einem anderen verwandt ist, was Sie wollen, wenn ich Sie richtig verstehe, brauchen Sie nur diese winzige Methode, um das Beste aus Eloquent zu machen :

(Implementieren Sie es in BaseModel , Entity oder ein Umfang, was auch immer zu Ihnen passt)

// 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);

Die Magie passiert hier:

/**
 * 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);
}

Hey, aber was ist da los?

isRelatedTo() funktioniert so:

  1. prüfen, ob $id bestanden wurde ist ein Modell oder nur eine ID und bereitet $related vor model und seine $id zur Verwendung im Rückruf. Wenn Sie kein Objekt übergeben, muss Eloquent alle zugehörigen Modelle in $relations instanziieren (relation1.relation2.relation3... )-Kette, um diejenige zu erhalten, an der wir interessiert sind - das passiert in getNestedRelated() , ziemlich einfach.

  2. dann müssen wir so etwas tun:

    // 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. Da wir nicht wissen, wie tief die Beziehung verschachtelt ist, müssen wir die Rekursion verwenden. Wir übergeben jedoch eine Closure an whereHas , also müssen wir einen kleinen Trick anwenden, um sich selbst in seinem Körper aufzurufen (tatsächlich rufen wir es nicht auf, sondern übergeben es als $callback zu whereHas -Methode, da letztere einen Abschluss als 2. Param erwartet) - dies könnte für diejenigen nützlich sein, die Anonyme rekursive PHP-Funktionen :

    // save it to the variable and pass it by reference
    $callback = function () use (&$callback) {
      if (...) // call the $callback again
      else // finish;
    }
    
  4. wir gehen auch zum Abschluss $relations über (jetzt als Array) per Referenz, um die Verschiebung seiner Elemente rückgängig zu machen, und wenn wir sie alle haben (was bedeutet, dass wir whereHas verschachtelt haben). ), setzen wir schließlich das where -Klausel anstelle eines weiteren whereHas , um nach unserem $related zu suchen Modell.

  5. Lassen Sie uns schließlich bool zurückgeben