[Update] Laravel: Aufruf von Datenbank-Prozeduren
Laravel ist an sich ein gutes Framework zur Erstellung von Projekten aller Art. Aber es hat natürlich, wie alle anderen auch, Schwächen.
Oft finden sich Schwächen im Bereich des Umgangs mit Datenbanken und so ist es durchaus auch bei der von Laravel verwendeten Implementierung namens „Eloquent“.
Ausgangspunkt
Ausgangspunkt für meine Betrachtung ist ein gewöhnliches, relationales Datenbanksystem. Ich verwende MariaDB, welche in vielen Punkten noch immer ähnlich dem von MySQL funktioniert, doch weder sind sie binär gleich, noch beinhaltet MySQL den Umfang von Funktionen einer aktuellen MariaDB-Version.
Beruflich gehe ich mit Massen-Daten um und ich sage bewusst nicht „BigData“, weil dieser Ausdruck an vielen Stellen gebraucht wird, an denen er (meist schon von der Datenmenge her) nichts zu suchen hat, bloß weil er den Inhalt einer Excel-Tabelle übersteigt.
Das bedeutet schlicht, dass mein Ansatz für ein Projekt immer aus der Datenbank heraus entsteht und die Anzeige auf einer Webseite oder in Reports nur das Ende der Verarbeitungskette darstellt.
Web-Frontends sollten mit simplen Abfragen gefüllt werden können und stellen gegebenenfalls Auswahlkriterien zur Auswahl dieser Daten bereit oder nehmen ein paar Grunddaten auf, die außerhalb des Web-Frontends weiter verarbeitet werden müssen.
Was Eloquent will
Der Grundansatz von Eloquent ist durchaus toll: Man braucht im Prinzip eine Model-Klasse, die auch weiter keinen Inhalt benötigt, wenn man sich an die Erwartungen von Eloquent hält.
Dann kann man mit sehr einfachen, kurzen Befehlen Datenbankabfragen mittels der im Framework definierten Datenbankverbindung durchführen.
Nur muss der Name der Tabelle in der Datenbank dann der Mehrzahl des Klassen-Namens entsprechen, also etwa suchte das System bei dem Klassennamen „Car“ nach einer Tabelle namens „cars“.
Diese Tabelle muss einen numerischen Primary Key enthalten und am Ende zwei Timestamp-Spalten, so könnte man sie auch aus dem Framework heraus generieren.
Kurz: Mit diesem Standard lässt sich im professionellen Bereich kaum umgehen, in dem z.B. unterschiedliche Namenskonventionen für Klassen im Framework und Tabellen in Datenbanken bestehen.
Aber zum Glück lässt sich dass in den Model-Klassen definieren…
Eloquent und Prozeduren
Der vorherige Exkurs diente vor allem der Illustration dessen, was Eloquent dann auch bietet:
einfache Befehle wie, um beim vorherigen Beispiel zu bleiben , etwa Car::all() um die komplette Tabelle abzufragen.
Auch um die Auswahl des Ergebnisses auf alle möglichen Arten einzuschränken oder Fremdschlüssel-Beziehungen im Ergebnis abzubilden gibt es Werkzeuge und Methoden.
Aber es gibt keine Möglichkeit aus Eloquent eine auf der Datenbank gelagerte Prozedur zu starten.
Warum Prozedur?
Prozeduren sind im Prinzip entweder mindestens Sammlungen von Datenbank-Aktionen oder Abläufe von bestimmten Datenbank-Aktion mit dem Aufruf weiterer Prozeduren um eine Verarbeitung oder Prüfung von Daten zu gewährleisten, die aus mehreren Einzelvorgängen besteht.
Es geht also um mehr als nur eine simple Zeile in eine Tabelle zu schreiben.
Beispielweise trägt man also nicht nur den neuen Nutzer in die Tabelle der Nutzer einer Anwendung ein, was Eloquent bis dahin ja schon erledigt hat, sondern weist ihn vielleicht noch Berechtigungen zu, summiert Nutzer-Statistiken neu etc. oder checkt zusätzliche Daten auf Richtigkeit.
Was immer man so braucht und sich ausdenkt und die Datenbank eindeutig besser kann.
Dafür muss man aber in Laravel auf den Query-Builder zurückgreifen, der ebenfalls keine eigene Funktion für den Aufruf von Prozeduren kennt, aber immerhin Prepared Statements beherrscht und kann dann die Prozedur wie folgt einfach ausführen:
DB::raw('call rwn_aktualisieren(?)',[$req['pwt_datum']]);
Hier im Beispiel, ihr habt es ja längst erkannt, wird eine Prozedur mit einem Parameter, der aus dem Request entnommen wurde, angeworfen und aktualisiert hier einige Tabellen (sofern es die Daten erfordern), die von verschiedenen Reports benötigt werden.
Ärgerlich ist es halt, dass sonst alles so schick objektorientiert verläuft und man hier SQL im Klartext servieren muss und keine Rückgabe erhält, da der Befehl ein DB-Result-Set als Ergebnis erwartet.
[Update] Wie genau der Aufruf der Prozedur zu schreiben ist, hängt mitunter auch von der verwendeten Laravel-Version ab.
In manchen Versionen funktioniert es wie oben beschreiben, in anderen Kontexten muss man das ganze noch in einem Select-Befehl verstecken:
DB::connection('mydatabase')->select(DB::raw('call rwn_aktualisieren('.$x.')'));
Bei dieser Schreibweise funktioniert dann aber die Übergabe eines Parameter-Arrays nicht mehr, man muss sie dann einfach wie oben direkt einsetzen.