• Czas czytania ~4 min
  • 20.01.2023

Laravel ma fantastyczny kontener do wstrzykiwania zależności, ale wiele osób go unika. W tym samouczku omówię, w jaki sposób opieram się na kontenerze Laravels, aby mój kod działał dla mnie.

Using the container is all about being organized—having a consistent place to keep your container wiązania and naming conventions that make sense and allow you to know what is going on. The container is only as good as the person sticking things into, after all.

Powiedzmy, że chcemy trzymać wszystkie nasze powiązania w jednym miejscu, u dostawcy usług. Brzmi sensownie, prawda? Ale co się dzieje, gdy nasza aplikacja rośnie? Zaczynamy od może 5-6 powiązań dla prostej aplikacji, dodajemy kilka nowych funkcji i musimy dodać powiązania do kontenera. Zanim się zorientujemy, dostawca usług, z którego korzystamy, jest bardzo duży i znalezienie czegokolwiek wymaga dużego wysiłku poznawczego.

Jak możemy z tym walczyć? Jak możemy się upewnić, że nie umieszczamy ich po prostu u usługodawcy, aby ukryć problem? Pozwól, że przedstawię Ci, jak ja do tego podchodzę.

Mój główny AppServiceProvider jest punktem wejścia do mojej aplikacji, więc moim zadaniem jest zarejestrowanie jego kluczowych obszarów. Opieram się na Domain Driven Design, więc mam jednego usługodawcę na domenę. Pozwalam każdej domenie na czyste zarządzanie powiązaniami.

final class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->register(
            provider: AuthDomainServiceProvider::class,
        );
        $this->app->register(
            provider: CommunicationDomainServiceProvider::class,
        );
        $this->app->register(
            provider: WorkDomainServiceProvider::class,
        );
    }
}

Korzystając z tego podejścia, mogę włączać i wyłączać domeny zgodnie z wymaganiami, szybko dodawać nowe i uzyskiwać przegląd wszystkich domen w mojej aplikacji z jednego pliku.

Oczywiście zatrzymuję innych domyślnych dostawców usług z aplikacją Laravel, ponieważ mają swój cel. Zajrzyjmy więc do jednego z dostawców usług domenowych, aby zrozumieć, do czego jest używany.

final class AuthDomainServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->register(
            provider: QueryServiceProvider::class,
        );
        $this->app->register(
            provider: CommandServiceProvider::class,
        );
        $this->app->register(
            provider: FactoryServiceProvider::class,
        );
    }
}

Tak więc mój dostawca usług uwierzytelniania jest używany wyłącznie do rejestrowania aspektów domeny, które muszą pisać swoje powiązania.

Zapytanie — są to operacje odczytu w aplikacji, typowe zapytania, które muszę uruchomić, lub części zapytań, które muszę uruchomić. Napisałem poradnik jak to zrobić tutaj.

Command — Są to operacje zapisu w aplikacji. Zazwyczaj zostaną one przeniesione do zadań w tle, aby aplikacja mogła szybko zareagować.

Fabryka — są to fabryki obiektów danych. Uważam, że obiekty danych stają się duże i nieporządne oraz zajmują dużo miejsca. Moim rozwiązaniem tego problemu było przeniesienie ich do dedykowanych fabryk, których mogłem użyć do stworzenia obiektów danych w mojej aplikacji.

spójrzmy na CommandServiceProvider i jak możemy to wykorzystać do skutecznego rejestrowania poleceń w naszej aplikacji.

final class CommandServiceProvider extends ServiceProvider
{
    public array $bindings = [
        FindOrCreateUserContract::class => FindOrCreateUser::class,
        GenerateApiTokenContract::class => GenerateApiToken::class,
        SendPasswordResetContract::class => SendPasswordReset::class,
    ];
}

Laravel umożliwia korzystanie z bindings właściwość dostawcy usług w celu zarejestrowania wszelkich powiązań, które nie wymagają przekazywania argumentów. Pozwala to zachować porządek u naszych dostawców usług.

Przyjrzyjmy się jednemu z tych powiązań, aby zrozumieć, jak wyglądają i do czego służą.

interface GenerateApiTokenContract
{
    public function handle(Authenticatable $user, DataObjectContract $payload): Model|NewAccessToken;
}

Następnie przechodzimy do realizacji.

final class GenerateApiToken implements GenerateApiTokenContract
{
    public function handle(Authenticatable $user, DataObjectContract $payload): Model|NewAccessToken
    {
        return DB::transaction(
            fn ():  Model|NewAccessToken => $user->createToken(
                name: $payload->name,
            ),
        );
    }
}

Opakowujemy operację zapisu w transakcję bazy danych, a następnie używamy wstrzykniętego modelu użytkownika i wywołujemy na nim metodę create token, przekazując właściwość name z naszego ładunku. Dzięki temu wszystko jest czyste - możesz również użyć tego do wygenerowania tokenów API dla dowolnego użytkownika w swojej aplikacji, a nie tylko aktualnie zalogowanego użytkownika.

Korzystanie z kontenera w ten sposób oznacza, że ​​moje kontrolery są zawsze czyste i minimalistyczne. Spójrzmy na przykładowy kontroler API do logowania użytkownika.

final readonly class LoginController
{
    public function __construct(
        private GenerateApiTokenContract $command,
        private TokenNameGenerator $generator,
    ) {}
    public function __invoke(LoginRequest $request): Responsable
    {
        $request->authenticate();

        return new TokenResponse(
            data: TokenFactory::make(
                data: $this->command->handle(
                    user: auth()->user(),
                    payload: new TokenRequest(
                        name: $generator->generate(),
                    ),
                ),
            ),
        );
    }
}

Oczywiście możesz trochę podzielić ten kod, być może dając mu więcej przestrzeni do oddychania. Ale dla mnie to jest cel, do którego dążę. Opieram się na kontenerze i używam Laravela na swoją korzyść, mając małe ruchome części, które łączą się, aby osiągnąć cel końcowy.

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

O

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

O autorze CrazyBoy49z
WORK EXPERIENCE
Kontakt
Ukraine, Lutsk
+380979856297