• Czas czytania ~3 min
  • 01.02.2023

Duplikacja kodu jest najbardziej bolesną rzeczą dla wielu programistów, myślisz, że rozwiązałeś problem, ale istnieje kilka przypadków tego samego problemu.

W wielu bazach kodu, które widziałem jako programista Laravel, polecenia konsoli zawsze wydają się być zapomnianym obszarem lub częścią, w której ludzie muszą zwracać większą uwagę na jakość.

W tym samouczku omówię, jak można podejść do pisania kodu z naciskiem na ograniczenie duplikacji kodu. Na początek postawmy hipotetyczny. Mamy aplikację Laravel, która jest sklepem e-commerce i raz dziennie chcemy generować raport o całej sprzedaży i statusie przesyłek. Nasze obecne podejście polega na zalogowaniu się do panelu administracyjnego i kliknięciu przycisku, aby wygenerować raport.

Może to być nierealne, ponieważ pierwszą instancją byłoby zautomatyzowanie go. Ale zostań ze mną przez chwilę, gdy zaryzykujemy ten pomysł, aby go ulepszyć.

Naszym pierwszym krokiem będzie stworzenie polecenia rzemieślniczego lub zestawu poleceń rzemieślniczych, które będą generować raporty. Gdy zaczynamy przyglądać się raportowaniu, sensowne jest posiadanie poleceń wyraźnie nazwanych tym, co chcemy osiągnąć. Zacznijmy od wyników sprzedaży.

final class SalesFigures extends Command
{
    public $signature = 'reports:sales';

    public $description = 'Run a daily report on sales.';

    public function handle(): int
    {
        $date = now()->subDay();

        $sales = Order::query()
            ->where('status', Status::COMPLETE)
            ->whereBetween(
                'completed_at',
                $date->startOfDay(),
                $date->endOfDay(),
            )->latest()->get();

        // send information through to the report builder
    }
}

Mamy tutaj proste polecenie, że będziemy mogli uruchomić i uzyskać dane dotyczące sprzedaży na wczoraj, które są oznaczone jako kompletne. Samo zapytanie jest dość proste. Sprawdza status i datę wczoraj, a następnie je zamawia, więc najnowsze jest pierwsze - aby umożliwić nam zbudowanie chronologicznego raportu.

Jak jednak możemy to poprawić? Czy istnieją inne aspekty aplikacji, w których musimy uzyskać te zamówienia w podobnej kolejności? Zacznijmy proces refaktoryzacji.

Po pierwsze, to konkretne zapytanie jest czymś, co musimy uruchomić w kilku różnych obszarach. Możemy więc przenieść to do własnej klasy, abyśmy mogli ją uruchomić.

final class ResultsForPeriod implements ResultsForPeriodContract
{
    public function handle(
        Builder $query,
        CarbonInterface $start,
        CarbonInterface $end,
    ): Builder {
        return $builder->whereBetween(
            'completed_at',
            $start,
            $end,
        );
    }
}

Pozwoli nam to uzyskać wyniki dla określonego przedziału czasowego w dowolnych modelach - co jest bardziej korzystne dla projektu.

final class SalesFigures extends Command
{
    public $signature = 'reports:sales';

    public $description = 'Run a daily report on sales.';

    public function handle(ResultsForPeriodContract $query): int
    {
        $date = now()->subDay();

        $sales = $query->handle(
            query: Order::query()
                ->where('status', Status::COMPLETE),
            start: $date->startOfDay(),
            end: $date->endOfDay()
        )->latest()->get();

        // send information through to the report builder
    }
}

Zaimplementowaliśmy zapytanie, które stworzyliśmy do filtrowania na podstawie przedziału czasowego. Gdzie jeszcze możemy to zrobić, aby uczynić go czystszym i bardziej wydajnym? Czy moglibyśmy stworzyć specjalną usługę, która obsługiwałaby ten aspekt raportowania? Czy ta usługa jest pomocna również w innych obszarach?

Nasz pulpit nawigacyjny e-commerce prawdopodobnie będzie zawierał pewne informacje z tych raportów, więc niektóre ponowne użycie kodu jest już gotowe. Przenieśmy to do usługi.

final class ReportService implements ReportServiceContract
{
    public function __construct(
        private readonly ResultsForPeriodContract $periodFilter,
    ) {}
    public function dailySales(CarbonInterface $start, CarbonInterface $end): Collection
    {
        return $this->periodFilter->handle(
            query: Order::query()->where('status', Status::COMPLETE),
        )->latest()->get();
    }
}

Możemy teraz przenieść to z powrotem do dowództwa rzemieślniczego.

final class SalesFigures extends Command
{
    public $signature = 'reports:sales';

    public $description = 'Run a daily report on sales.';

    public function handle(ReportServiceContract $service): int
    {
        $date = now()->subDay();

        $sales = $service->dailySales(
            query: Order::query(),
            start: $date->startOfDay(),
            end: $date->endOfDay()
        );
        // send information through to the report builder
    }
}

Jak widać, mamy nowe czyste polecenie, które ładnie wykorzystuje współdzielony kod z innymi obszarami naszej aplikacji.

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