MVC (Laravel), wo Logik hinzugefügt werden soll

136

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

Sabrina Leggett
quelle
3
Können Sie Ihre Frage minimieren?
Die Alpha
3
Sie können dies auch überprüfen .
Die Alpha
1
"Woher weiß ich, dass ich eine Methode PublishPost () in einer Bibliothek aufrufen muss, wenn ich nur $ post-> is_published = 1 ausführen kann?" Dokumentation?
Ceejayoz
Eine der Schönheiten bei Beredsamkeit und ORMS ist, dass es einfacher ist, mit ihnen ohne viele Dokumente zu arbeiten.
Sabrina Leggett
1
Vielen Dank für die Veröffentlichung. Ich habe mit den gleichen Problemen zu kämpfen und fand Ihren Beitrag und Ihre Antwort unglaublich hilfreich. Letztendlich habe ich entschieden, dass Laravel keine gute Architektur für irgendetwas bietet, das über eine schnelle und schmutzige Ruby-on-Rails-Website hinausgeht. Trates überall, Schwierigkeiten, Klassenfunktionen zu finden und Tonnen von Auto-Magic-Müll überall. ORM hat noch nie funktioniert und wenn Sie es verwenden, sollten Sie wahrscheinlich NoSQL verwenden.
Alex Barker

Antworten:

171

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, tableund die Beziehungen (das ist , weil ich die Repository - Muster verwenden, sonst würde das Modell auch die save, 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 UserNotificationEventssolche 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 CustomFormServiceund endete mit CustomFormService, CustomFormRender, CustomFieldService, CustomFieldRender, CustomAnswerServiceund CustomAnswerRender. 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:

app/
    controllers/
    MyCompany/
        Composers/
        Exceptions/
        Models/
        Observers/
        Sanitizers/
        ServiceProviders/
        Services/
        Validators/
    views
    (...)

Ich benutze jeden Ordner für eine bestimmte Funktion. Beispielsweise Validatorsenthält das Verzeichnis eine BaseValidatorKlasse, die für die Verarbeitung der Validierung verantwortlich ist, basierend auf $rulesund $messagesvon 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).

Luís Cruz
quelle
3
tolle Erklärung. Hier stehe ich gerade - im aktuellen Projekt setze ich meine Geschäftslogik in Modelle ein und es funktioniert tatsächlich sehr gut. Wir müssen SOLID definitiv ein bisschen fummeln, aber es hat uns noch nicht wirklich in Schwierigkeiten gebracht. Es ist schnell, es ist ein bisschen schmutzig, aber bisher ist unser Projekt sehr wartbar, weil es so trocken ist. Ich bin im Moment definitiv in Ordnung, wenn ich mich an sie halte, weil sie die Arbeit erledigen, aber in jedem zukünftigen Projekt werde ich wahrscheinlich einfach mit dem Standard arbeiten, was so klingt, als wären Repositories geworden.
Sabrina Leggett
2
Ich bin froh, dass Sie einen Weg gefunden haben, der für Sie Sinn macht. Seien Sie vorsichtig mit den Annahmen, die Sie heute treffen . Ich habe über 3 Jahre an einem Projekt gearbeitet und am Ende Controller und Modelle mit mehr als 5000 Codezeilen gefunden. Viel Glück bei Ihrem Projekt.
Luís Cruz
auch ein bisschen schmutzig, aber ich dachte darüber nach, Eigenschaften zu verwenden, um zu vermeiden, dass Modelle riesig werden. Auf diese Weise kann ich sie ein wenig trennen
Sabrina Leggett
Dieser Artikel artikuliert gut, WENN es sinnvoll ist, Dienste zu nutzen. In Ihrem Formularbeispiel ist es zwar sinnvoll, Dienste zu verwenden, aber er erklärt, wie er dies tut. Wenn die Logik in direktem Zusammenhang mit einem Modell steht, fügt er sie in dieses Modell ein. justinweiss.com/articles/where-do-you-put-your-code
Sabrina Leggett
Die Erklärung gefällt mir sehr gut. Ich habe eine Frage: Sie haben erwähnt, dass die Validierung nicht in den Controller übernommen werden soll. Wo ist Ihrer Meinung nach der beste Ort für die Validierung? Viele schlagen vor, es in die erweiterte Anforderungsklasse aufzunehmen (und auch, was wir derzeit tun), aber was ist, wenn ich nicht nur auf http-Anfrage, sondern auch auf handwerklichem Befehl usw. validieren möchte, ist es wirklich ein guter Ort?
Kingshark
24

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:

  • Accessoren und Mutatoren: Laravel hat großartige Accessoren und Mutatoren. Wenn ich eine Aktion ausführen möchte, wenn ein Beitrag vom Entwurf zum veröffentlichten verschoben wird, kann ich dies aufrufen, indem ich die Funktion setIsPublishedAttribute erstelle und die Logik dort einbinde
  • Überschreiben von Erstellen / Aktualisieren usw .: Sie können Eloquent-Methoden in Ihren Modellen jederzeit überschreiben, um benutzerdefinierte Funktionen einzuschließen. Auf diese Weise können Sie die Funktionalität für jede CRUD-Operation aufrufen. Bearbeiten: Ich denke, es gibt einen Fehler beim Überschreiben des Erstellens in neueren Laravel-Versionen (daher verwende ich Ereignisse, die jetzt im Boot registriert sind).
  • Validierung: Ich verknüpfe meine Validierung auf die gleiche Weise, z. B. führe ich die Validierung durch, indem ich CRUD-Funktionen und bei Bedarf auch Accessoren / Mutatoren überschreibe. Weitere Informationen finden Sie unter Esensi oder dwightwatson / validating.
  • Magische Methoden: Ich verwende die Methoden __get und __set meiner Modelle, um gegebenenfalls Funktionen zu nutzen
  • Eloquent erweitern: Wenn Sie eine Aktion für alle Aktualisierungen / Erstellungen ausführen möchten , können Sie eloquent sogar erweitern und auf mehrere Modelle anwenden.
  • Veranstaltungen: Dies ist ein direkter und allgemein vereinbarter Ort, um dies ebenfalls zu tun. Der größte Nachteil bei Ereignissen ist meiner Meinung nach, dass Ausnahmen schwer nachzuvollziehen sind (möglicherweise nicht der neue Fall beim neuen Ereignissystem von Laravel). Ich möchte meine Ereignisse auch nach dem gruppieren, was sie tun, anstatt nach dem Zeitpunkt, zu dem sie aufgerufen werden. ZB habe ich einen MailSender-Abonnenten, der auf Ereignisse wartet, die E-Mails senden.
  • Hinzufügen von Pivot / BelongsToMany-Ereignissen: Eines der Dinge, mit denen ich am längsten zu kämpfen hatte, war das Anhängen von Verhalten an die Änderung von Zugehörigkeit zu vielen Beziehungen. Beispiel: Ausführen einer Aktion, wenn ein Benutzer einer Gruppe beitritt. Ich bin fast fertig damit, eine benutzerdefinierte Bibliothek dafür aufzupolieren. Ich habe es noch nicht veröffentlicht, aber es ist funktionsfähig! Ich werde versuchen, bald einen Link zu posten. BEARBEITEN Am Ende habe ich alle meine Drehpunkte zu normalen Modellen gemacht und mein Leben war so viel einfacher ...

Berücksichtigung der Bedenken der Menschen bei der Verwendung von Modellen:

  • Organisation: Ja, wenn Sie mehr Logik in Modelle aufnehmen, können diese länger sein, aber im Allgemeinen habe ich festgestellt, dass 75% meiner Modelle immer noch ziemlich klein sind. Wenn ich die größeren organisieren möchte, kann ich dies mithilfe von Merkmalen tun (z. B. einen Ordner für das Modell mit einigen weiteren Dateien wie PostScopes, PostAccessors, PostValidation usw. nach Bedarf erstellen). Ich weiß, dass dies nicht unbedingt das ist, wofür Eigenschaften sind, aber dieses System funktioniert ohne Probleme.

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/

Sabrina Leggett
quelle
2
Interessante und gültige Gedanken. Aber ich bin neugierig - wie testen Sie Ihre Geschäftslogik, wenn sie an Modelle gebunden ist, die an Eloquent gebunden sind, das an die Datenbank gebunden ist?
JustAMartin
code.tutsplus.com/tutorials/… oder Sie können Ereignisse wie gesagt verwenden, wenn Sie es weiter aufschlüsseln möchten
Sabrina Leggett
1
@JustAMartin Sind Sie sicher, dass Sie die Datenbank nicht einfach für Ihre Komponententests verwenden können? Was ist der Grund, es nicht zu tun? Viele Menschen sind sich einig, dass es oft in Ordnung ist, Datenbanken in Unit-Tests zu verwenden. (einschließlich Martin Fowler, martinfowler.com/bliki/UnitTest.html : "Ich behandle die Verwendung von Doubles für externe Ressourcen nicht als absolute Regel. Wenn das Gespräch mit der Ressource für Sie stabil und schnell genug ist, gibt es keinen Grund, dies nicht zu tun es in Ihren Unit-Tests ")
Alex P.
@ AlexP11223 Ja, das macht Sinn. Ich habe versucht, SQLite als meine Testdatenbank zu integrieren, und im Allgemeinen ging es gut, obwohl SQLite einige schwerwiegende Einschränkungen aufweist, die bei Laravel-Migrationen und benutzerdefinierten Abfragen (falls vorhanden) berücksichtigt werden müssen. Natürlich handelt es sich dann nicht ausschließlich um Unit-Tests, sondern um Funktionstests, aber auf diese Weise ist es noch effizienter. Wenn Sie Ihr Modell jedoch vollständig isoliert testen möchten (als strenger Komponententest), ist möglicherweise eine spürbare Menge zusätzlichen Codes (Verspottungen usw.) erforderlich.
JustAMartin
22

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:

  1. Der Controller ruft die vom Benutzer angeforderte Aktion ab, sendet Parameter und delegiert alles an eine Serviceklasse.
  2. Die Serviceklasse übernimmt die gesamte Logik für die Operation: Eingabevalidierung, Ereignisprotokollierung, Datenbankoperationen usw.
  3. Das Modell enthält Informationen zu Feldern, Datentransformationen und Definitionen von Attributvalidierungen.

So mache ich es:

Dies ist die Methode eines Controllers, um etwas zu erstellen:

public function processCreateCongregation()
{
    // Get input data.
    $congregation                 = new Congregation;
    $congregation->name           = Input::get('name');
    $congregation->address        = Input::get('address');
    $congregation->pm_day_of_week = Input::get('pm_day_of_week');
    $pmHours                      = Input::get('pm_datetime_hours');
    $pmMinutes                    = Input::get('pm_datetime_minutes');
    $congregation->pm_datetime    = Carbon::createFromTime($pmHours, $pmMinutes, 0);

    // Delegates actual operation to service.
    try
    {
        CongregationService::createCongregation($congregation);
        $this->success(trans('messages.congregationCreated'));
        return Redirect::route('congregations.list');
    }
    catch (ValidationException $e)
    {
        // Catch validation errors thrown by service operation.
        return Redirect::route('congregations.create')
            ->withInput(Input::all())
            ->withErrors($e->getValidator());
    }
    catch (Exception $e)
    {
        // Catch any unexpected exception.
        return $this->unexpected($e);
    }
}

Dies ist die Serviceklasse, die die mit der Operation verbundene Logik ausführt:

public static function createCongregation(Congregation $congregation)
{
    // Log the operation.
    Log::info('Create congregation.', compact('congregation'));

    // Validate data.
    $validator = $congregation->getValidator();

    if ($validator->fails())
    {
        throw new ValidationException($validator);
    }

    // Save to the database.
    $congregation->created_by = Auth::user()->id;
    $congregation->updated_by = Auth::user()->id;

    $congregation->save();
}

Und das ist mein Modell:

class Congregation extends Eloquent
{
    protected $table = 'congregations';

    public function getValidator()
    {
        $data = array(
            'name' => $this->name,
            'address' => $this->address,
            'pm_day_of_week' => $this->pm_day_of_week,
            'pm_datetime' => $this->pm_datetime,
        );

        $rules = array(
            'name' => ['required', 'unique:congregations'],
            'address' => ['required'],
            'pm_day_of_week' => ['required', 'integer', 'between:0,6'],
            'pm_datetime' => ['required', 'regex:/([01]?[0-9]|2[0-3]):[0-5]?[0-9]:[0-5][0-9]/'],
        );

        return Validator::make($data, $rules);
    }

    public function getDates()
    {
        return array_merge_recursive(parent::getDates(), array(
            'pm_datetime',
            'cbs_datetime',
        ));
    }
}

Weitere Informationen zu dieser Methode zum Organisieren meines Codes für eine Laravel-App: https://github.com/rmariuzzo/Pitimi

Rubens Mariuzzo
quelle
Es scheint, als wären Dienste das, was ich in meinem Beitrag als Bibliotheken bezeichnet habe. Ich denke, dass dies besser ist als Repositorys, wenn Sie nicht mehrere ORMS verwenden müssen, aber das Problem ist, dass Sie Ihr gesamtes Projekt migrieren müssen (was Sie nicht mit Ereignissen zu tun haben), und es scheint so Am Ende wird nur die Modellstruktur gespiegelt, sodass Sie nur all diese zusätzlichen Dateien haben. Warum nicht einfach in die Modelle aufnehmen? Zumindest haben Sie auf diese Weise keine zusätzlichen Dateien.
Sabrina Leggett
Das ist eine interessante Frage @SabrinaGelbart, mir wurde beigebracht, Modelle die Datenbankentitäten darstellen zu lassen und keine Logik zu haben. Aus diesem Grund habe ich diese zusätzlichen Dateien erstellt, die als Dienste bezeichnet werden: um die gesamte Logik und alle zusätzlichen Operationen zu speichern. Ich bin mir nicht sicher, was die gesamte Bedeutung von Ereignissen ist, die Sie zuvor beschrieben haben, aber ich denke, dass wir mit Diensten und der Verwendung von Laravels Ereignissen alle Dienstmethoden anwenden können, um Ereignisse am Anfang und am Ende auszulösen. Auf diese Weise kann jedes Ereignis vollständig von der Logik entkoppelt werden. Was denken Sie?
Rubens Mariuzzo
Mir wurde auch das über Modelle beigebracht ... wäre schön, eine gute Erklärung dafür zu bekommen, warum (vielleicht Abhängigkeitsprobleme)?
Sabrina Leggett
Ich mag diesen Ansatz! Ich habe im Internet nach einer Vorstellung davon gesucht, wie ich mit der Logik des Modells umgehen soll, habe mir Repositories angesehen, aber es schien zu kompliziert und nutzlos für ein bisschen Gebrauch. Dienstleistungen sind eine gute Idee. Meine Frage ist, nachdem Sie einen Ordner mit Diensten im App-Ordner erstellt haben, müssen Sie ihn in bootstrap / start.php oder irgendwo zum Booten einfügen, weil ich über Ihren Git nachgesehen habe und ihn nicht finden konnte? @ RubensMariuzzo. Wird es automatisch in der gesamten App verfügbar? wir können also einfach CongregationService :: getCongregations () verwenden; ??
Oguzhan
1
Wenn Sie nur ein $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).
Programmierer
12

Meiner Meinung nach hat Laravel bereits viele Möglichkeiten, Ihre Geschäftslogik zu speichern.

Kurze Antwort:

  • Verwenden Sie die RequestObjekte 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.
  • Verwenden Sie die JobObjekte von laravel , um Aufgaben auszuführen, für die einzelne Komponenten erforderlich sind, und senden Sie sie dann einfach ab. Ich denke Job, 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 accessorModell 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 MVCLogikproblem. Es ist nur eine Sache oder Organisation.

Beispiel:

Anfrage :

namespace App\Http\Requests;

use App\Post;
use App\Jobs\PostNotifier;
use App\Events\PostWasCreated;
use App\Http\Requests\Request;

class PostRequest extends Request
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'title'       => 'required',
            'description' => 'required'
        ];
    }

    /**
     * Save the post.
     *
     * @param Post $post
     *
     * @return bool
     */
    public function persist(Post $post)
    {
        if (!$post->exists) {
            // If the post doesn't exist, we'll assign the
            // post as created by the current user.
            $post->user_id = auth()->id();
        }

        $post->title = $this->title;
        $post->description = $this->description;

        // Perform other tasks, maybe fire an event, dispatch a job.

        if ($post->save()) {
            // Maybe we'll fire an event here that we can catch somewhere else that
            // needs to know when a post was created.
            event(new PostWasCreated($post));

            // Maybe we'll notify some users of the new post as well.
            dispatch(new PostNotifier($post));

            return true;
        }

        return false;
    }
}

Controller :

namespace App\Http\Controllers;

use App\Post;
use App\Http\Requests\PostRequest;

class PostController extends Controller
{

   /**
    * Creates a new post.
    *
    * @return string
    */
    public function store(PostRequest $request)
    {
        if ($request->persist(new Post())) {
            flash()->success('Successfully created new post!');
        } else {
            flash()->error('There was an issue creating a post. Please try again.');
        }

        return redirect()->back();
    }

   /**
    * Updates a post.
    *
    * @return string
    */
    public function update(PostRequest $request, $id)
    {
        $post = Post::findOrFail($id);

        if ($request->persist($post)) {
            flash()->success('Successfully updated post!');
        } else {
            flash()->error('There was an issue updating this post. Please try again.');
        }

        return redirect()->back();
    }
}

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.

Steve Bauman
quelle
aber - sollen Jobs nicht in die Warteschlange gestellt werden? Manchmal möchten wir wahrscheinlich, dass es in die Warteschlange gestellt wird, aber nicht immer. Warum nicht stattdessen Befehle verwenden? Was ist, wenn Sie eine Geschäftslogik schreiben möchten, die als Befehl, Ereignis oder Warteschlange ausgeführt werden kann?
Sabrina Leggett
1
Jobs müssen nicht in die Warteschlange gestellt werden. Sie geben dies an, indem Sie die Schnittstelle für den von ShouldQueueLaravel 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.
Steve Bauman