Magento 2 to jeden z najbardziej wzorcowych (dosłownie) frameworków PHP. Jego architektura jest niemal podręcznikowym przykładem zastosowania wzorców GoF. Jeśli rozumiesz wzorce omówione w tej serii, czytanie kodu Magento staje się znacznie łatwiejsze – zamiast „skąd się to bierze” masz „aha, to Factory” albo „to Observer przez di.xml”. Pokazuję gdzie konkretnie każdy wzorzec żyje w platformie.
Wzorce kreacyjne w Magento 2
Factory Method i Abstract Factory
Magento 2 generuje klasy Factory automatycznie dla każdego modelu. Gdy w konstruktorze zadeklarujesz ProductFactory, Magento generuje plik generated/code/Magento/Catalog/Model/ProductFactory.php z metodą create(). To klasyczny Factory Method – fabryka wie jak stworzyć produkt, Ty tylko prosisz ją o nowy egzemplarz.
<?php
// Factory Method - automatycznie generowane przez Magento
use Magento\Catalog\Model\ProductFactory;
class MyService
{
public function __construct(
private ProductFactory $productFactory
) {}
public function createProduct(array $data): \Magento\Catalog\Model\Product
{
// create() to factory method - zawsze nowa instancja
return $this->productFactory->create(['data' => $data]);
}
}
// Abstract Factory - rodziny powiązanych obiektów
// Magento\Framework\DB\Adapter\AdapterInterface tworzy spójną rodzinę:
// - zapytania SELECT, INSERT, UPDATE, DELETE
// - obsługę transakcji
// - budowanie warunków WHERE
// Podmiana adaptera (MySQL -> PostgreSQL) zmienia całą rodzinę operacji
Singleton
ObjectManager Magento działa domyślnie jako singleton w obrębie requestu – każda klasa zarejestrowana bez shared="false" jest tworzona raz i zwracana przy kolejnych żądaniach. To „managed singleton” zarządzany przez kontener DI, a nie klasyczny getInstance().
<?php // Singleton przez DI - Magento tworzy instancję raz i reużywa // (shared="true" to domyślne zachowanie) $serviceA = $objectManager->get(MyService::class); $serviceB = $objectManager->get(MyService::class); // $serviceA === $serviceB - ten sam obiekt // Non-singleton - shared="false" w di.xml lub przez Factory // Każde create() zwraca nową instancję $product1 = $productFactory->create(); $product2 = $productFactory->create(); // $product1 !== $product2
Builder
Magento\Framework\Api\SearchCriteriaBuilder to podręcznikowy przykład wzorca Builder. Budujesz złożone kryterium wyszukiwania krok po kroku przez fluent interface, a create() zwraca gotowy, zwalidowany obiekt:
<?php
// Builder - SearchCriteriaBuilder
$searchCriteria = $this->searchCriteriaBuilder
->addFilter('status', 1)
->addFilter('visibility', [2, 3, 4], 'in')
->addSortOrder(
$this->sortOrderBuilder
->setField('name')
->setAscendingDirection()
->create()
)
->setPageSize(20)
->setCurrentPage(1)
->create(); // build() - zwraca gotowy SearchCriteria
// Inne Buildery w Magento:
// - Magento\Framework\Api\SortOrderBuilder
// - Magento\Ui\Component\Form\Field\Builder (UI Components)
Wzorce strukturalne w Magento 2
Decorator
System pluginów Magento 2 (Interceptors) to implementacja wzorca Decorator przez AOP. Magento generuje klasy Interceptor które owijają oryginalne klasy warstwami pluginów:
<?php
// Plugin "after" - Decorator dodający funkcjonalność po wywołaniu metody
class ProductNamePlugin
{
public function afterGetName(
\Magento\Catalog\Model\Product $subject,
string $result
): string {
// Dekorujemy wynik - dodajemy sufiks
return $result . ' [Promocja]';
}
}
// Magento generuje:
// Magento\Catalog\Model\Product\Interceptor extends Product
// który opakowuje oryginalne metody pluginami
// - to jest wzorzec Decorator wygenerowany automatycznie
Proxy
Magento automatycznie generuje klasy Proxy dla klas z ciężkimi konstruktorami. Plik generated/code/Magento/Catalog/Model/ResourceModel/Product/Proxy.php implementuje lazy loading – prawdziwy obiekt tworzony jest dopiero przy pierwszym wywołaniu metody:
<?xml version="1.0"?>
<config>
<!-- Wstrzyknięcie Proxy zamiast ciężkiej klasy -->
<type name="Vendor\Module\Model\MyService">
<arguments>
<argument name="heavyDependency" xsi:type="object">
Magento\Catalog\Model\ResourceModel\Product\Proxy
</argument>
</arguments>
</type>
</config>
Adapter
Magento\Framework\DB\Adapter\AdapterInterface i jego implementacje to Adapter – ujednolicają dostęp do różnych baz danych za jednym interfejsem. Magento\Framework\Filesystem\DriverInterface adaptuje operacje na plikach niezależnie od systemu plików (lokalny, remote, stream).
Facade
Magento\Framework\App\Helper\AbstractHelper oraz helpery modułów jak Magento\Catalog\Helper\Data to Facade – upraszczają dostęp do złożonych podsystemów przez jeden obiekt. Podobnie Magento\Framework\View\Result\PageFactory ukrywa złożoność budowania strony wynikowej.
Wzorce behawioralne w Magento 2
Observer
System zdarzeń Magento 2 to pełna implementacja Observer. Magento\Framework\Event\Manager pełni rolę Subject, a każdy observer zarejestrowany w events.xml to Concrete Observer:
<?xml version="1.0"?>
<config>
<event name="sales_order_place_after">
<observer name="vendor_module_notify"
instance="Vendor\Module\Observer\NotifyErp"/>
</event>
</config>
<?php
// Concrete Observer
class NotifyErp implements \Magento\Framework\Event\ObserverInterface
{
public function execute(\Magento\Framework\Event\Observer $observer): void
{
$order = $observer->getData('order');
// reaguj na zdarzenie - Subject (EventManager) nie zna tej klasy
}
}
Strategy
System obliczania cen w Magento to wielowarstwowy Strategy. Magento\Framework\Pricing\Price\Collection trzyma zbiór strategii cenowych (RegularPrice, SpecialPrice, TierPrice), każda implementuje PriceInterface. Podobnie metody wysyłki i płatności to Strategy – każda implementuje MethodInterface i może być podmieniona przez konfigurację.
<?php // Strategy - ceny produktu use Magento\Catalog\Pricing\Price\RegularPrice; use Magento\Catalog\Pricing\Price\SpecialPrice; use Magento\CatalogRule\Pricing\Price\CatalogRulePrice; // Każda implementuje PriceInterface - wymienne strategie obliczania ceny $regularPrice = $product->getPriceInfo()->getPrice(RegularPrice::PRICE_CODE); $specialPrice = $product->getPriceInfo()->getPrice(SpecialPrice::PRICE_CODE); $rulePrice = $product->getPriceInfo()->getPrice(CatalogRulePrice::PRICE_CODE);
Command
Magento Command w dwóch miejscach: system komend CLI (bin/magento) oparty na Symfony Console, oraz Message Queue Framework gdzie każda wiadomość w kolejce to Command – serializowany obiekt z danymi do wykonania przez Consumer.
<?php
// Command - komenda CLI
class ReindexCommand extends \Symfony\Component\Console\Command\Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
// execute() to factory method z Command GoF
return Command::SUCCESS;
}
}
// Command - wiadomość w Message Queue
// Publisher wysyła komendę, Consumer wykonuje - klasyczny Command pattern
Chain of Responsibility
Pipeline przetwarzania płatności w Magento to Chain of Responsibility. Każdy krok (walidacja, autoryzacja, capture, settlement) to handler który może przerwać łańcuch lub przekazać dalej. Podobnie Magento\Framework\App\Router\Base i system routerów – każdy router próbuje dopasować URL, przy sukcesie zatrzymuje łańcuch.
<?php // Chain of Responsibility - router Magento // FrontController::dispatch() przechodzi przez listę routerów: // 1. Magento\Framework\App\Router\Base (standardowe akcje) // 2. Magento\Cms\App\Router (strony CMS) // 3. Magento\UrlRewrite\App\Router (przepisania URL) // 4. Magento\Framework\App\Router\DefaultRouter (404) // Pierwszy który dopasuje - zatrzymuje łańcuch
Mapa wzorców GoF w Magento 2
| Wzorzec | Gdzie w Magento 2 |
|---|---|
| Factory Method | Generowane klasy *Factory, ObjectManager |
| Abstract Factory | DB Adapter, Filesystem Driver |
| Singleton | Shared instances w ObjectManager (domyślne) |
| Builder | SearchCriteriaBuilder, SortOrderBuilder |
| Decorator | System pluginów – generowane Interceptory |
| Proxy | Generowane klasy *Proxy (lazy loading) |
| Adapter | DB Adapter, Filesystem Driver, Cache Backend |
| Facade | Helper klasy, PageFactory, Context |
| Observer | System zdarzeń – events.xml + ObserverInterface |
| Strategy | PriceInterface, MethodInterface (shipping/payment) |
| Command | CLI commands, Message Queue messages |
| Chain of Responsibility | Router pipeline, payment processing pipeline |
Podsumowanie serii
Wzorce GoF to język którym możesz opisywać architekturę systemu – szybciej i precyzyjniej niż długimi opisami. Gdy mówisz „to jest Observer przez di.xml” albo „użyjmy tu Buildera bo konstruktor rośnie” – każdy developer w zespole od razu wie o czym mówisz.
Magento 2 jest zbudowane na tych wzorcach konsekwentnie i celowo. Architekci platformy przez lata dopracowywali sposób w jaki DI, pluginy, eventy i fabryki współpracują ze sobą. Rozumienie wzorców GoF sprawia że ta architektura przestaje być chaosem XML-i i klas, a staje się spójnym systemem z jasną logiką decyzji projektowych.
