Dlaczego niektóre klasy definiują zastrzyki zarówno w swoim konstruktorze, jak i pliku di.xml?

12

Nie rozumiem, dlaczego w niektórych klasach ich zastrzyki zależności są deklarowane dwukrotnie - raz w di.xmlkonstruktorze konkretnej klasy.

Na przykład w Magento\Backend\Model\Urljego di.xmlzdefiniowano następujący zestaw typów dla DI:

<type name="Magento\Backend\Model\Url">
    <arguments>
        <argument name="scopeResolver" xsi:type="object">
Magento\Backend\Model\Url\ScopeResolver</argument>
        <argument name="authSession" xsi:type="object">
Magento\Backend\Model\Auth\Session\Proxy</argument>
        <argument name="formKey" xsi:type="object">
Magento\Framework\Data\Form\FormKey\Proxy</argument>
        <argument name="scopeType" xsi:type="const">
Magento\Store\Model\ScopeInterface::SCOPE_STORE </argument>
        <argument name="backendHelper" xsi:type="object">
Magento\Backend\Helper\Data\Proxy</argument>
    </arguments>
</type>

Ale jednocześnie, w swojej konkretnej klasie, klasy zdefiniowane w pliku di.xml wymagane do wstrzyknięcia są ponownie deklarowane w konstruktorze:

<?php
    public function __construct(
        \Magento\Framework\App\Route\ConfigInterface $routeConfig,
        \Magento\Framework\App\RequestInterface $request,
        \Magento\Framework\Url\SecurityInfoInterface $urlSecurityInfo,
        \Magento\Framework\Url\ScopeResolverInterface $scopeResolver,
        \Magento\Framework\Session\Generic $session,
        \Magento\Framework\Session\SidResolverInterface $sidResolver,
        \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory,
        \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        $scopeType,
        \Magento\Backend\Helper\Data $backendHelper,
        \Magento\Backend\Model\Menu\Config $menuConfig,
        \Magento\Framework\App\CacheInterface $cache,
        \Magento\Backend\Model\Auth\Session $authSession,
        \Magento\Framework\Encryption\EncryptorInterface $encryptor,
        \Magento\Store\Model\StoreFactory $storeFactory,
        \Magento\Framework\Data\Form\FormKey $formKey,
        array $data = []
) {
    //...
}
?>

Jeśli spojrzymy na jego konstruktor powyżej \Magento\Framework\App\Route\ConfigInterface $routeConfig, na przykład nie jest zdefiniowany w di.xml. Jest zdefiniowany tylko w konstruktorze, a Magento nadal będzie wprowadzał routeConfigklasę do użytku, prawda? To samo dotyczy \Magento\Framework\Encryption\EncryptorInterface $encryptorkilku innych.

Dlaczego więc istnieje potrzeba zdefiniowania innych zastrzyków zarówno di.xmlw konstruktorze, jak i w konstruktorze, skoro posiadanie tych deklaracji w konstruktorze jest wystarczające, aby Magento wstrzyknął te zależności do klasy w celu użycia?

ksenon
źródło

Odpowiedzi:

15

Jak stwierdzono w dokumentacji , w Magento 2 di.xmlmożna użyć do:

Możesz skonfigurować argumenty konstruktora klasy w swoim di.xmlwęźle argumentów. Menedżer obiektów wstrzykuje te argumenty do klasy podczas tworzenia. Nazwa argumentu skonfigurowanego w pliku XML musi odpowiadać nazwie parametru w konstruktorze w skonfigurowanej klasie.

W twoim przypadku jest to trochę skomplikowane, wyjaśnię każdy argument jeden po drugim:

  • \Magento\Framework\App\Route\ConfigInterface $routeConfig: jest to interfejs, więc nie można go bezpośrednio używać . Preferencji dla tej klasy jest zdefiniowany wapp/etc/di.xml i jest to Magento\Framework\App\Route\Configklasa
  • \Magento\Framework\App\RequestInterface $request : to samo dotyczy tej klasy, preferowane jest Magento\Framework\App\Request\Http
  • \Magento\Framework\Url\SecurityInfoInterface $urlSecurityInfo: ta sama sprawa tutaj ponownie z Magento\Framework\Url\SecurityInfo\Proxypreferencją
  • \Magento\Framework\Url\ScopeResolverInterface $scopeResolver: tutaj zaczynamy od interesującego fragmentu. W app/etc/di.xmlpreferencją jest zdefiniowana dla tego interfejsu i jest to Magento\Framework\Url\ScopeResolverklasa. Jednak dla Magento\Backend\Model\UrlMagento 2 należy użyć innej klasy, a zatem określa, która klasa w di.xmlopublikowanym przez Ciebie elemencie Magento\Backend\Model\Url\ScopeResolverbędzie używana.
  • \Magento\Framework\Session\Generic $session jest to normalna klasa i dlatego może być używana jako taka.
  • \Magento\Framework\Session\SidResolverInterface $sidResolver: powrót do interfejsu, preferencja jest nadal zdefiniowana w app/etc/di.xmli tak jestMagento\Framework\Session\SidResolver\Proxy
  • \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory : jest to klasa fabryczna, więc może być używana jako taka.
  • \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver: wracamy do naszego app/etc/di.xmli preferujemy:Magento\Framework\Url\QueryParamsResolver
  • \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig: inny przypadek tutaj, w którym ** zdefiniowano preferencję app/etc/di.xmli tak jest Magento\Framework\App\Config.
  • $scopeType: tutaj mamy tylko zmienną bez żadnej klasy przed nią. Twój moduł di.xmlokreśla, że Magento\Store\Model\ScopeInterface::SCOPE_STOREpowinna być używana jako wartość tej zmiennej. **
  • \Magento\Backend\Helper\Data $backendHelper: tutaj moglibyśmy użyć tej klasy jako takiej. Jednak tutaj używany jest serwer proxy, ponieważ ta klasa niekoniecznie jest używana (zobacz ten post, aby uzyskać szczegółowe informacje na temat klas serwerów proxy: Magento 2: praktyczne wyjaśnienie, czym jest klasa proxy? )
  • \Magento\Backend\Model\Menu\Config $menuConfig : możemy użyć tej klasy jako takiej.
  • \Magento\Framework\App\CacheInterface $cache: inna preferencja zdefiniowana app/etc/di.xmldla tego interfejsu, którą jestMagento\Framework\App\Cache\Proxy
  • \Magento\Backend\Model\Auth\Session $authSession: to samo znowu tutaj moglibyśmy użyć tej klasy, ale zamiast tego używamy klasy proxy do leniwego ładowania.
  • \Magento\Framework\Encryption\EncryptorInterface $encryptor: przeskakując do app/etc/di.xmltyłu i uważamy, że Magento\Framework\Encryption\Encryptorjest to preferencja
  • \Magento\Store\Model\StoreFactory $storeFactory : fabryka, abyśmy mogli używać jej jako takiej.
  • \Magento\Framework\Data\Form\FormKey $formKey: tutaj ponownie używamy Magento\Framework\Data\Form\FormKey\Proxyklasy proxy do leniwego ładowania.
  • array $data = []: ten zawsze pojawia się na końcu i domyślnie jest automatycznie ustawiany na pustą tablicę. Więcej informacji znajdziesz tutaj: Magento 2: jaki jest parametr konstruktora tablic danych $?

Podsumowując

Globalnie parametry konstruktorów klas są interfejsami lub klasami, które nie są możliwe do utworzenia. Dzięki temu di.xmlmożesz dostosować zależności, których chcesz używać dla każdego konstruktora klasy. Dotyczy to także klas możliwych do utworzenia. Na przykład konstruktor klasy, który przyjmuje klasę produktu jako argument konstruktora. Można go dostosować w konfigurowalnym module produktu, dlatego zamiast argumentu przyjmuje konfigurowalną klasę produktu.

Raphael at Digital Pianism
źródło
Czy zawsze wymagana jest preferencja dla parametru interfejsu? Czy można to uznać za awarię? Czy sensowne jest po prostu zdefiniowanie konkretnego argumentu w konfiguracji bez preferencji? Czy to nie jest możliwe?
robsch
6

Ważne jest zrozumienie różnicy między definicją zależności a konfiguracją zależności.

Zależności nie są zdefiniowane w pliku di.xml. Zależności są definiowane wewnątrz konstruktora odpowiedniej klasy poprzez określenie interfejsu, abstraktu lub fabryki jako typu tej konkretnej zależności, np. $routeConfigJest zależnością typu \Magento\Framework\App\Route\ConfigInterface.

Z drugiej strony di.xmljest to miejsce do konfigurowania zależności za pomocą <preference/>węzłów i / lub xpath:type/arguments/argumentwęzłów (czasami w połączeniu z bardziej zaawansowanymi węzłami konfiguracji, takimi jak <virtualType/>lub <proxy/>). Konfiguracja zależności oznacza po prostu odwzorowanie argumentu konstruktora obiektu na implementację / obiekt / konkret .

Chcesz, aby zależności były konfigurowalne za pomocą di.xml, dzięki czemu możesz je zamienić i użyć innej implementacji dla określonego interfejsu lub argumentu pod pewnymi warunkami (czytaj dalej przykład, aby zrozumieć, co pewne warunki powinny oznaczać).

Na przykład, opracowując rozszerzenie, najpierw należy utworzyć nową klasę (tę klasę nazywamy implementacją ). Twoja nowa klasa implementuje \Magento\Framework\App\Route\ConfigInterfaceinterfejs i ma w sobie konkretną funkcjonalność, która honoruje umowę interfejsu. Teraz zaczyna się część konfiguracyjna : aby powiedzieć Magento, aby użyła nowo zdefiniowanej implementacji, musisz skonfigurować tę implementację jako zależność od obiektuMagento\Backend\Model\Url . Wykonujesz tę konfigurację w di.xmlplikach lub w module. W takim przypadku musisz użyć <preference/>węzła, aby odwzorować interfejs na nową implementację. Innym razem używałbyś bardziej szczegółowego xpath:type/arguments/argument di.xmlwęzła doodwzoruj tylko konkretne argumenty (aka zależności, aka interfejsy) konkretnego na konkretne implementacje . Teraz twoja implementacja będzie aktywna jako zależność dla obiektu tylko \Magento\Backend\Model\Url w określonych warunkach , np. W przepływie wykonania kodu bieżącego żądania aplikacji Magento\Backend\Model\Urltworzony jest obiekt typu i potrzebuje on implementacji dla zdefiniowanej przez konstruktora zależności, $routeConfigktóra nazywa się typu \Magento\Framework\App\Route\ConfigInterface.

To prawie tak, jakby powiedzieć:

„Hej, panie ObjectManager! Za każdym razem, gdy Magento\Backend\Model\Urlżądana jest instancja typu obiektu, najpierw spójrz na definicję konstruktora klasy i przeanalizuj zdefiniowane w niej zależności . Chcę, abyś przejrzał wewnątrz końcowego, scalonego di.xmlbieżącego żądania HTTP konfiguracji dla każdego skonfigurowanego zależność , która jest zdefiniowana w klasie konstruktora Magento \ Backend \ Modele \ Url . Dajesz mi , że skonfigurowany realizację zależności.”

adjco
źródło