Laravel – Service, Repository Pattern

Ein wichtiger Baustein der Softwareentwicklung ist die Softwarearchitektur. Ab einer bestimmten Größe steht und fällt, meines Erachtens, ein Projekt mit der Softwarearchitektur. Damit ist nicht gemeint, das prozedural geschriebener Code perse schlecht ist. Im Gegenteil. In der Vergangenheit habe ich mit prozedural geschriebenen Code gute Software gebaut die teilweise bis heute läuft. Ich erinnere mich auch zu gerne an meine Zeit bei der Fincaorca GmbH in Berlin und deren CTO Jens. Dieser hat mit einem weiterem Kollegen 2006 ein Reisebuchungsportal aufgesetzt. Ein sehr großer Teil war dort prozedural geschriebener Code. Wo ich 2016 hinzugestoßen bin, war es ein Mix aus objektorientierten geschrieben Modulen und dem alten prozedural geschriebenen Grundgerüst. Und erst vor kurzem (01/2021!) habe ich erfahren, dass es nun mit einem schönen PHP Framework ersetzt werden soll. Lange Rede, kurzer Sinn. Soll nur heißen, dass eine Software Straight Forward und gut durchdacht sein soll. Pragmatische Ansätze bei wichtigen Entscheidungen, machen das Leben als Softwareentwickler aber auch der Auftraggebers um einiges leichter.

Bei Laravel gibt es von Hause aus nicht das Service Repository Pattern. Jetzt werden einige sagen: Momentmal Controller Model ist doch ein wenig wie Service Repository. Ich sage nein. In den meisten Fällen ist ein Request nicht nur „get-all-users“ (gebe mir alle User) zurück. Sobald eine Komplexität die Abfrage einfärbt ist es Zeit, über ein Service Repository Pattern (SRP) nachzudenken. Aus folgenden Gründen: Spätere Wartung, Änderungen oder Erweiterungen werden definitiv mit diesem Pattern leichter gehen und das bei Gleichzeitiger Fehlerreduktion. Was unterm Strich bedeutet: weniger Stress, mehr Zeit für andere Sachen und Kosten gespart.

Wie geht man also vor?

Stellen wir uns vor:
– wir haben ein eShop
– der Nutzer befindet sich in seinem Backend und will den Preis seiner letzten Rechnung einsehen
– über die Order id wird dieser Prozess angetriggert
– wir gehen von einem XHR Request aus
– die Order soll sich selbst aber auch ihre Order Items und Summe des Einkaufs netto und UST mitliefern. Damit wir eine schöne Auflistung unserer Produkte haben.

Der Order Controller bekommt den Request mit einer OrderID rein. Dieser könnte nun in seiner Logik in der selbigen Methode verarbeitet werden.

$order = Order::find($order_id);   // gebe      
$sum_net = $sum_gross = $vat = 0; // Initialisierung der vars
foreach($order->order_items as $orderItem)  // loop durch die einzelnen OrderItems
{
    $orderItem->product; // hole das zugehörige Produkt, welches zum Order Item in Relation steht, ab
    $sum_net += $orderItem->order_item_price * $orderItem->order_item_quantity; // Summe Netto alle Produkte / Items
    $vat += ((($orderItem->order_item_price * $orderItem->order_item_quantity) * $orderItem->vat) / 100); // Steuer insgesamt
}    
         
$order->sums = ["net"=>$sum_net,"vat"=>$vat,"gross"=>($sum_net+$vat) ];
return $order; 

zurück bekommen wir ein JSON Objekt:

{Order: {}, OrderItems: {}, sums: {net: x,vat: x, gross: x} 

Hmm. Kann man machen, aber ist doch irgendwie ein wenig zu viel Business Logik drin, oder? Ein Controller soll doch nur die Anfrage annehmen und eine Antwort geben. Die Logik sollte dann ein Service übergeben werden. Der Service beinhaltet die Business Logik und arbeitet nur diese Logik ab. Der Service arbeitet aber nicht mit den Modelen zusammen. Den die Datenquellen bzw Datenlogik sind beim Repository verankert. Des Repository kennt die Modele und spricht sie entsprechend an.

Also schreiben wir es nun mal um.

Order Controller

use \App\Service\OrderService;  

public function __construct() 
{
    $this->orderService = new OrderService();
}
 
public function order(Request $requerst, int $id)
{
    $order_id = (int) $id;
    return $orderService→getOrder();
}

Order Service

 
use \App\Service\OrderRepository;
 
public function __construct() 
{
    $this-> orderRepository = new OrderRepository();
}
 
public function getOrder(int $order_id) {
    # hier kommen die Business Logiken rein
    #- Validierung der Order ID
    #- check ob Nutzer die Anfrage überhaupt machen darf (AUTH)
    #- …
    #- hole jetzt aus der Repository die Order ab:
    $order = $orderRepository→getOrderById($id);
    $sum_net = $sum_gross = $vat = 0; // Initialisierung der vars
    foreach($order->order_items as $orderItem)  // loop durch die einzelnen OrderItems
    {
        $orderItem->product; // hole das zugehörige Produkt, welches zum Order Item in  Relation steht, ab
        $sum_net += $orderItem->order_item_price * $orderItem->order_item_quantity; // Summe Netto alle Produkte / Items
        $vat += ((($orderItem->order_item_price * $orderItem->order_item_quantity) * $orderItem->vat) / 100); // Steuer insgesamt
     }            
     $order->sums = ["net"=>$sum_net,"vat"=>$vat,"gross"=>($sum_net+$vat) ];
     return $order;
 } 

Order Repository

use \App\Models\Order;
public function getOrderById(int $id) 
{
    return Order::find($id);
} 

Das wäre jetzt ein einfaches Beispiel. Aber wenn man ein ganzes Projekt in dieser Gangart umsetzt, wird man die Vorteile deutlicher zu spüren bekommen. Klassen lassen sich besser lesen und der wichtigste Punkt zum Schluss. Man kann dieses Pattern hervorragen testen.


Leave a Comment

Your email address will not be published. Required fields are marked *

*

*

Empfholende Artikel


Larvel withCount mit Where Condition

November 18, 2021

Stellt Euch vor ihr habt ein Model Post mit einer Relation Comment. Auf der Übersichtsseite von Post möchtet ihr nur die Anzahl der Kommentare eines jeweiligen Post anzeigen. Dafür gibt es die echt hilfreiche Laravel Funktion withCount(). Post::withCount(‘comments’)->all(); Aber bei Comments kann es ja sein, dass manchen noch approved werden müssen. Diese müssen dann noch […]

Laravel Mailhog Docker

October 20, 2021

Letztens wurde ich gefragt, wie ich Mail bei Laravel teste. Ich meinte ich habe einen SMTP Server den ich nutzen kann. Da meinte er, dass wäre zwar okay aber man könnte sich damit auch eine valide Mailadresse verbrennen. Mit verbrennen meint der Kollege, dass sie global als SPAM Mailadresse angerechnet werden kann. Falls die Tests […]

Meine wichtigsten Artisan Commands

September 6, 2021

Als Laravel Entwickler braucht man sie ständig. Wenn man etwas länger Frontendarbeiten gemacht hat, kommt es schon mal vor, dass man etwas vergessen hat. Daher hier die Liste mit den häufigsten Artisan Commands: php artisan migrate:rollback –step=1 Ihr wollte eine oder x-beliebige Migrationen zurückspulen? Dann rollback und die Anzahl der Migrationssteps eingeben. php artisan migrate:refresh […]

Laravel – Slugs einrichten

September 2, 2021

Slugs machen die URLs Sprechender und damit auch SEO-(Freund)licher. Statt domain.de/posts/1 würde man mit domain.de/posts/mein-erster-blog-eintrag arbeiten. Um das in Laravel zu realisieren, bedient man sich in der Regel externer Bibliotheken. Einmal eine von Spartie und eine cviebrock. Man kann es natürlich aber auch selber machen. Die Bibliotheken sind hier aber vorzuziehen. Weil es ist keine […]

Laravel Blade Components

August 27, 2021

Componets – Aus VueJS kennen wir es und haben es bereits lieben gelernt. In Laravel können wir im Blade auch sehr dynamisch in Componenten arbeiten. Von Hause gibt uns Laravel Componets mit. Am Beispiel erklärt Stellen wir uns vor, wir wollen ein Blog erstellen. In der Übersichtsseite, sollen die Blogartikel untereinander gelistet werden. Wer ohne […]

Laravel Resource – Kurz mal erklärt

August 26, 2021

Nutzen wir zum Beispiel Larvel als Backend und beliefern das Frontend via api Route mit Daten, dann stellt sich manchmal die Frage, welche Daten braucht das Frontend eigentlich? Braucht man wirklich created_at oder updated_at mit auszuliefern? Sobald man sich so eine oder ähnliche Frage stellt wird es Zeit sich mal mit Laravel Resource auseinander zu […]