2016-01-19 11 views
10

Uwagi: Począwszy od wersji 2.8, Symfony warunkiem autowire: true do konfiguracji usług, a od wersji 3.3, Symfony warunkiem alias (zamiast autowire_types) do aliasu konkretny obiekt do interfejs automatycznego wtrysku zależności do "kontrolerów jako usług". Jest też pakiet, który pozwala na autowirowanie dla metod "działania" kontrolera, chociaż odsunąłem się od tego i skupiłem się bardziej na odmianie wzorca ADR (który jest zasadniczo pojedynczą klasą "działania" z metodą interfejsu i nie wrzucanie ładunków metod działania w ramach jednej klasy, która ostatecznie doprowadzi do architektonicznego koszmaru). To jest faktycznie to, czego szukałem przez te wszystkie lata i teraz już nie potrzebuję "wciągać" przyzwoitego wtryskiwacza zależnego od rekursywności (auryn), ponieważ obecnie struktura obsługuje to, co powinno mieć cztery lata wcześniej. Zostawię tę odpowiedź tutaj na wypadek, gdyby ktoś chciał śledzić kroki, które zrobiłem, aby zobaczyć, jak działa jądro i niektóre opcje dostępne na tym poziomie.Symfony - zmiana jak kontrolery są wystąpienia i wykonywane


Uwaga: Chociaż to pytanie przede wszystkim skierowany Symfony 3, powinno być również istotne dla użytkowników Symfony 2, jak logika jądro wydaje się nie zmieniły się znacznie.

Chcę zmienić sposób tworzenia kontrolerów w Symfony. Logika ich powstawania znajduje się obecnie w HttpKernel::handle, a dokładniej, HttpKernel::handleRaw. Chcę zastąpić call_user_func_array($controller, $arguments) z własnym wtryskiwaczem wykonującym tę konkretną linię.

opcje próbowałem dotąd:

  • Rozszerzanie HttpKernel::handle z własną metodą, a następnie o to nazywane przez symfony
http_kernel: 
    class: AppBundle\HttpKernel 
    arguments: ['@event_dispatcher', '@controller_resolver', '@request_stack'] 

Minusem jest to, że z powodu handleRaw jest Prywatne, nie mogę go rozszerzyć bez hackowatej refleksji, więc musiałbym skopiować i wkleić tonę kodu.

  • Tworzenie i rejestracji nowego kontrolera resolverowi
controller_resolver: 
    class: AppBundle\ControllerResolver 
    arguments: [] 

To była zasadnicza nieporozumienie miałem więc pomyślałem, że to udokumentować tutaj. Zadaniem przelicznika jest rozstrzygnięcie: , gdzie, aby znaleźć kontroler jako wywoływalny. To jeszcze nie zostało jeszcze wywołane. Jestem bardzo zadowolony z tego, jak Symfony korzysta z tras od routes.yml i wykrywa klasę i metodę, aby wywołać kontroler jako wywoływalny.

  • Dodanie detektora zdarzeń na kernel.request
kernel.request: 
    class: MyCustomRequestListener 
    tags: 
     - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 33 /** Important, we'll get to why in a minute **/ } 

Przyjrzeniu na Http Kernel Component Documentation, widzimy, że ma następujący typowy cel:

dodać więcej informacji Żądanie, zainicjuj części systemu lub zwróć odpowiedź, jeśli to możliwe (np. warstwa bezpieczeństwa, która odmawia dostępu).

Pomyślałem, że poprzez utworzenie nowego słuchacza, używając mojego własnego wtryskiwacza do tworzenia mojego kontrolera, a następnie zwraca odpowiedź w tym słuchacza, by ominąć resztę kodu, który tworzy instancję kontrolera. To jest to, co chcę! Ale jest w tym poważna wada:

Profil Symfony nie pojawia się ani nic z tego, to tylko moja odpowiedź i tyle. Nie żyje. odkryłem, że mogę przełączyć priorytet od 31 do 33 i to przełączanie między moim kodu i Symfonys i wierzę, że to dlatego, że od słuchacza routera priority. Czuję, że schodzę tu na złą drogę.

Nie, to pozwala mi zmienić wpłacone która zostanie wywołana przez call_user_func_array(), a nie jak regulator jest faktycznie instancja, która jest mój cel.

mam udokumentować moje pomysły, ale jestem na zewnątrz. Jak mogę osiągnąć następujące?

  • zmienić sposób kontrolery są wystąpienia, a następnie stracony, a konkretnie call_user_func_array() który jest w krwawej metody prywatnej (dzięki Symfony)
  • Spadek z powrotem do domyślnego kontrolera instancji jeśli kopalnia nie działa
  • Pozostawić wszystko jeszcze pracować zgodnie z oczekiwaniami, jak załadunku profiler
  • umieć ten pakiet z rozszerzeniem dla innych użytkowników

Dlaczego chcę to zrobić?

Kontrolery mogą mieć wiele różnych metod dla różnych okoliczności i każda metoda powinna być w stanie wpisać podpowiedź dla tego, czego wymaga indywidualnie, a nie mieć konstruktora, który bierze wszystkie rzeczy, z których niektóre mogą nawet nie być używane, w zależności od metody kontrolera wykonany. Kontrolery nie są zgodne z zasadą pojedynczej odpowiedzialności i są "przypadkiem skrajności obiektu". Ale oni są tym, kim są.

Chcę zastąpić sposób tworzenia kontrolerów za pomocą mojego własnego rekursywnie wstawianego wtryskiwacza, a także sposobu ich wykonywania, ponownie za pomocą introspekcji rekurencyjnej za pośrednictwem mojego wtryskiwacza, ponieważ domyślny pakiet Symfony nie ma tej funkcji. Nawet z najnowszą opcją usługi "autowire" w Symfony 2.8+.

+0

Nie wyobrażam sobie wielkiego stołu, ale refleksja może być kluczem do zastąpienia tej prywatnej metody. http: //blag.kazeno.net/development/access-private-protected-properties ten artykuł może ci pomóc. – FZE

+0

@ FeyyazEsatoğlu Mój pierwszy punkt wypunktowania wyjaśnia, w jaki sposób nie chcę korzystać z refleksji (próbowałem już tego i nie lubiłem). – Jimbo

+0

oops, przepraszam, ominąłem to. – FZE

Odpowiedz

0

Dlaczego nie zwrócisz własnego wywołania niestandardowego ControllerResolverInterface, który utworzy instancję Controller w sposób, który chcesz i wywołaj?

Byłby to w zasadzie dekorator.

Można przedłużyć Symfony\Component\HttpKernel\Controller\ControllerResolver za pomocą własnej implementacji metody instantiateController() lub można wdrożyć ControllerResolverInterface od podstaw.

UPD:

Kiedy Symfony sprawia call_user_func_array($controller, $arguments); połączenia w handleRaw() zmienna $controller to, co już wrócił ze swojego zwyczaju ControllerResolver. Oznacza to, że możesz zwrócić wszelkie wywołania z twojego resolwera (może to być [$this, "callController"] f.e.) i wewnątrz tego wywoływacza utworzysz nową Controller z Auryn i wywołasz ją.

UPD2:

Jeśli nadal zmaga się z tym, dodam przykład, ponieważ można pominąć tego, co mam na myśli tutaj.

use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver; 

class AutowiringControllerResolver extends ControllerResolver 
{ 
    // ... constructor stuff; assume $injector is a part of this class 

    protected function createController($controller) 
    { 
     $controller = parent::createController($controller); 

     return function (...$arguments) use ($controller) { 
      // you can do with resolved $arguments whatever you want 
      // or you can override getArguments() method and return 
      // empty array to discard getArguments() functionality completely 

      return $this->injector->execute($controller); 
     }; 
    } 

    protected function instantiateController($classname) 
    { 
     return $this->injector->make($classname); 
    } 
} 
+0

Dekorator taki jak w: 'funkcja function() use (kontroler $) {return $ controller; } '? To możliwe do wywołania. Problem polega na tym, że muszę mieć kontroler 'execute()' d (jeśli znasz interfejs API Auryn), więc nadal muszę przekazać mu tę metodę, aby również zostać automatycznie rozwiązany. – Jimbo

+0

Na przykład z 'ControllerResolverInterface # getController' zwrócisz' function() use ($ class, $ method, $ injector) {return $ injector-> execute ([$ class, $ method]); } 'Czy to nie zadziałałoby dla twojej sprawy? – nikita2206

+0

Tak więc 'getController' musi zwrócić wartość wywołania tylko dla samego kontrolera, co oznacza, że ​​Auryn obsługuje konstruktor za pomocą' make() '. Muszę również obsłużyć metodę kontrolera **, która jest wywoływana ** przez 'execute', co jest wywołaniem' call_user_func() '. – Jimbo

0

Rozdzielacz kontrolera faktycznie robi dwie rzeczy. Pierwszym jest uzyskanie kontrolera. Drugim jest uzyskanie listy argumentów dla danej akcji.

$arguments = $this->resolver->getArguments($request, $controller); 
$response = call_user_func_array($controller, $arguments); 

Jest to metoda getArguments, którą można przesłonić, aby zaimplementować specjalną funkcję "wtrysku metodą działania". Trzeba tylko określić, jakie argumenty wymaga metoda działania i zwrócić ich tablicę.

Na podstawie innego pytania uważam, że możesz nie rozumieć funkcji autowire. Autowire rzeczywiście dotyczy tylko wtrysku konstruktora. To nie pomoże przy iniekcji metodą działania.

Jeśli getArguments nie rozwiąże problemu, to przesłonięcie metody obsługi jest naprawdę jedyną opcją. Tak, istnieje sporo kodu do skopiowania/wklejenia z handleRaw, ale to dlatego, że jest tam sporo do zrobienia. I nawet jeśli handleRaw byłby chroniony, nadal musiałbyś skopiować/wkleić kod tylko po to, aby dostać się do jednej linii, którą chcesz zastąpić.

+0

Automatyczne nadawanie może się zdarzyć przy wywołaniach metod. W przypadku metody typowania podpowiedzi dla konkretnego obiektu proces uzyskiwania nazwy tego obiektu przez introspekcję i tworzenie instancji jest tym samym procesem stosowanym do konstruktora. Obecnie patrzę na handleRaw override though. Nie miłe, ale możliwe. – Jimbo

+0

Jeśli tak powiesz. Kiedy wymyślisz, jak zdobyć kontener Symfony, aby wypromować wywołanie metody, prosimy o przesłanie przykładu. – Cerad

+0

To część, którą próbuję rozgryźć. Istnieje skrzyżowanie między metodą obsługi jądra, która tworzy instancję kontrolera i program tłumaczący, który wylicza argumenty. Będzie jakiś sposób :) – Jimbo

0

Słuchacz jest za późno, aby rozwiązać twoje potrzeby, ponieważ nie obejmuje kontenera Dependency Injection, który jest niezbędny do utworzenia prawidłowego obiektu (~ = usługa).

Prawdopodobnie szukasz funkcji automatycznego sterowania kontrolerem.

Jeśli tak, można znaleźć rozwiązanie lub przynajmniej inspiracji w tym pakiecie: http://www.tomasvotruba.cz/blog/2016/03/10/autowired-controllers-as-services-for-lazy-people/

Spełnia swoje potrzeby w tych punktach:

  • autowire wtryskiwacz
  • awaryjna do domyślnych (FrameworkBundle'S) Kontroler resolver, jeśli nie zostanie znaleziony,
  • powinien również utrzymywać cały przepływ działa, ponieważ podczas procesu rozdzielania kontrolera nie ma hacków.
Powiązane problemy