Rozbicie routes na kilka plików w Laravel

Gdy aplikacja robi się coraz większa dla czytelności można chcieć podzielić plik routes/web.php na kilka mniejszych.

Aby to zrealizować moją pierwszą myślą było po prostu include wydzielonych plików z trasami:

// routes/web.php

require_once 'web/redirects.php';
require_once 'web/website.php';
require_once 'web/admin.php';

Mimo iż to jest złe podejście to po tej zmianie aplikacja działała całkowicie poprawnie.

Problemy zaczęły się gdy wykonałem testy PHPUnit

    public function test_home()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }

    public function test_posts()
    {
        $response = $this->get('/posts');

        $response->assertStatus(200);
    }

Pierwszy test zawsze przechodził jednak następne (bez względu czy w tym samym czy innym pliku) zwracały błąd:

Route [home] not defined.

Okazuje się, że mechanika testująca Laravela wczytuje jedynie plik routes/web.php

Aby poprawnie rozbić routes na kilka plików zamiast includów w samym pliku trzeba dostarczyć ścieżki w Provoderze.

Poprawne rozbicie routes na wiele plików

// App\Providers\RouteServiceProvider.php;

Route::middleware('web')
    ->namespace($this->namespace)
    ->group(
        [
            base_path('routes/web.php'),
            base_path('routes/web/redirects.php'),
            base_path('routes/web/website.php'),
            base_path('routes/web/admin.php'),
        ]
    );

Wzorzec projektowy Strategy. Elastyczność i niezależność w twoim kodzie.

Wzorzec projektowy Strategy przydaje się do definiowania rodziny zachowań, algorytmów lub strategii, które mogą być wymieniane przez aplikację. Umożliwia to programowanie do interfejsu, a nie konkretnej implementacji.

Do konkretnych zastosowań wzorca Strategy należą:

  • Abstrakcja algorytmów: Strategy pozwala na oddzielenie logiki algorytmu od klienta, co pozwala na łatwą zmianę implementacji algorytmu bez konieczności modyfikacji kodu klienta.
  • Elastyczność: Aplikacja może zmienić strategię w trakcie działania, co pozwala na dostosowanie działania aplikacji do różnych potrzeb czy sytuacji.
  • Łatwe testowanie: oddzielenie algorytmów od reszty aplikacji pozwala na łatwe testowanie poszczególnych implementacji algorytmów.
  • Skalowalność: rozdzielenie algorytmów

Wzorzec projektowy Strategy – przykład implementacji

Poniżej przedstawiam przykład implementacji wzorca projektowego Strategy w PHP:

interface SortStrategy {
    public function sort(array $data): array;
}

class BubbleSort implements SortStrategy {
    public function sort(array $data): array {
        // implementacja sortowania bąbelkowego
        return $data;
    }
}

class quickSort implements SortStrategy {
    public function sort(array $data): array {
        // implementacja sortowania szybkiego
        return $data;
    }
}

class Sorter {
    protected $sortStrategy;

    public function __construct(SortStrategy $sortStrategy) {
        $this->sortStrategy = $sortStrategy;
    }

    public function setSortStrategy(SortStrategy $sortStrategy) {
        $this->sortStrategy = $sortStrategy;
    }

    public function sort(array $data): array {
        return $this->sortStrategy->sort($data);
    }
}

Kod powyżej zawiera interfejs SortStrategy, który zawiera metodę sort(array $data): array. Klasy bubbleSort i quickSort implementują ten interfejs i zwracają posortowane dane.

Klasa Sorter jest klasą klienta, która przyjmuje obiekt implementujący interfejs SortStrategy w konstruktorze, a następnie używa tego obiektu do wywołania metody sort(array $data) .

$data = [5, 1, 3, 8, 2, 9];

$sorter = new Sorter(new bubbleSort());
$sorter->sort($data);

$sorter->setSortStrategy(new quickSort());
$sorter->sort($data);

W powyższym przykładzie tworzymy instancję klasy Sorter z podaną implementacją sortowania, która jest przypisana do pola sortStrategy. W kolejnym kroku dzięki metodzie setSortStrategy zmieniamy implementację sortowania na inną.

Wzorzec strategy pozwala nam na dynamiczne zmienienie implementacji algorytmu, pozwala na oddzielenie logiki algorytmu od klienta oraz umożliwia tworzenie nowych implementacji bez konieczności modyfikacji kodu klienta.

Wzorzec projektowy Strategy w Laravel

W Laravelu wzorzec Strategy jest używany w kilku miejscach, w tym:

Obsługa różnych formatów danych – Laravel używa strategii do obsługi różnych formatów danych, takich jak json, xml czy też csv. Dzięki temu możemy łatwo dostosować format danych który jest zwracany przez API.

Obsługa różnych metod płatności – Laravel używa strategii do obsługi różnych metod płatności, takich jak PayPal czy też Stripe. Dzięki temu możemy łatwo dostosować system płatności do potrzeb naszej aplikacji.

Obsługa różnych metod wysyłki – Laravel używa strategii do obsługi różnych metod wysyłki, takich jak Poczta Polska czy też DHL. Dzięki temu możemy łatwo dostosować system wysyłki do potrzeb naszej aplikacji.

Widać więc, że wzorzec strategy jest ważnym elementem w Laravelu, ponieważ pozwala na dynamiczne dostosowywanie działania aplikacji do różnych potrzeb i sytuacji, co wpływa na elastyczność i skalowalność aplikacji. Dzięki temu, logika działania jest oddzielona od reszty aplikacji, co pozwala na łatwe testowanie oraz rozwijanie poszczególnych modułów.

Wzorzec projektowy Fabryka. Twoja szansa na zwiększenie czytelności i testowalności kodu.

Wzorzec projektowy Fabryka to Twoja przepustka do projektowania modularnego i skalowalnego kodu.

Wzorzec projektowy Fabryka jest przydatny do tworzenia obiektów. Głównym celem tego wzorca jest oddzielenie logiki tworzenia obiektów od reszty aplikacji oraz umożliwienie łatwego tworzenie różnych typów obiektów.

Do konkretnych zastosowań wzorca Fabryka należą:

  • Abstrakcja tworzenia obiektów – fabryka jest odpowiedzialna za tworzenie obiektów, a nie klient, dlatego logika tworzenia obiektów jest oddzielona od reszty aplikacji.
  • Tworzenie różnych typów obiektów – fabryka może tworzyć różne typy obiektów, które implementują tę samą klasę bazową lub interfejs. To pozwala na łatwe rozszerzanie aplikacji o nowe funkcjonalności.
  • Tworzenie obiektów z danymi testowymi – fabryki często są używane do tworzenia obiektów z danymi testowymi, co jest przydatne przy tworzeniu testów jednostkowych.
  • Elastyczność – Wzorzec fabryka umożliwia na dynamiczne tworzenie różnych implementacji tej samej klasy bazowej czy interfejsu.

Wzorzec projektowy Fabryka – przykład implementacji

Poniżej przedstawiam przykład implementacji wzorca projektowego Fabryka w PHP:

interface Vehicle {
    public function getType(): string;
}

class Car implements Vehicle {
    public function getType(): string {
        return "Car";
    }
}

class Bike implements Vehicle {
    public function getType(): string {
        return "Bike";
    }
}

class VehicleFactory {
    public static function createVehicle(string $type): Vehicle {
        switch ($type) {
            case "car":
                return new Car();
            case "bike":
                return new Bike();
            default:
                throw new Exception("Invalid vehicle type");
        }
    }
}

Kod powyżej zawiera interfejs Vehicle, który zawiera metodę getType(). Klasy Car i Bike implementują ten interfejs i zwracają odpowiedni typ pojazdu. Klasa VehicleFactory jest fabryką, która posiada statyczną metodę createVehicle(), która jest odpowiedzialna za tworzenie obiektów klas Car i Bike. Metoda ta przyjmuje jako argument typ pojazdu, a następnie w zależności od tego jaki typ jest podany zwraca odpowiedni obiekt.

$car = VehicleFactory::createVehicle("car");
echo $car->getType(); // "Car"

$bike = VehicleFactory::createVehicle("bike");
echo $bike->getType(); // "Bike"

W powyższym przykładie używamy metody createVehicle z klasy VehicleFactory by utworzyć instancję klasy Car lub Bike.

Warto zauważyć, że zwiększa to abstrakcję i oddziela logikę tworzenia obiektów od reszty aplikacji. Dzięki temu w razie potrzeby, możemy łatwo zmienić sposób tworzenia obiektów bez wpływu na pozostałą część aplikacji.

Warto wspomnieć że wzorzec Fabryka jest często używany w połączeniu z innymi wzorcami projektowymi takimi jak np. Wzorzec projektowy Strategia czy też Wzorzec projektowy Builder. Dzięki temu połączeniu, tworzą one skomplikowany system, który jest bardziej elastyczny i łatwy w utrzymaniu.

Wzorzec projektowy Fabryka w Laravel

W Laravelu wzorzec fabryka jest używany w kilku miejscach, w tym:

Tworzenie obiektów modeli – Laravel używa fabryk do tworzenia obiektów modeli, które reprezentują rekordy z bazy danych. Fabryki pozwalają na łatwe utworzenie obiektów z danymi testowymi, co jest bardzo przydatne przy tworzeniu testów jednostkowych.

Tworzenie migracji – Laravel używa fabryk do generowania danych testowych do migracji, które pozwalają na przetestowanie aplikacji przed uruchomieniem jej na produkcji.

Tworzenie przykładowych danych – Laravel udostępnia także API do tworzenia przykładowych danych, które są generowane przez fabryki.

Tworzenie modeli w kontrolerach – Fabryki Laravel pozwalają także na proste tworzenie modeli bezpośrednio w kontrolerach, w związku z tym użycie konstruktora nie jest konieczne.

Widać więc, że fabryki są istotnym elementem w Laravelu, ponieważ pozwalają na generowanie danych testowych, tworzenie modeli i migracji, co wpływa na poprawienie przetestowalności aplikacji. Dzięki fabrykom kod jest bardziej czytelny, ponieważ logika tworzenia obiektów jest oddzielona od reszty aplikacji.

Wzorzec projektowy Fabryka w Symfony

W Symfony możemy również użyć fabryki do tworzenia instancji usług zależnie od określonych warunków lub konfiguracji.

Na przykład, możemy stworzyć fabrykę, która na podstawie parametru konfiguracyjnego utworzy odpowiednią instancję usługi do obsługi operacji na plikach.

class FileServiceFactory {
    public static function create(string $storageType): FileServiceInterface {
        switch ($storageType) {
            case 'local':
                return new LocalFileService();
            case 'cloud':
                return new CloudFileService();
            default:
                throw new \InvalidArgumentException('Invalid storage type');
        }
    }
}

// Użycie fabryki
$fileService = FileServiceFactory::create('cloud');
$fileService->uploadFile($file);