1unitedpower: Wie Properties aus externen Klassen in Trait bekannt machen?

Beitrag lesen

Ich will jetzt (noch) nicht mit dem ganzen Forum diskutieren, ob diese Vorstellung gut oder schlecht ist.

Tja :P Ich hoffe du lässt dich trotzdem auf mich ein.

Der Sinn solcher Wrapper ist es, Datenbankabfragen unabhängig von einer konkreten Datenbank zu formulieren.

Der Vorteil ergibt sich, wenn man mit verschiedenen Datenbank-Management-Systemenen zu arbeiten hat. Dann kann man nämlich das Wissen, das man in einem Projekt über die Schnittstelle erworben hat, in anderen Kontexten wiederverwenden. Dafür würde sich aber PHPs eigene PDO-Schnittstelle schon qualifiezieren, die abstrahiert ja ebenfalls schon von konkreten DBMS.

Es gibt darüber hinaus auch direkte Vorteile bei der Arbeit in einem konkreten Projekt. PDO und mysqli arbeiten auf einer sehr primitiven Ebene, SQL-Statements werden durch Strings repräsentiert. Das kann schnell ins Auge gehen, etwa wenn ein String einen SQL-Syntax-Fehler enthält oder wenn das Statement nicht mit dem Datenbank-Schema kompatibel ist. Das erste Problem ist heute durch intelligente Editoren größtenteils gelöst. Das zweite Problem ist schwieriger, dafür ist eine präzisere Repräsentation für das Statement und für das Datenbank-Schema notwendig.

Also eigentlich nichts anderes als ein ORM Tool, was es schon reichlich fertig gibt.

Ein ORM ist nochmal eine Abstraktions-Ebene darüber. Für mich ist das defnierende Charaktermerkmal, dass ein ORM Domäne-getrieben arbeitet: Die Programmiererin arbeitet ausschließlich mit ihrem Domain-Model und ruft das ORM nur an, wenn sie die flüchtige Repräsentation im Hauptspeicher mit der persistenten Datenhaltung synchronisieren möchte. Intern generiert das ORM das dafür notwendige Datenbank-Schema und die notwendigen Querys. Das ist zumindest die ideale Sicht, die ich auf ein ORM habe. In der Praxis ist die Trennung oft nicht so glasklar.

Das System von MB arbeitet dagegen Datenbank-getrieben, er geht von keinem Domain-Modell aus, sondern möchte es dem Programmierer erleichtern, beliebige Datenbank-Abfragen zu verfassen.

Das ist überhaupt nicht trivial, eine ResultExpression kann einfach eine Referenz auf eine Spalte sein, aber auch ein Subselect. Eine SourceTable kann eine Tabelle sein, oder ein Subselect, oder ein Join. Die FilterCollection (WHERE) ist ein Alptraum, wenn Du beliebige Bedingungen zulassen willst. Vermutlich brauchst Du beliebig zusammenstöpselbare AND- und OR-Gruppen, in denen Bedingungen stecken. Eine Bedingung ist ein LIKE, ein IN, ein EXIST, oder =. Die Werte für Bedingungen sind einfache Werte (Spalten, Konstanten), mathematische Ausdrücke, oder Subselects.

Das ist im Grunde genommen nicht besonders schwierig, nur sau viel Arbeit, weil die SQL so umfangreich ist. Man kann sich von der SQL-Grammatik leiten lassen und einen Datentypen für den abstrakten Syntax-Baum erstellen. Hat man die Grammatik bspw. in BNF vorliegen, kann man für jedes Nicht-Terminal-Symbol eine Klasse erstellen, die einen (inneren) Knoten im Syntax-Baum repräsentiert. Terminal-Symbole werden im Syntax-Baum zu Blättern und bekommen ebenfalls jeweils eine eigene Klasse. Nicht-Terminale, die mehrfach auf der linken Seite einer Produktions-Regel auftauchen, können ein gemeinsames Interface implementieren. So kann man PHPs Typsystem benutzen, um die Wohlgeformtheit der Bäume sicherzustellen.

Wie soll sowas dann am Ende benutzt werden?

Zum Beispiel mit einer funktionalen Schnittstelle, eine Factory-Funktion pro Knoten und Blatt. Dein Beispiel einmal aufgegriffen, könnte das bspw. so aussehen:

$a = reference();
$b = reference();
$x = reference();
$select = select(
    [
        field($a, 'foo'),
        field($a, 'bar'),
        field($b, 'foo')
    ],
    from (
        as_(
            join(
                as_(table('table1'), $a),
                as_(table('table2'), $b),
                on(
                    equals(
                        field($a, 'id'),
                        field($b, 'id')
                    )
                )
            ),
            $x
        )
    ),
    where (
        greater(
            field($a, 'foo'),
            integer_(47)
        )
    )
);

Bei den Aliassen habe ich in die Trickkiste gegriffen: Aliasse modellieren eigentlich Querverbindungen innerhalb des Syntax-Baums. In meiner Darstellung oben, gehe ich deshalb von einem abstraken Syntax-Graphen auf. Die Querverbindungen werden über PHP-Variablen modelliert. Das muss man nicht so machen, aber ich finde es ganz nett, weil es sichtbar macht, dass der vergebne Name eines Alias immateriell ist, also keine eigene Stuktur-Information trägt.