Kurze Antwort
Die dritte Option: Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
$teamMorphType = Relation::getMorphedModel('team');
$groupMorphType = Relation::getMorphedModel('group');
$formMorphType = Relation::getMorphedModel('form');
$permissible = [
$teamMorphType => [$user->team_id],
$groupMorphType => [],
$formMorphType => [],
];
foreach ($user->permissible as $permissible) {
switch ($permissible->permissible_type) {
case $teamMorphType:
case $groupMorphType:
case $formMorphType:
$permissible[$permissible->permissible_type][] = $permissible->permissible_id;
break;
}
}
$forms = Form::query()
->where('user_id', '=', $user->id)
->orWhereIn('id', $permissible[$fromMorphType])
->orWhereIn('team_id', $permissible[$teamMorphType])
->orWhereIn('group_id', $permissible[$groupMorphType])
->get();
Lange Antwort
Einerseits ist (fast) alles, was Sie in Code tun können, in Bezug auf die Leistung besser als in Abfragen.
Andererseits würde das Abrufen von mehr Daten aus der Datenbank als erforderlich bereits zu viele Daten sein (RAM-Nutzung usw.).
Aus meiner Sicht brauchen Sie etwas dazwischen, und nur Sie werden wissen, wo das Gleichgewicht sein würde, abhängig von den Zahlen.
Ich würde vorschlagen, mehrere Abfragen auszuführen, die letzte Option, die Sie vorgeschlagen haben ( Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
):
- Fragen Sie alle Bezeichner für alle Berechtigungen ab (5 Abfragen).
- Das Zusammenführen aller Formulare führt zum Speicher und zu eindeutigen Werten
array_unique($ids)
- Fragen Sie das Formularmodell mit den Bezeichnern in einer IN () - Anweisung ab.
Sie können die drei von Ihnen vorgeschlagenen Optionen ausprobieren und die Leistung überwachen, indem Sie die Abfrage mit einem Tool mehrmals ausführen. Ich bin jedoch zu 99% sicher, dass die letzte Option die beste Leistung bietet.
Dies kann sich auch stark ändern, je nachdem, welche Datenbank Sie verwenden, aber wenn wir zum Beispiel über MySQL sprechen; In einer sehr großen Abfrage werden mehr Datenbankressourcen verwendet, die nicht nur mehr Zeit als einfache Abfragen aufwenden, sondern auch die Tabelle vor Schreibvorgängen schützen. Dies kann zu Deadlock-Fehlern führen (es sei denn, Sie verwenden einen Slave-Server).
Wenn andererseits die Anzahl der Formular-IDs sehr groß ist, können Fehler für zu viele Platzhalter auftreten. Daher möchten Sie die Abfragen möglicherweise in Gruppen von beispielsweise 500 IDs aufteilen (dies hängt stark von der Grenze ab ist in der Größe, nicht in der Anzahl der Bindungen) und führen die Ergebnisse im Speicher zusammen. Selbst wenn Sie keinen Datenbankfehler erhalten, können Sie auch einen großen Leistungsunterschied feststellen (ich spreche immer noch über MySQL).
Implementierung
Ich gehe davon aus, dass dies das Datenbankschema ist:
users
- id
- team_id
forms
- id
- user_id
- team_id
- group_id
permissible
- user_id
- permissible_id
- permissible_type
Zulässig wäre also eine bereits konfigurierte polymorphe Beziehung .
Daher wären die Beziehungen:
- Besitzt Formular:
users.id <-> form.user_id
- Team besitzt Form:
users.team_id <-> form.team_id
- Hat Berechtigungen für eine Gruppe, die ein Formular besitzt:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Team'
- Hat Berechtigungen für ein Team, das ein Formular besitzt:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Group'
- Hat die Erlaubnis zu einem Formular:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\From'
Version vereinfachen:
$teamMorphType = Relation::getMorphedModel('team');
$groupMorphType = Relation::getMorphedModel('group');
$formMorphType = Relation::getMorphedModel('form');
$permissible = [
$teamMorphType => [$user->team_id],
$groupMorphType => [],
$formMorphType => [],
];
foreach ($user->permissible as $permissible) {
switch ($permissible->permissible_type) {
case $teamMorphType:
case $groupMorphType:
case $formMorphType:
$permissible[$permissible->permissible_type][] = $permissible->permissible_id;
break;
}
}
$forms = Form::query()
->where('user_id', '=', $user->id)
->orWhereIn('id', $permissible[$fromMorphType])
->orWhereIn('team_id', $permissible[$teamMorphType])
->orWhereIn('group_id', $permissible[$groupMorphType])
->get();
Detaillierte Version:
// Owns Form
// users.id <-> forms.user_id
$userId = $user->id;
// Team owns Form
// users.team_id <-> forms.team_id
// Initialise the array with a first value.
// The permissions polymorphic relationship will have other teams ids to look at
$teamIds = [$user->team_id];
// Groups owns Form was not mention, so I assume there is not such a relation in user.
// Just initialise the array without a first value.
$groupIds = [];
// Also initialise forms for permissions:
$formIds = [];
// Has permissions to a group that owns a Form
// permissible.user_id <-> users.id && permissible.permissible_type = 'App\Team'
$teamMorphType = Relation::getMorphedModel('team');
// Has permissions to a team that owns a Form
// permissible.user_id <-> users.id && permissible.permissible_type = 'App\Group'
$groupMorphType = Relation::getMorphedModel('group');
// Has permission to a Form
// permissible.user_id <-> users.id && permissible.permissible_type = 'App\Form'
$formMorphType = Relation::getMorphedModel('form');
// Get permissions
$permissibles = $user->permissible()->whereIn(
'permissible_type',
[$teamMorphType, $groupMorphType, $formMorphType]
)->get();
// If you don't have more permissible types other than those, then you can just:
// $permissibles = $user->permissible;
// Group the ids per type
foreach ($permissibles as $permissible) {
switch ($permissible->permissible_type) {
case $teamMorphType:
$teamIds[] = $permissible->permissible_id;
break;
case $groupMorphType:
$groupIds[] = $permissible->permissible_id;
break;
case $formMorphType:
$formIds[] = $permissible->permissible_id;
break;
}
}
// In case the user and the team ids are repeated:
$teamIds = array_values(array_unique($teamIds));
// We assume that the rest of the values will not be repeated.
$forms = Form::query()
->where('user_id', '=', $userId)
->orWhereIn('id', $formIds)
->orWhereIn('team_id', $teamIds)
->orWhereIn('group_id', $groupIds)
->get();
Verwendete Ressourcen:
Datenbankleistung:
- Abfragen an die Datenbank (ohne den Benutzer): 2 ; eine, um das Zulässige zu bekommen, und eine andere, um die Formulare zu bekommen.
- Keine Joins !!
- Die minimal möglichen OPs (
user_id = ? OR id IN (?..) OR team_id IN (?...) OR group_id IN (?...)
.
PHP, im Speicher, Leistung:
- foreach Schleifen der zulässigen mit einem Schalter Inneren.
array_values(array_unique())
um zu vermeiden, dass die IDs wiederholt werden.
- Im Speicher, 3 Anordnungen von IDs (
$teamIds
, $groupIds
,$formIds
)
- Im Speicher relevante Berechtigungen beredte Sammlung (diese kann bei Bedarf optimiert werden).
Vor-und Nachteile
PROS:
- Zeit : Die Summe der Zeiten einzelner Abfragen ist kürzer als die Zeit einer großen Abfrage mit Verknüpfungen und ODER.
- DB-Ressourcen : Die MySQL-Ressourcen, die von einer Abfrage mit Join- und / oder Anweisungen verwendet werden, sind größer als die Summe, die von der Summe ihrer separaten Abfragen verwendet wird.
- Geld : Weniger Datenbankressourcen (Prozessor, RAM, Lesen von Datenträgern usw.), die teurer sind als PHP-Ressourcen.
- Schlösser : Wenn Sie keinen schreibgeschützten Slave-Server abfragen, führen Ihre Abfragen zu weniger Lesesperren für Zeilen (die Lesesperre wird in MySQL gemeinsam genutzt, sodass kein weiterer Lesevorgang gesperrt wird, aber kein Schreibvorgang blockiert wird).
- Skalierbar : Mit diesem Ansatz können Sie weitere Leistungsoptimierungen vornehmen, z. B. die Abfragen aufteilen.
Nachteile:
- Code-Ressourcen : Durch Berechnungen im Code und nicht in der Datenbank werden offensichtlich mehr Ressourcen in der Code-Instanz, insbesondere aber im RAM, verbraucht, wodurch die mittleren Informationen gespeichert werden. In unserem Fall wäre dies nur eine Reihe von IDs, was eigentlich kein Problem sein sollte.
- Wartung : Wenn Sie die Eigenschaften und Methoden von Laravel verwenden und Änderungen an der Datenbank vornehmen, ist die Aktualisierung des Codes einfacher als bei expliziteren Abfragen und Verarbeitungen.
- Übermaß? : In einigen Fällen kann die Optimierung der Leistung zu viel des Guten sein, wenn die Daten nicht so groß sind.
So messen Sie die Leistung
Einige Hinweise zur Messung der Leistung?
- Langsame Abfrageprotokolle
- ANALYSETABELLE
- TABELLENSTATUS WIE ANZEIGEN
- EXPLAIN ; Erweitertes EXPLAIN-Ausgabeformat ; mit EXPLAIN ; Ausgabe erklären
- WARNHINWEISE ANZEIGEN
Einige interessante Profiling-Tools: