Wzorzec Budowniczy w PHP - Praktyczne zastosowanie

27.08.2025

Wzorzec projektowy Budowniczy w PHP 8.4

Standard Budowniczy (Builder) to wzorzec kreacyjny umożliwiający krok po kroku konstruowanie złożonych obiektów. Wykorzystuje fluent interface do intuicyjnego budowania zapytań SQL i konfiguracji.

Dlaczego wzorzec Budowniczy jest ważny?

  • Upraszcza tworzenie obiektów z wieloma opcjonalnymi parametrami,
  • Eliminuje problemy z długimi listami parametrów w konstruktorach,
  • Umożliwia tworzenie różnych reprezentacji tego samego obiektu,
  • Zapewnia czytelność i intuicyjność API poprzez fluent interface.

Kluczowe cechy wzorca Budowniczy

  • Fluent interface - metody zwracają $this dla łańcuchowego wywołania,
  • Krok po kroku - obiekt budowany stopniowo metodami buildera,
  • Walidacja - sprawdzanie poprawności w trakcie budowania,
  • Separacja konstrukcji - oddzielenie procesu budowania od reprezentacji.

Jak działa ten wzorzec?

Wzorzec projektowy Budowniczy (Builder) pozwala krok po kroku konstruować złożone obiekty, oddzielając proces ich tworzenia od reprezentacji. Dzięki temu można łatwo tworzyć różnorodne warianty tego samego obiektu bez konieczności budowania wielu konstruktorów czy używania dużej liczby argumentów. Klasa abstrakcyjna BaseQueryBuilder, która pochodzi z rozdziału 8 ebooka o PHP 8.4, stanowi doskonałą ilustrację tego wzorca, służąc jako podstawa do budowania zapytań SQL przy użyciu intuicyjnego i czytelnego API.

W przykładzie klasy BaseQueryBuilder widoczne jest zastosowanie metody łańcuchowej (fluent interface), co jest charakterystycznym elementem wzorca Builder. Metody takie jak where(), whereLike(), whereNull() czy whereGroup() umożliwiają programiście konstruowanie złożonych warunków zapytań poprzez proste i czytelne wywołania. Każda z tych metod zwraca obiekt typu static, co umożliwia płynne łączenie kolejnych wywołań metod w jeden spójny łańcuch.

<?php

declare(strict_types=1);

namespace DJWeb\Framework\DBAL\Query\Builders;

use DJWeb\Framework\DBAL\Contracts\ConnectionContract;
use DJWeb\Framework\DBAL\Contracts\Query\ConditionContract;
use DJWeb\Framework\DBAL\Contracts\Query\QueryBuilderContract;
use DJWeb\Framework\DBAL\Query\Conditions\AndCondition;
use DJWeb\Framework\DBAL\Query\Conditions\OrCondition;
use DJWeb\Framework\DBAL\Query\Conditions\WhereCondition;
use DJWeb\Framework\DBAL\Query\Conditions\WhereGroupCondition;
use DJWeb\Framework\DBAL\Query\Conditions\WhereLikeCondition;
use DJWeb\Framework\DBAL\Query\Conditions\WhereNotNullCondition;
use DJWeb\Framework\DBAL\Query\Conditions\WhereNullCondition;

abstract class BaseQueryBuilder implements QueryBuilderContract
{
    /**
     * @var array<int, ConditionContract>
     */
    protected array $conditions = [];
    protected string $table;
    /**
     * @var array<int, int|string|float|null>
     */
    protected array $params = [];

    public function __construct(
        protected ConnectionContract $connection
    ) {
    }

    public function where(
        string $column,
        string $operator,
        mixed $value,
        bool $and = true
    ): static {
        $condition = new WhereCondition($column, $operator, $value);
        $item = $and ?
            new AndCondition($condition, count($this->conditions))
            : new OrCondition($condition);
        $this->conditions[] = $item;
        return $this;
    }

    public function whereLike(
        string $column,
        string $pattern,
        bool $and = true
    ): static {
        $condition = new WhereLikeCondition($column, $pattern);
        return $this->whereCondition($condition, $and);
    }

    public function whereNull(string $column, bool $and = true): self
    {
        $condition = new WhereNullCondition($column);
        return $this->whereCondition($condition, $and);
    }

    public function whereGroup(callable $callback, bool $and = true): self
    {
        $groupCondition = new WhereGroupCondition($this);
        $callback($groupCondition);
        return $this->whereCondition($groupCondition, $and);
    }

    private function whereCondition(
        ConditionContract $condition,
        bool $and = true
    ): static {
        $item = $and ?
            new AndCondition($condition, count($this->conditions))
            : new OrCondition($condition);
        $this->conditions[] = $item;
        return $this;
    }
}

Istotnym aspektem implementacji wzorca budowniczy w tym przykładzie jest również delegowanie odpowiedzialności poszczególnym klasom warunków, np. WhereLikeCondition. Taka struktura pozwala utrzymać przejrzystość kodu i separację logiki generowania fragmentów SQL od głównego obiektu budującego. Dzięki temu rozszerzenie o nowe typy warunków czy operatorów SQL jest znacznie prostsze i bardziej zorganizowane – wystarczy stworzyć nową klasę warunku implementującą interfejs ConditionContract.

<?php

declare(strict_types=1);

namespace DJWeb\Framework\DBAL\Query\Conditions;

use DJWeb\Framework\DBAL\Contracts\Query\ConditionContract;

readonly class WhereLikeCondition implements ConditionContract
{
    public function __construct(
        private string $column,
        private string $pattern
    ) {
    }

    public function getSQL(): string
    {
        return "{$this->column} LIKE ?";
    }

    public function getParams(): array
    {
        return [$this->pattern];
    }
}

Zastosowanie wzorca Budowniczy znacząco poprawia czytelność oraz intuicyjność kodu generującego zapytania do bazy danych. Dzięki takiej implementacji, programiści mogą łatwo budować dynamiczne i złożone zapytania, zachowując jednocześnie porządek, elastyczność i łatwość testowania kodu. W efekcie, proces rozwoju i utrzymania aplikacji bazujących na DBAL-u w PHP 8.4 staje się bardziej efektywny i profesjonalny.

🎓 Chcesz poznać więcej wzorców projektowych?

Ten wpis to fragment kompleksowego kursu PHP 8.4, w którym szczegółowo omawiam wszystkie wzorce projektowe wraz z praktycznymi przykładami z rzeczywistych projektów.

Kup pełny kurs PHP 8.4 🚀 Pobierz darmowy fragment 📥

Zobacz też inne wzorce projektowe