Kurz zu den Spezifikationen:
Eine Spezifikation ist ein Entwurfsmuster, mit dem Sie die Regeln der Geschäftslogik in Form einer Kette von Objekten widerspiegeln können, die durch boolesche Logikoperationen verbunden sind. Mit den Spezifikationen können Sie doppelte, ähnliche Methoden im Repository entfernen und die Geschäftslogik duplizieren.
Heute gibt es zwei erfolgreiche und beliebte PHP-Projekte (wenn Sie andere Projekte kennen, schreiben Sie bitte in die Kommentare) , mit denen Sie Geschäftsregeln in Spezifikationen beschreiben und Datensätze filtern können. Dies sind die Spezifikationen von RulerZ und Happyr Doctrine . Beide Projekte sind leistungsstarke Werkzeuge mit ihren Vor- und Nachteilen. Beim Vergleich dieser Projekte wird ein ganzer Artikel gezeichnet. Hier möchte ich Ihnen sagen, was uns die neue Version in der Doctrine Specification gebracht hat.
Kurz über die Doctrine Specification
Wer mit dem Projekt mehr oder weniger vertraut ist, kann diesen Abschnitt sicher überspringen .
Mit Hilfe dieses Projekts können Sie Spezifikationen in Form von Objekten beschreiben, diese aus der Komposition zusammensetzen und dadurch komplexe Geschäftsregeln erstellen. Die resultierenden Zusammensetzungen können frei wiederverwendet und zu noch komplexeren Zusammensetzungen kombiniert werden, die leicht zu testen sind. Die Doctrine-Spezifikation wird verwendet, um Doctrine-Abfragen zu erstellen. Im Wesentlichen ist die Doctrine-Spezifikation die Abstraktionsebene über den Doctrine ORM QueryBuilder und die Doctrine ORM Query.
Die Spezifikationen gelten über das Doctrine Repository:
$result = $em->getRepository(MyEntity::class)->match($spec);
Die Spezifikation kann manuell angewendet werden, ist jedoch nicht besonders praktisch und auf lange Sicht sinnlos. $spec = ... $alias = 'e'; $qb = $em->getRepository(MyEntity::class)->createQueryBuilder($alias); $spec->modify($qb, $alias); $filter = (string) $spec->getFilter($qb, $alias); $qb->andWhere($filter); $result = $qb->getQuery()->execute();
Es gibt verschiedene Methoden im Repository:
match
- alle Ergebnisse erhalten, die der Spezifikation entsprechen;matchSingleResult
- Äquivalent zu Query::getSingleResult()
;matchOneOrNullResult
- entspricht matchSingleResult
, lässt jedoch null
;getQuery
- getQuery
einen QueryBuilder, indem eine Spezifikation darauf angewendet wird, und gibt ein Query-Objekt daraus zurück.
Kürzlich wurde ihnen die Methode getQueryBuilder
hinzugefügt, mit der ein QueryBuilder erstellt und unter Anwendung der Spezifikation zurückgegeben wird.
Das Projekt identifiziert verschiedene Arten von Spezifikationen:
Logische Spezifikationen
Die andX
und orX
dienen auch als Sammlung von Spezifikationen.
Spec::andX()
Spec::orX()
Spec::not()
Es ist üblich, Objekte mit Bibliotheksspezifikationen über die Spezifikationsfassade zu installieren, dies ist jedoch nicht erforderlich. Sie können das Spezifikationsobjekt explizit instanziieren:
new AndX(); new OrX(): new Not();
Filterspezifikation
Filterspezifikationen bilden tatsächlich die Regeln der Geschäftslogik und werden in der WHERE
Anforderung verwendet. Dazu gehören Vergleichsoperationen:
isNull
- SQL ist IS NULL
äquivalentisNotNull
- SQL ist IS NOT NULL
äquivalentin
- äquivalent zu IN ()
notIn
- NOT IN ()
äquivalenteq
- Gleichheitstest =
neq
- auf Ungleichheit prüfen !=
lt
weniger als <
lte
- kleiner oder gleich <=
gt
- mehr als >
gte
- größer oder gleich >=
like
- SQL LIKE
ÄquivalentinstanceOfX
- Äquivalent zu DQL INSTANCE OF
Ein Beispiel für die Verwendung von Filterspezifikationen:
$spec = Spec::andX( Spec::eq('ended', 0), Spec::orX( Spec::lt('endDate', new \DateTime()), Spec::andX( Spec::isNull('endDate'), Spec::lt('startDate', new \DateTime('-4 weeks')) ) ) );
Abfragemodifikatoren
Abfragemodifikatoren haben nichts mit Geschäftslogik und Geschäftsregeln zu tun. Wie der Name schon sagt, ändern sie nur QueryBuilder. Der Name und der Zweck der vordefinierten Modifikatoren entsprechen ähnlichen Methoden in QueryBuilder.
join
leftJoin
innerJoin
limit
offset
orderBy
groupBy
having
Ich möchte den slice
Modifikator separat notieren. Es kombiniert die Funktionen limit
und offset
und berechnet den Offset basierend auf der Größe des Slice und seiner Seriennummer. Bei der Implementierung dieses Modifikators waren wir mit dem Autor des Projekts nicht einverstanden. Mit der Erstellung eines Modifikators verfolgte ich das Ziel, die Konfiguration von Spezifikationen während der Paginierung zu vereinfachen. In diesem Zusammenhang sollte die erste Seite mit der Seriennummer 1 dem ersten Slice mit der Seriennummer 1 entsprechen. Der Autor des Projekts hielt es jedoch für richtig, die Zählung im Programmierstil zu starten, dh 0. Daher sollten Sie angeben, dass Sie angeben müssen, wenn Sie das erste Slice benötigen 0 als Seriennummer.
Ergebnismodifikatoren
Ergebnismodifikatoren existieren geringfügig außerhalb der Spezifikationen. Sie gelten für Doctrine Query. Die folgenden Modifikatoren steuern die Query::setHydrationMode()
):
asArray
asSingleScalar
asScalar
Der cache
Modifikator steuert das Caching des Abfrageergebnisses.
Wir sollten auch den Modifikator roundDateTimeParams
. Es hilft bei der Lösung von Caching-Problemen, wenn Sie mit Geschäftsregeln arbeiten müssen, bei denen einige Werte mit der aktuellen Zeit verglichen werden müssen. Dies sind normale Geschäftsregeln, aber aufgrund der Tatsache, dass die Zeit keine Konstante ist, funktioniert das Zwischenspeichern für mehr als eine Sekunde für Sie nicht. Der Modifikator roundDateTimeParams
entwickelt, um dieses Problem zu lösen. Es durchläuft alle Parameter der Anforderung, sucht nach dem darin enthaltenen Datum und rundet es auf den angegebenen Wert ab. Dadurch erhalten wir Datumswerte, die immer ein Vielfaches eines Werts sind, und wir werden das Datum in Zukunft nicht mehr erhalten. Das heißt, wenn wir die Anforderung 10 Minuten zwischenspeichern möchten, verwenden wir Spec::cache(600)
und Spec::roundDateTimeParams(600)
. Ursprünglich wurde vorgeschlagen, diese beiden Modifikatoren der Einfachheit halber zu kombinieren, es wurde jedoch beschlossen, sie für SRP zu trennen.
Eingebettete Spezifikationen
Die Happyr Doctrine-Spezifikation verfügt über eine separate Schnittstelle für Spezifikationen, die einen Filter und einen Anforderungsmodifikator kombiniert. Die einzige vordefinierte Spezifikation ist countOf
der Sie die Anzahl der Entitäten ermitteln können, die der Spezifikation entsprechen. Um Ihre eigenen Spezifikationen zu erstellen, ist es üblich, die abstrakte BaseSpecification
Klasse zu erweitern.
Innovationen
Dem Repository wurden neue Methoden hinzugefügt:
matchSingleScalarResult
- Äquivalent zu Query::getSingleScalarResult()
;matchScalarResult
- entspricht Query::getScalarResult()
;iterate
ist das Äquivalent von Query::iterate()
.
Die MemberOfX
Spezifikation wird MemberOfX
- das DQL-Äquivalent von MEMBER OF
und der Abfragemodifikator indexBy
werden indexBy
- das Äquivalent von QueryBuilder::indexBy()
.
Operanden
Die neue Version führt das Konzept des Operanden ein . Alle Bedingungen in den Filtern bestehen aus linken und rechten Operanden und einem Operator dazwischen.
<left_operand> <operator> <right_operand>
In früheren Versionen konnte der linke Operand nur ein Entitätsfeld und der rechte Operand nur ein Wert sein. Dies ist ein einfacher und effektiver Mechanismus, der für die meisten Aufgaben ausreicht. Gleichzeitig werden bestimmte Einschränkungen auferlegt:
- Funktionen können nicht verwendet werden;
- Aliase können nicht für Felder verwendet werden.
- Es ist unmöglich, zwei Felder zu vergleichen.
- Es ist unmöglich, zwei Werte zu vergleichen;
- Arithmetische Operationen können nicht verwendet werden.
- Datentyp für Wert kann nicht angegeben werden.
In der neuen Version werden Operandenobjekte in Argumenten an die Filter übergeben und ihre Transformation in DQL an die Operanden selbst delegiert. Dies eröffnet viele Möglichkeiten und erleichtert Filter.
Feld und Wert
Um die Abwärtskompatibilität aufrechtzuerhalten, wird das erste Argument in den Filtern in einen Feldoperanden konvertiert, wenn es kein Operand ist, und das letzte Argument wird in einen Wertoperanden konvertiert. Daher sollten Sie keine Probleme beim Aktualisieren haben.
Sie können 2 Felder vergleichen:
Sie können 2 Felder verschiedener Entitäten vergleichen:
Arithmetische Operationen
Unterstützung für Standard-Arithmetikoperationen hinzugefügt -
, +
, *
, /
, %
. Betrachten Sie beispielsweise die Berechnung von Benutzerpunkten:
Arithmetische Operationen können ineinander verschachtelt werden:
Funktionen
Die neue Version fügte Operanden mit Funktionen hinzu. Sie können als statische Methoden der Spec
Klasse oder über die Spec::fun()
-Methode verwendet werden.
Funktionen können ineinander verschachtelt werden:
Argumente für Funktionen können als separate Argumente oder durch Übergabe in einem Array übergeben werden:
Probenahmeverwaltung
Manchmal müssen Sie eine Liste von Rückgabewerten verwalten. Zum Beispiel:
- Fügen Sie dem Ergebnis eine weitere Entität hinzu, um keine Unterabfragen zum Abrufen der Links durchzuführen.
- Nicht die gesamte Entität zurückgeben, sondern nur eine Reihe separater Felder;
- Verwenden Sie Aliase.
- Verwenden Sie versteckte Aliase mit Sortierbedingungen (es erfordert Doctrine, aber sie versprechen, es zu beheben ).
Vor Version 0.8.0 mussten für diese Aufgaben Spezifikationen für diese Anforderungen erstellt werden. Ab Version 0.8.0 können Sie die Methode getQueryBuilder()
verwenden und die Auswahl über die QueryBuilder-Oberfläche verwalten.
Die neue Version 1.0.0 fügt select
Anforderungsmodifikatoren select
und addSelect
hinzu. select
ersetzt die Liste der auswählbaren Werte vollständig und addSelect
fügt der Liste neue Werte hinzu. Als Wert können Sie ein Objekt verwenden, das die Selection
oder einen Filter implementiert. Auf diese Weise können Sie die Funktionen der Bibliothek an Ihre Anforderungen anpassen. Betrachten Sie die Möglichkeiten, die bereits vorhanden sind.
Sie können ein Feld auswählen:
Sie können der Auswahl ein Feld hinzufügen:
Sie können mehrere Felder auswählen:
Sie können den zurückgegebenen Werten eine Entität hinzufügen:
Sie können Aliase für auswählbare Felder verwenden:
Sie können der Auswahl ausgeblendete Felder hinzufügen:
Sie können beispielsweise Ausdrücke verwenden, um einen Rabatt auf ein Produkt zu erhalten:
Sie können Aliase in den Spezifikationen verwenden:
Das ist im Grunde alles. Hier enden die Innovationen. Die neue Version hat viele interessante und nützliche Funktionen gebracht. Ich hoffe sie haben dich interessiert.
PS: Ich kann anhand eines Beispiels die Verwendung von Spezifikationen analysieren und die Vor- und Nachteile ihrer Verwendung aufzeigen. Wenn dies für Sie interessant ist, schreiben Sie in die Kommentare oder in PM.