MVC (Laravel), gdzie dodać logikę

137

Powiedzmy, że ilekroć wykonuję operację CRUD lub modyfikuję relację w określony sposób, chcę też zrobić coś innego. Np. Za każdym razem, gdy ktoś publikuje post, chcę również zapisać coś w tabeli do celów analitycznych. Może nie jest to najlepszy przykład, ale generalnie istnieje wiele funkcji „pogrupowanych”.

Zwykle widzę tego typu logikę umieszczoną w kontrolerach. To wszystko w porządku, dopóki nie zechcesz odtworzyć tej funkcji w wielu miejscach. Kiedy zaczynasz wchodzić w częściowe, tworzenie API i generowanie fikcyjnej zawartości, staje się problemem z utrzymaniem rzeczy SUCHYCH.

Sposoby radzenia sobie z tym to zdarzenia, repozytoria, biblioteki i dodawanie do modeli. Oto moje rozumienie każdego z nich:

Usługi: tutaj większość ludzi prawdopodobnie umieściłaby ten kod. Moim głównym problemem związanym z usługami jest to, że czasami trudno jest znaleźć w nich określoną funkcjonalność i czuję, że zapomina się o nich, gdy ludzie koncentrują się na używaniu Eloquent. Skąd mam wiedzieć, że muszę wywołać metodę publishPost()w bibliotece, skoro mogę to zrobić $post->is_published = 1?

Jedynym warunkiem, w jakim to działa dobrze, jest to, że używasz TYLKO usług (i idealnie sprawisz, że Eloquent będzie w jakiś sposób niedostępny dla wszystkich kontrolerów).

Ostatecznie wygląda na to, że utworzyłoby to kilka dodatkowych niepotrzebnych plików, jeśli Twoje żądania generalnie są zgodne ze strukturą modelu.

Repozytoria: z tego, co rozumiem, jest to w zasadzie jak usługa, ale istnieje interfejs, dzięki czemu można przełączać się między ORMami, których nie potrzebuję.

Zdarzenia: uważam to za najbardziej elegancki system w pewnym sensie, ponieważ wiesz, że zdarzenia modelowe zawsze będą wywoływane metodami elokwentnymi, więc możesz pisać kontrolery tak, jak zwykle. Widzę jednak, że robi się bałagan i jeśli ktoś ma przykłady dużych projektów wykorzystujących zdarzenia do krytycznego sprzężenia, chciałbym to zobaczyć.

Modele: Tradycyjnie miałbym klasy, które wykonywały CRUD, a także obsługiwały krytyczne sprzężenia. To faktycznie ułatwiło sprawę, ponieważ znałeś wszystkie funkcje związane z CRUD +, cokolwiek trzeba było z tym zrobić, było tam.

Proste, ale w architekturze MVC zwykle nie jest to to, co widzę. W pewnym sensie wolę to od usług, ponieważ jest trochę łatwiejsze do znalezienia i jest mniej plików do śledzenia. Może się jednak trochę zdezorganizować. Chciałbym usłyszeć wady tej metody i dlaczego większość ludzi tego nie robi.

Jakie są zalety / wady każdej metody? Czy coś mi brakuje?

Sabrina Leggett
źródło
3
Czy możesz zminimalizować swoje pytanie?
Alpha
3
Możesz również to sprawdzić .
Alpha
1
„Skąd mam wiedzieć, że muszę wywołać metodę PublishingPost () w bibliotece, skoro mogę po prostu wykonać $ post-> is_published = 1?” Dokumentacja?
ceejayoz
jedna z piękności o elokwencji i ORMS, czy łatwiej jest z nimi pracować bez wielu dokumentów?
Sabrina Leggett
1
Dzięki za opublikowanie tego. Zmagam się z tymi samymi problemami i znalazłem Twój post i odpowiedź niezwykle pomocną. Ostatecznie zdecydowałem, że Laravel nie zapewnia dobrej architektury dla czegokolwiek, co wykracza poza szybką i brudną witrynę Ruby-on-Rails. Wszędzie są problemy, trudno znaleźć funkcje klas i wszędzie mnóstwo śmieci z automatycznej magii. ORM nigdy nie działał i jeśli go używasz, prawdopodobnie powinieneś używać NoSQL.
Alex Barker,

Odpowiedzi:

171

Myślę, że wszystkie wzorce / architektury, które prezentujesz, są bardzo przydatne, o ile przestrzegasz zasad SOLID .

Albowiem gdzie dodać logikę myślę, że ważne jest, aby zapoznać się z jednolitej odpowiedzialności Zasada . Moja odpowiedź dotyczy również tego, że pracujesz nad średnim / dużym projektem. Jeśli jest to projekt, który rzuca coś na stronę , zapomnij o tej odpowiedzi i dodaj to wszystko do kontrolerów lub modeli.

Krótka odpowiedź brzmi: gdzie ma to dla Ciebie sens (w przypadku usług) .

Długa odpowiedź:

Administratorzy : Jaka jest odpowiedzialność administratorów? Jasne, możesz umieścić całą swoją logikę w kontrolerze, ale czy to odpowiedzialność kontrolera? Nie sądzę.

U mnie kontroler musi otrzymać żądanie i zwrócić dane, a to nie jest miejsce na walidacje, wywoływanie metod db itp.

Modele : Czy jest to dobre miejsce na dodanie logiki, takiej jak wysyłanie powitalnego e-maila, gdy użytkownik rejestruje się lub aktualizuje liczbę głosów w poście? Co jeśli chcesz wysłać ten sam e-mail z innego miejsca w kodzie? Tworzysz metodę statyczną? A jeśli te e-maile wymagają informacji z innego modelu?

Myślę, że model powinien reprezentować byt. Z laravel, używam tylko klasę modelu, aby dodać takie rzeczy jak fillable, guarded, tablea stosunki (to dlatego używam Repository Pattern, inaczej model będzie również posiadać save, update, finditp metod).

Repozytoria (wzorzec repozytorium) : Na początku byłem tym bardzo zdezorientowany. I tak jak ty pomyślałem „cóż, używam MySQL i to wszystko”.

Jednak zrównoważyłem zalety i wady korzystania ze wzorca repozytorium i teraz go używam. Myślę, że teraz , w tej chwili, będę musiał używać tylko MySQL. Ale jeśli za trzy lata będę musiał przejść na coś takiego jak MongoDB, większość pracy zostanie wykonana. Wszystko kosztem jednego dodatkowego interfejsu i pliku $app->bind(«interface», «repository»).

Zdarzenia ( wzorzec obserwatora ): Zdarzenia są przydatne w przypadku rzeczy, które mogą być rzucane w dowolnej klasie w dowolnym momencie. Pomyśl na przykład o wysyłaniu powiadomień do użytkownika. W razie potrzeby uruchamiasz zdarzenie, aby wysłać powiadomienie w dowolnej klasie aplikacji. Następnie możesz mieć taką klasę, UserNotificationEventsktóra obsługuje wszystkie uruchomione zdarzenia dla powiadomień użytkowników.

Usługi : do tej pory możesz dodać logikę do kontrolerów lub modeli. Dla mnie sensowne jest dodanie logiki w ramach usług . Spójrzmy prawdzie w oczy, usługi to fantazyjna nazwa dla zajęć. Możesz mieć tyle zajęć, ile ma to sens w twojej aplikacji.

Weźmy ten przykład: Niedawno opracowałem coś takiego jak Formularze Google. I zaczęło się CustomFormServicei skończyło się CustomFormService, CustomFormRender, CustomFieldService, CustomFieldRender, CustomAnswerServicei CustomAnswerRender. Czemu? Ponieważ to miało dla mnie sens. Jeśli pracujesz z zespołem, powinieneś umieścić swoją logikę tam, gdzie ma to sens dla zespołu.

Zaletą korzystania z usług w porównaniu z kontrolerami / modelami jest to, że nie jesteś ograniczony przez pojedynczy kontroler lub pojedynczy model. Możesz utworzyć dowolną liczbę usług w oparciu o projekt i potrzeby aplikacji. Dodaj do tego zaletę wywoływania usługi w dowolnej klasie aplikacji.

To trwa długo, ale chciałbym pokazać, jak zbudowałem moją aplikację:

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

Używam każdego folderu do określonej funkcji. Na przykład Validatorskatalog zawiera BaseValidatorklasę odpowiedzialną za przetwarzanie walidacji na podstawie $rulesi$messages określonych walidatorach (zwykle po jednym dla każdego modelu). Mógłbym równie łatwo umieścić ten kod w usłudze, ale sensowne jest, aby mieć do tego określony folder, nawet jeśli jest on używany tylko w usłudze (na razie).

Zalecam przeczytanie następujących artykułów, ponieważ mogą one trochę lepiej wyjaśnić:

Breaking the Mould, Dayle Rees (autor CodeBright): Tutaj złożyłem to wszystko razem, mimo że zmieniłem kilka rzeczy, aby pasowały do ​​moich potrzeb.

Oddzielenie kodu w Laravel przy użyciu repozytoriów i usług autorstwa Chrisa Gooseya: Ten post wyjaśnia dobrze, czym jest usługa i wzorzec repozytorium oraz jak do siebie pasują.

Laracasts ma również uproszczoną repozytoria i pojedynczą odpowiedzialność, które są dobrymi zasobami z praktycznymi przykładami (nawet jeśli musisz zapłacić).

Luís Cruz
źródło
3
świetne wyjaśnienie. Oto sytuacja, w której w tej chwili stoję - w obecnym projekcie umieszczam logikę biznesową w modelach i tak naprawdę działa bardzo dobrze. Zdecydowanie musimy trochę pomieszać SOLID, ale tak naprawdę jeszcze nie wpędziło nas to w kłopoty. Jest szybki, trochę brudny, ale jak na razie nasz projekt jest bardzo łatwy w utrzymaniu, ponieważ jest taki SUCHY. W tej chwili zdecydowanie nie przeszkadza mi trzymanie się ich, ponieważ wykonują swoją pracę, ale w każdym przyszłym projekcie prawdopodobnie po prostu pójdę z tym, co jest standardowe, co brzmi tak, jakby stały się repozytoria.
Sabrina Leggett
2
Cieszę się, że znalazłeś sposób, który ma dla ciebie sens. Po prostu uważaj na założenia, które przyjmujesz dzisiaj . Pracowałem nad projektem od ponad 3 lat i skończyło się na kontrolerach i modelach z ponad 5000 liniami kodu. Powodzenia w Twoim projekcie.
Luís Cruz
też trochę brudne, ale myślałem o użyciu cech, aby uniknąć ogromnych modeli. W ten sposób mogę je trochę oddzielić
Sabrina Leggett
Ten artykuł dobrze się wyraża, KIEDY korzystanie z usług ma sens. W twoim przykładzie Forma ma sens korzystanie z usług, ale wyjaśnia, jak to robi, czyli kiedy logika jest bezpośrednio związana z modelem, który umieszcza w tym modelu. justinweiss.com/articles/where-do-you-put-your-code
Sabrina Leggett
Naprawdę podoba mi się wyjaśnienie. Mam jedno pytanie: wspomniałeś o tym, aby nie umieszczać walidacji w kontrolerze, więc jak myślisz, gdzie jest najlepsze miejsce do weryfikacji? Wielu sugeruje umieszczenie go w rozszerzonej klasie Request (a także tego, co obecnie robimy), ale co, jeśli chcę sprawdzić nie tylko na żądanie http, ale także na polecenie artisan itp., Czy to naprawdę dobre miejsce?
kingshark
24

Chciałem zamieścić odpowiedź na moje własne pytanie. Mógłbym o tym mówić całymi dniami, ale spróbuję szybko to opublikować, aby mieć pewność, że to zrobię.

Skończyło się na tym, że wykorzystałem istniejącą strukturę, którą zapewnia Laravel, co oznacza, że ​​zachowałem moje pliki głównie jako Model, Widok i Kontroler. Mam również folder Biblioteki dla komponentów wielokrotnego użytku, które tak naprawdę nie są modelami.

NIE OPAKOWAŁEM MOICH MODELI W USŁUGI / BIBLIOTEKI . Wszystkie podane powody nie przekonały mnie w 100% do korzyści płynących z korzystania z usług. Chociaż mogę się mylić, o ile widzę, po prostu skutkują tonami dodatkowych, prawie pustych plików, które muszę tworzyć i przełączać się między nimi podczas pracy z modelami, a także naprawdę zmniejszają korzyści z używania elokwentnego (szczególnie jeśli chodzi o ODZYSKIWANIE modeli , np. przy użyciu paginacji, zakresów itp.).

Umieszczam logikę biznesową W MODELACH i uzyskuję dostęp elokwentny bezpośrednio z moich kontrolerów. Używam kilku podejść, aby upewnić się, że logika biznesowa nie zostanie ominięta:

  • Akcesory i mutatory : Laravel ma świetne akcesory i mutatory. Jeśli chcę wykonać akcję za każdym razem, gdy wiadomość jest przenoszona z wersji roboczej do opublikowanej, mogę to wywołać, tworząc funkcję setIsPublishedAttribute i dołączając tam logikę
  • Zastępowanie tworzenia / aktualizacji itp.: Zawsze możesz zastąpić metody elokwentne w swoich modelach, aby uwzględnić niestandardowe funkcje. W ten sposób możesz wywołać funkcje w dowolnej operacji CRUD. Edycja: Myślę, że jest błąd z nadpisywaniem tworzenia w nowszych wersjach Laravela (więc używam zdarzeń teraz zarejestrowanych w bootowaniu)
  • Uprawomocnienie: W ten sam sposób podpinam moją walidację, np. Uruchomię walidację, zastępując funkcje CRUD, a także akcesory / mutatory, jeśli to konieczne. Aby uzyskać więcej informacji, zobacz Esensi lub dwightwatson / validating.
  • Metody magiczne: używam metod __get i __set moich modeli, aby podłączyć się do funkcjonalności, gdy jest to konieczne
  • Rozszerzanie elokwencji: Jeśli istnieje akcja, którą chcesz wykonać przy wszystkich aktualizacjach / tworzeniu, możesz nawet rozszerzyć elokwencję i zastosować ją do wielu modeli.
  • Wydarzenia: jest to proste i ogólnie uzgodnione miejsce, w którym można to zrobić. Myślę, że największą wadą wydarzeń jest to, że wyjątki są trudne do wyśledzenia (może to nie być nowy przypadek z nowym systemem zdarzeń Laravel). Lubię też grupować moje wydarzenia według tego, co robią, a nie kiedy są wywoływane ... np. Mam abonenta MailSender, który nasłuchuje wydarzeń, które wysyłają pocztę.
  • Dodawanie zdarzeń Pivot / BelongsToMany: Jedną z rzeczy, z którymi borykałem się najdłużej, było powiązanie zachowań z modyfikacją relacji shouldToMany. Np. Wykonywanie akcji za każdym razem, gdy użytkownik dołącza do grupy. Prawie skończyłem dopracowywać niestandardową bibliotekę. Jeszcze go nie opublikowałem, ale działa! Wkrótce spróbuję opublikować link. EDYTUJ Skończyło się na tym, że zrobiłem wszystkie moje zwroty w normalnych modelach, a moje życie było o wiele łatwiejsze ...

Rozwiązywanie problemów ludzi związanych z używaniem modeli:

  • Organizacja: Tak, jeśli dodasz więcej logiki do modeli, mogą być dłuższe, ale ogólnie stwierdziłem, że 75% moich modeli jest nadal dość małych. Jeśli zdecyduję się zorganizować większe, mogę to zrobić za pomocą cech (np. Utworzyć folder dla modelu z kilkoma innymi plikami, takimi jak PostScopes, PostAccessors, PostValidation itp. W razie potrzeby). Wiem, że niekoniecznie do tego służą cechy, ale ten system działa bez problemu.

Dodatkowa uwaga: Czuję, że pakowanie modeli w usługi jest jak posiadanie szwajcarskiego scyzoryka z dużą ilością narzędzi i budowanie wokół niego innego noża, który w zasadzie robi to samo? Tak, czasami możesz chcieć odklejać ostrze lub upewnić się, że dwa ostrza są używane razem ... ale zazwyczaj są na to inne sposoby ...

KIEDY KORZYSTAĆ Z USŁUG : W tym artykule bardzo dobrze przedstawiono WSPANIAŁE przykłady korzystania z usług ( wskazówka: niezbyt często ). Mówi po prostu, że kiedy twój obiekt używa wielu modeli lub modeli w dziwnych częściach ich cyklu życia , ma to sens. http://www.justinweiss.com/articles/where-do-you-put-your-code/

Sabrina Leggett
źródło
2
Ciekawe i aktualne myśli. Ale jestem ciekawy - jak przetestować jednostkową logikę biznesową, jeśli jest ona powiązana z modelami, które są powiązane z Eloquent, który jest powiązany z bazą danych?
JustAMartin
code.tutsplus.com/tutorials/… lub możesz użyć wydarzeń, tak jak powiedziałem, jeśli chcesz to dalej rozłożyć
Sabrina Leggett
1
@JustAMartin czy jesteś pewien, że nie możesz po prostu używać bazy danych w testach jednostkowych? Jaki jest powód, aby tego nie robić? Wiele osób zgadza się, że często można używać bazy danych w testach jednostkowych. (w tym Martin Fowler, martinfowler.com/bliki/UnitTest.html : „Nie traktuję podwójnych dla zasobów zewnętrznych jako absolutnej zasady. Jeśli rozmowa z zasobem jest stabilna i wystarczająco szybka, to nie ma powodu, aby tego nie robić to w twoich testach jednostkowych ”)
Alex P.
@ AlexP11223 Tak, to ma sens. Próbowałem zintegrować SQLite jako moją testową bazę danych i ogólnie wszystko poszło dobrze, chociaż SQLite ma poważne ograniczenia, które muszą być uwzględnione w migracji Laravel i niestandardowych zapytaniach (jeśli występują). Oczywiście nie są to testy stricte jednostkowe, ale testy funkcjonalne, ale dzięki temu są jeszcze wydajniejsze. Mimo to, jeśli chcesz przetestować swój model w całkowitej izolacji (jako ścisły test jednostkowy), może to wymagać zauważalnej ilości dodatkowego kodu (makiety itp.).
JustAMartin,
22

To, czego używam do stworzenia logiki między kontrolerami i modelami, to utworzenie warstwy usług . Zasadniczo jest to mój przepływ dla każdej akcji w mojej aplikacji:

  1. Kontroler odbiera żądane przez użytkownika działanie i wysyła parametry oraz deleguje wszystko do klasy usługi.
  2. Klasa usługi wykonuje całą logikę związaną z operacją: walidację danych wejściowych, rejestrowanie zdarzeń, operacje na bazie danych itp.
  3. Model zawiera informacje o polach, transformacji danych i definicjach walidacji atrybutów.

Oto jak to robię:

Oto metoda kontrolera do tworzenia czegoś:

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);
    }
}

To jest klasa usług, która wykonuje logikę związaną z operacją:

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();
}

A to jest mój model:

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',
        ));
    }
}

Aby uzyskać więcej informacji o tym, jak organizuję swój kod dla aplikacji Laravel: https://github.com/rmariuzzo/Pitimi

Rubens Mariuzzo
źródło
Wygląda na to, że usługi są tym, co w moim poście nazwałem bibliotekami. Myślę, że jest to lepsze niż repozytoria, jeśli nie musisz używać wielu ORMS, ale problem polega na tym, że musisz przenieść cały projekt (co nie musisz robić z wydarzeniami) i wygląda na to, że to po prostu kończy się odzwierciedleniem struktury Modelu, więc masz te wszystkie dodatkowe pliki. Dlaczego po prostu nie uwzględnić tego w modelach? Przynajmniej w ten sposób nie masz dodatkowych plików.
Sabrina Leggett
To interesujące pytanie @SabrinaGelbart, uczono mnie, żebym pozwalał modelom reprezentować jednostki bazy danych i nie mieć żadnej logiki. To jest powód, dla którego stworzyłem te dodatkowe pliki nazwane usługami: aby przechowywać całą logikę i wszelkie dodatkowe operacje. Nie jestem pewien, jakie jest znaczenie wydarzeń, które opisałeś wcześniej, ale myślę, że dzięki usługom i wykorzystaniu wydarzeń Laravel możemy stworzyć wszystkie metody usług, aby uruchamiać zdarzenia na początku i na końcu. W ten sposób każde zdarzenie można całkowicie oddzielić od logiki. Co myślisz?
Rubens Mariuzzo
Nauczono mnie, że także o modelach ... byłoby miło uzyskać dobre wyjaśnienie, dlaczego (może kwestie zależności)?
Sabrina Leggett
Podoba mi się to podejście! Szukałem w Internecie, aby dowiedzieć się, jak powinienem obsługiwać logikę modelu, przejrzałem Repozytoria, ale wydawało się to zbyt skomplikowane i bezużyteczne, aby można było je wykorzystać. Usługi to dobry pomysł. Moje pytanie brzmi po utworzeniu folderu usług w folderze aplikacji, czy musisz dołączyć go do bootstrap / start.php lub gdziekolwiek w celu uruchomienia, ponieważ przeglądałem twój git nie mogłem go znaleźć? @RubensMariuzzo. Czy automatycznie staje się dostępny za pośrednictwem aplikacji? więc możemy po prostu użyć CongregationService :: getCongregations (); ??
Oguzhan
1
Jeśli wszystko, co robisz, to $congregation->save();może nie potrzebujesz repozytoriów. Możesz jednak zauważyć, że z czasem Twoje potrzeby w zakresie dostępu do danych wzrosną. Może zacząć mieć zapotrzebowanie na $congregation->destroyByUser()lub $congregationUsers->findByName($arrayOfSelectedFields);i tak dalej. Dlaczego nie odłączyć usług od potrzeb dostępu do danych. Pozwól, aby reszta Twojej aplikacji działała z obiektami / tablicami zwróconymi z repozytoriów, i po prostu zajmij się manipulowaniem / formatowaniem / itp ... Twoje repozytorium będzie rosło (ale podzieli je na różne pliki, ostatecznie złożoność projektu musi gdzieś znajdować się).
prograhammer
12

Moim zdaniem Laravel ma już wiele opcji przechowywania logiki biznesowej.

Krótka odpowiedź:

  • Użyj Requestobiektów laravel, aby automatycznie sprawdzić poprawność danych wejściowych, a następnie utrwalić dane w żądaniu (utwórz model). Ponieważ wszystkie dane wprowadzane przez użytkowników są bezpośrednio dostępne w żądaniu, uważam, że ma to sens tutaj.
  • Używaj Jobobiektów laravel do wykonywania zadań wymagających pojedynczych komponentów, a następnie po prostu je wysyłaj . Myślę, że Jobobejmują klasy usług. Wykonują zadanie, takie jak logika biznesowa.

Długa odpowiedź:

Użyj repozytoriów, gdy jest to wymagane: repozytoria są na pewno nadmiernie rozbudowane i przez większość czasu są po prostu używane jako accessormodel. Wydaje mi się, że na pewno mają jakieś zastosowanie, ale chyba że tworzysz ogromną aplikację, która tego wymaga takiej elastyczności, abyś mógł całkowicie porzucić laravel, trzymaj się z dala od repozytoriów. Podziękujesz sobie później, a Twój kod będzie znacznie prostszy.

Zadaj sobie pytanie, czy istnieje możliwość, że będziesz zmieniać frameworki PHP pytanie, lub typ bazy danych, którego laravel nie obsługuje.

Jeśli Twoja odpowiedź brzmi „Prawdopodobnie nie”, nie implementuj wzorca repozytorium.

Oprócz powyższego, nie kładź wzoru na wspaniałym ORM, takim jak Eloquent. Po prostu dodajesz złożoność, która nie jest wymagana i nie przyniesie Ci to żadnych korzyści.

Oszczędnie korzystaj z usług: dla mnie klasy usług są po prostu miejscem do przechowywania logiki biznesowej w celu wykonania określonego zadania z określonymi zależnościami. Laravel ma je po wyjęciu z pudełka, zwane „Jobs”, i mają znacznie większą elastyczność niż niestandardowa klasa usług.

Wydaje mi się, że Laravel ma dobrze zaokrąglone rozwiązanie MVCproblemu logiki. To tylko sprawa lub organizacja.

Przykład:

Żądanie :

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;
    }
}

Kontroler :

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();
    }
}

W powyższym przykładzie dane wejściowe żądania są automatycznie sprawdzane, a wszystko, co musimy zrobić, to wywołać metodę utrwalania i przekazać nowy post. Uważam, że czytelność i łatwość konserwacji powinny zawsze przewyższać złożone i niepotrzebne wzorce projektowe.

Następnie możesz użyć dokładnie tej samej metody utrwalania, aby aktualizować posty, ponieważ możemy sprawdzić, czy post już istnieje i wykonać logikę naprzemienną w razie potrzeby.

Steve Bauman
źródło
ale - czy zadania „nie powinny” znajdować się w kolejce? Czasami prawdopodobnie chcemy, aby był w kolejce, ale nie przez cały czas. Dlaczego zamiast tego nie użyć poleceń? A jeśli chcesz napisać logikę biznesową, która może być wykonywana jako polecenie, zdarzenie lub kolejka?
Sabrina Leggett
1
Zadania nie muszą być w kolejce. Określasz to, implementując interfejs w zadaniu, ShouldQueuektóre zapewnia Laravel. Jeśli chcesz napisać logikę biznesową w poleceniu lub zdarzeniu, po prostu uruchom zadanie w tych zdarzeniach / poleceniach. Praca w Laravels jest niezwykle elastyczna, ale ostatecznie są to zwykłe klasy usług.
Steve Bauman