Notatki Deva Notatki Deva Trochę kodu, trochę gadania

Magento 2, DI i env vars

Jakiś czas temu walczyłem z integracją zewnętrznej biblioteki w Magento 2. Konieczne było podanie danych API w konstruktorze, więc stwierdziłem, że zmienne środowiskowe (env vars) idealnie się nadadzą. DI Magento na to pozwala. Przynajmniej w teorii… No dobra, nie w teorii, bo już korzystałem z tej metody.

Problem z integracją zewnętrznej biblioteki i zmiennych środowiskowych

Dla uproszczenia uznajmy, że potrzebuję tylko 1 parametru w konstruktorze (string $token):

<?php

namespace Vendor\Library;

class FooService
{
    public const ENV_VAR_TOKEN = 'FOO_API_TOKEN';

    public function __construct(string $token)
    {
        // ...
    }
}

I do tego konfiguracja di.xml

<type name="App\Module\FooService">
    <arguments>
        <argument name="token" xsi:type="init_parameter">App\Module\FooService::ENV_VAR_TOKEN</argument>
    </arguments>
</type>

<preference for="Vendor\Library\FooService" type="App\Module\FooService"/>

A na koniec .env

FOO_API_TOKEN=secret

Ale jak to działa? No tak: jeżeli typ argumentu przekazanego do serwisu to init_parameter, wtedy jego wartość musi być stałą. Następnie wartość tej stałej używana jest jako klucz, którego Magento szuka w zmiennej globalnej $_SERVER. Proste prawda? Tyle że nie działa.

Dlaczego?

Odpowiedź okazała się tak prosta, że aż głupio się przyznać, że zleciało ponad pół dnia na jej znalezienie.

Magento nie umie w init_parameter, jeżeli klasa nie jest częścią modułu Magento, a zamiast tego, żyje w /vendor. Wyjątkiem jest tryb developerski MAGE_MODE=developer. W takim przypadku wszystko zadziała 😂

Jest jeszcze jeden wyjątek. W trybie produkcyjnym ponownie uruchomiłem bin/magento setup:upgrade && bin/magento setup:di:compile. I działa.

WTF i Magento. Nierozłączni przyjaciele
WTF i Magento. Nierozłączni przyjaciele

Integracja przez moduł Magento

Czas na znalezienie rozwiązania. Bo ustawieniu dziesiątek breakpointów w vendorach Magento okazało się, że chwili pierwszej kompilacji DI Magento, system nic nie wie o tym, że ma pobrać dane ze zmiennej środowiskowej. Najprostsze rozwiązanie, na jakie udało mi się wpaść (biorąc pod uwagę presję czasu, nie szukałem kolejnych :)) to rozszerzenie klasy biblioteki w module, który ją integruje. W rezultacie mam taki kod:

<?php

namespace App\Module;

class FooService extends \Vendor\Library\FooService
{
    public const ENV_VAR_TOKEN = 'FOO_API_TOKEN';
}

Oczywiście di.xml wymagało poprawy:


<type name="App\Module\FooService">
    <arguments>
        <argument name="token" xsi:type="init_parameter">App\Module\FooService::ENV_VAR_TOKEN</argument>
    </arguments>
</type>

<preference for="Vendor\Library\FooService" type="App\Module\FooService"/>

Nawet nie próbowałem dodać konstruktora w klasie modułu. Wystarczyło rozszerzyć klasę biblioteki i nagle Magento potrafi wstrzyknąć odpowiednie dane w odpowiednie miejsca.

Podsumowanie

Naprawdę nie wiem jak to podsumować. Wydaje mi się, że skoro Magento oferuje w taki czy inny sposób korzystanie ze zmiennych środowiskowych, to powinny one działać. Z drugiej strony, to Magento. Nie spodziewajmy się cudów.

Lekcja na przyszłość: jeżeli chcesz coś zrobić pomijając dziwne konfiguracje Magento – nie rób tego. Jakkolwiek niezrozumiały, dziwny i ułomny kod miałby z tego powstać, lepiej tak, niż później walczyć kilka godzin z wyżej opisanym problemem.


comments powered by Disqus