Nehmen wir an, wenn ich eine CRUD-Operation durchführe oder eine Beziehung auf eine bestimmte Weise ändere, möchte ich auch etwas anderes tun. Wenn beispielsweise jemand einen Beitrag veröffentlicht, möchte ich auch etwas für die Analyse in einer Tabelle speichern. Vielleicht nicht das beste Beispiel, aber im Allgemeinen gibt es viele dieser "gruppierten" Funktionen.
Normalerweise sehe ich diese Art von Logik in Controllern. Das ist alles in Ordnung, bis Sie diese Funktionalität an vielen Stellen reproduzieren möchten. Wenn Sie anfangen, Partials zu erstellen, eine API zu erstellen und Dummy-Inhalte zu generieren, wird es zu einem Problem, die Dinge trocken zu halten.
Die Möglichkeiten, wie ich dies verwalten kann, sind Ereignisse, Repositorys, Bibliotheken und das Hinzufügen zu Modellen. Hier sind meine Erkenntnisse von jedem:
Dienste: Hier würden die meisten Leute diesen Code wahrscheinlich einfügen. Mein Hauptproblem bei Diensten ist, dass es manchmal schwierig ist, bestimmte Funktionen in ihnen zu finden, und ich habe das Gefühl, dass sie vergessen werden, wenn sich die Leute auf die Verwendung von Eloquent konzentrieren. Woher weiß ich, dass ich eine Methode publishPost()
in einer Bibliothek aufrufen muss, wenn ich das nur kann $post->is_published = 1
?
Die einzige Bedingung, unter der dies gut funktioniert, ist, wenn Sie NUR Dienste verwenden (und Eloquent im Idealfall von Controllern insgesamt irgendwie unzugänglich machen).
Letztendlich scheint dies nur eine Reihe zusätzlicher unnötiger Dateien zu erzeugen, wenn Ihre Anforderungen im Allgemeinen Ihrer Modellstruktur folgen.
Repositories: Soweit ich weiß, ist dies im Grunde genommen wie ein Dienst, aber es gibt eine Schnittstelle, über die Sie zwischen ORMs wechseln können, die ich nicht benötige.
Ereignisse: Ich sehe dies in gewisser Hinsicht als das eleganteste System, da Sie wissen, dass Ihre Modellereignisse immer mit eloquenten Methoden aufgerufen werden, sodass Sie Ihre Controller so schreiben können, wie Sie es normalerweise tun würden. Ich kann sehen, dass diese unordentlich werden, und wenn jemand Beispiele für große Projekte hat, die Ereignisse für die kritische Kopplung verwenden, würde ich es gerne sehen.
Modelle: Traditionell hatte ich Klassen, die CRUD durchführten und auch die kritische Kopplung handhabten. Dies machte die Sache tatsächlich einfach, da Sie wussten, dass alle Funktionen rund um CRUD +, was auch immer damit getan werden musste, vorhanden waren.
Einfach, aber in der MVC-Architektur ist dies normalerweise nicht das, was ich getan sehe. In gewissem Sinne bevorzuge ich dies jedoch gegenüber Diensten, da es etwas einfacher zu finden ist und weniger Dateien zu verfolgen sind. Es kann allerdings etwas unorganisiert werden. Ich würde gerne die Nachteile dieser Methode hören und warum die meisten Leute es nicht zu tun scheinen.
Was sind die Vor- und Nachteile jeder Methode? Vermisse ich etwas
quelle
Antworten:
Ich denke, alle Muster / Architekturen, die Sie präsentieren, sind sehr nützlich, solange Sie den SOLID- Prinzipien folgen .
Für das Hinzufügen von Logik halte ich es für wichtig, auf das Prinzip der Einzelverantwortung zu verweisen . Meine Antwort geht auch davon aus, dass Sie an einem mittleren / großen Projekt arbeiten. Wenn es sich um ein Projekt handelt, das auf eine Seite geworfen wird , vergessen Sie diese Antwort und fügen Sie alles Controllern oder Modellen hinzu.
Die kurze Antwort lautet: Wo es für Sie sinnvoll ist (mit Dienstleistungen) .
Die lange Antwort:
Controller : Welche Verantwortung haben Controller? Sicher, Sie können Ihre gesamte Logik in einen Controller einfügen, aber liegt das in der Verantwortung des Controllers? Das glaube ich nicht.
Für mich muss der Controller eine Anfrage erhalten und Daten zurückgeben. Dies ist nicht der Ort, an dem Validierungen vorgenommen, Datenbankmethoden usw. aufgerufen werden können.
Modelle : Ist dies ein guter Ort, um Logik wie das Senden einer Begrüßungs-E-Mail hinzuzufügen, wenn sich ein Benutzer registriert oder die Stimmenzahl eines Beitrags aktualisiert? Was ist, wenn Sie dieselbe E-Mail von einer anderen Stelle in Ihrem Code senden müssen? Erstellen Sie eine statische Methode? Was ist, wenn diese E-Mails Informationen von einem anderen Modell benötigen?
Ich denke, das Modell sollte eine Entität darstellen. Mit Laravel, verwende ich nur die Modellklasse Dinge hinzuzufügen , wie
fillable
,guarded
,table
und die Beziehungen (das ist , weil ich die Repository - Muster verwenden, sonst würde das Modell auch diesave
,update
,find
, etc. Methoden).Repositories (Repository-Muster) : Am Anfang war ich sehr verwirrt. Und wie Sie dachte ich: "Nun, ich benutze MySQL und das ist es."
Ich habe jedoch die Vor- und Nachteile der Verwendung des Repository-Musters abgewogen und verwende es jetzt. Ich denke, dass ich jetzt , in diesem Moment, nur noch MySQL verwenden muss. Aber wenn ich in drei Jahren auf etwas wie MongoDB umsteigen muss, ist der größte Teil der Arbeit erledigt. Alles auf Kosten einer zusätzlichen Schnittstelle und a
$app->bind(«interface», «repository»)
.Ereignisse ( Beobachtermuster ): Ereignisse sind nützlich für Dinge, die zu jeder Zeit in jeder Klasse ausgelöst werden können. Denken Sie beispielsweise daran, Benachrichtigungen an einen Benutzer zu senden. Bei Bedarf lösen Sie das Ereignis aus, um eine Benachrichtigung an eine beliebige Klasse Ihrer Anwendung zu senden. Dann können Sie eine
UserNotificationEvents
solche Klasse haben , die alle Ihre ausgelösten Ereignisse für Benutzerbenachrichtigungen behandelt.Dienste : Bisher haben Sie die Wahl, Steuerungen oder Modellen Logik hinzuzufügen. Für mich ist es sinnvoll, die Logik in Services hinzuzufügen . Seien wir ehrlich, Services ist ein ausgefallener Name für Klassen. Und Sie können so viele Klassen haben, wie es für Sie innerhalb Ihrer Anwendung sinnvoll ist.
Nehmen Sie dieses Beispiel: Vor kurzem habe ich so etwas wie Google Forms entwickelt. Ich begann mit einem
CustomFormService
und endete mitCustomFormService
,CustomFormRender
,CustomFieldService
,CustomFieldRender
,CustomAnswerService
undCustomAnswerRender
. Warum? Weil es für mich Sinn machte. Wenn Sie mit einem Team arbeiten, sollten Sie Ihre Logik dort platzieren, wo es für das Team sinnvoll ist.Der Vorteil der Verwendung von Diensten gegenüber Controllern / Modellen besteht darin, dass Sie nicht durch einen einzelnen Controller oder ein einzelnes Modell eingeschränkt werden. Sie können je nach Design und Anforderungen Ihrer Anwendung so viele Dienste wie erforderlich erstellen. Hinzu kommt der Vorteil, einen Dienst in einer beliebigen Klasse Ihrer Anwendung aufzurufen.
Das dauert lange, aber ich möchte Ihnen zeigen, wie ich meine Bewerbung strukturiert habe:
Ich benutze jeden Ordner für eine bestimmte Funktion. Beispielsweise
Validators
enthält das Verzeichnis eineBaseValidator
Klasse, die für die Verarbeitung der Validierung verantwortlich ist, basierend auf$rules
und$messages
von bestimmten Validatoren (normalerweise eine für jedes Modell). Ich könnte diesen Code genauso gut in einen Dienst einfügen, aber es ist für mich sinnvoll, einen bestimmten Ordner dafür zu haben, auch wenn er (vorerst) nur innerhalb des Dienstes verwendet wird.Ich empfehle Ihnen, die folgenden Artikel zu lesen, da sie Ihnen die Dinge möglicherweise etwas besser erklären:
Breaking the Mould von Dayle Rees (Autor von CodeBright): Hier habe ich alles zusammengestellt, obwohl ich einige Dinge geändert habe, um sie meinen Bedürfnissen anzupassen.
Entkoppeln Sie Ihren Code in Laravel mithilfe von Repositorys und Services von Chris Goosey: In diesem Beitrag wird gut erklärt, was ein Service und das Repository-Muster sind und wie sie zusammenpassen.
Laracasts haben auch die vereinfachten Repositories und die Einzelverantwortung, die gute Ressourcen mit praktischen Beispielen sind (obwohl Sie bezahlen müssen).
quelle
Ich wollte eine Antwort auf meine eigene Frage posten. Ich könnte tagelang darüber reden, aber ich werde versuchen, dies schnell zu veröffentlichen, um sicherzugehen, dass ich es aufstehe.
Am Ende habe ich die vorhandene Struktur von Laravel verwendet, was bedeutet, dass ich meine Dateien hauptsächlich als Modell, Ansicht und Controller beibehalten habe. Ich habe auch einen Bibliotheksordner für wiederverwendbare Komponenten, die nicht wirklich Modelle sind.
Ich habe meine Modelle nicht in Dienstleistungen / Bibliotheken eingepackt . Alle angegebenen Gründe haben mich nicht zu 100% vom Nutzen der Nutzung von Diensten überzeugt. Ich kann mich zwar irren, aber soweit ich sehen kann, führen sie nur zu Tonnen von fast leeren Dateien, die ich erstellen und zwischen denen ich wechseln muss, wenn ich mit Modellen arbeite, und verringern auch den Nutzen der Verwendung von Beredsamkeiten (insbesondere beim Abrufen von Modellen) z. B. unter Verwendung von Paginierung, Gültigkeitsbereichen usw.).
Ich setze die Geschäftslogik in die Modelle ein und greife direkt von meinen Controllern aus beredt zu. Ich verwende eine Reihe von Ansätzen, um sicherzustellen, dass die Geschäftslogik nicht umgangen wird:
Berücksichtigung der Bedenken der Menschen bei der Verwendung von Modellen:
Zusätzlicher Hinweis: Ich habe das Gefühl, dass das Einwickeln Ihrer Modelle in Dienstleistungen wie ein Schweizer Taschenmesser mit vielen Werkzeugen ist und ein weiteres Messer darum herum gebaut wird, das im Grunde das Gleiche tut? Ja, manchmal möchten Sie vielleicht eine Klinge abkleben oder sicherstellen, dass zwei Klingen zusammen verwendet werden ... aber es gibt normalerweise andere Möglichkeiten, dies zu tun ...
WANN MAN DIENSTLEISTUNGEN NUTZT : Dieser Artikel enthält sehr gute Beispiele für die Verwendung von Diensten ( Hinweis: Es kommt nicht sehr oft vor ). Er sagt im Grunde, wenn Ihr Objekt mehrere Modelle oder Modelle an seltsamen Stellen ihres Lebenszyklus verwendet , ist dies sinnvoll. http://www.justinweiss.com/articles/where-do-you-put-your-code/
quelle
Um die Logik zwischen Controllern und Modellen zu erstellen, erstelle ich eine Serviceschicht . Grundsätzlich ist dies mein Ablauf für jede Aktion in meiner App:
So mache ich es:
Dies ist die Methode eines Controllers, um etwas zu erstellen:
Dies ist die Serviceklasse, die die mit der Operation verbundene Logik ausführt:
Und das ist mein Modell:
Weitere Informationen zu dieser Methode zum Organisieren meines Codes für eine Laravel-App: https://github.com/rmariuzzo/Pitimi
quelle
$congregation->save();
Repositories benötigen, benötigen Sie möglicherweise keine. Möglicherweise steigt jedoch Ihr Datenzugriffsbedarf im Laufe der Zeit. Möglicherweise haben Sie Bedarf an$congregation->destroyByUser()
oder$congregationUsers->findByName($arrayOfSelectedFields);
so weiter. Warum entkoppeln Sie Ihre Dienste nicht von den Datenzugriffsanforderungen? Lassen Sie den Rest Ihrer App mit Objekten / Arrays arbeiten, die von Repos zurückgegeben wurden, und bearbeiten Sie einfach / formatieren / etc ... Ihre Repos wachsen (aber teilen Sie sie in verschiedene Dateien auf, letztendlich muss sich die Komplexität eines Projekts irgendwo befinden).Meiner Meinung nach hat Laravel bereits viele Möglichkeiten, Ihre Geschäftslogik zu speichern.
Kurze Antwort:
Request
Objekte von laravel , um Ihre Eingabe automatisch zu validieren, und behalten Sie dann die Daten in der Anforderung bei (erstellen Sie das Modell). Da alle Benutzereingaben direkt in der Anfrage verfügbar sind, halte ich es für sinnvoll, dies hier durchzuführen.Job
Objekte von laravel , um Aufgaben auszuführen, für die einzelne Komponenten erforderlich sind, und senden Sie sie dann einfach ab. Ich denkeJob
, es umfasst Serviceklassen. Sie führen eine Aufgabe wie die Geschäftslogik aus.Lange Antwort:
Bei Bedarf Respositorys verwenden : Repositorys müssen überfüllt sein und werden meistens nur als
accessor
Modell verwendet. Ich habe das Gefühl, dass sie definitiv eine gewisse Verwendung haben, aber wenn Sie keine massive Anwendung entwickeln, die diese Flexibilität erfordert , damit Sie Laravel vollständig loswerden können, halten Sie sich von Repositories fern. Sie werden sich später bedanken und Ihr Code wird viel einfacher sein.Fragen Sie sich, ob die Möglichkeit besteht, dass Sie PHP-Frameworks oder einen Datenbanktyp ändern, den Laravel nicht unterstützt.
Wenn Ihre Antwort "Wahrscheinlich nicht" lautet, implementieren Sie das Repository-Muster nicht.
Bitte schlagen Sie außerdem kein Muster auf ein hervorragendes ORM wie Eloquent. Sie fügen nur Komplexität hinzu, die nicht erforderlich ist und von der Sie überhaupt nicht profitieren.
Verwenden Sie Services sparsam: Serviceklassen sind für mich nur ein Ort zum Speichern von Geschäftslogik, um eine bestimmte Aufgabe mit den angegebenen Abhängigkeiten auszuführen. Laravel hat diese sofort einsatzbereit, die als "Jobs" bezeichnet werden, und sie sind viel flexibler als eine benutzerdefinierte Serviceklasse.
Ich denke, Laravel hat eine umfassende Lösung für das
MVC
Logikproblem. Es ist nur eine Sache oder Organisation.Beispiel:
Anfrage :
Controller :
Im obigen Beispiel wird die Anforderungseingabe automatisch validiert. Sie müssen lediglich die persist-Methode aufrufen und einen neuen Beitrag übergeben. Ich denke, Lesbarkeit und Wartbarkeit sollten immer komplexe und nicht benötigte Entwurfsmuster übertreffen.
Sie können dann genau dieselbe Persist-Methode auch zum Aktualisieren von Posts verwenden, da wir prüfen können, ob der Post bereits vorhanden ist, und bei Bedarf eine abwechselnde Logik ausführen können.
quelle
ShouldQueue
Laravel bereitgestellten Job implementieren . Wenn Sie Geschäftslogik in einen Befehl oder ein Ereignis schreiben möchten, lösen Sie einfach den Job in diesen Ereignissen / Befehlen aus. Laravels Jobs sind extrem flexibel, aber am Ende sind sie nur einfache Serviceklassen.