Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [Symfony]Construct w kontrolerze
Forum PHP.pl > Forum > PHP > Frameworki
Szymciosek
Witam,
próbowałem zrobić taki myk, że po uruchomieniu jakiegokolwiek routingu uruchamiany jest zawsze konstruktor klasy, a on wywołuje mi metodę setExistingColumns(), w której mam:

  1. $entityManager = $this->getDoctrine()->getEntityManager();
  2. $this->existingColumns = $entityManager->getClassMetadata('BundleName:Users')->getFieldNames();


Lecz niestety otrzymuję błąd:
Kod
Fatal error: Call to a member function has() on a non-object in F:\WORK\author\bundleName\vendor\symfony\symfony\src\Symfony\Bundle\FrameworkBundle\Controller\Controller.php on line 191


Ta metoda tyczy się linii 191:
  1. public function getDoctrine()
  2. {
  3. if (!$this->container->has('doctrine')) {
  4. throw new \LogicException('The DoctrineBundle is not registered in your application.');
  5. }
  6.  
  7. return $this->container->get('doctrine');
  8. }


Moim zdaniem takie coś jest nierealne, ponieważ chcę to uruchomić poza metodą, która ma przypisany routing, ale mogę się mylić, stwierdzam to po tym, że w każdej innej metodzie typu indexAction taki myk działa.

Czy jest możliwość zrobienia tego żebym nie musiał 100x powtarzać kodu w każdej metodzie, która ma przypisany do siebie routing ?
Crozin
1. Nie do końca rozumiem czemu ma to służyć, ale jeżeli chcesz podpiąć jakiś kod pod każde żądanie najprawdopodobniej powinieneś skorzystać nasłuchiwać zdarzenia KernelEvents::REQUEST, bądź KernelEvents::CONTROLLER.
2. Dostajesz błąd, ponieważ w konstruktorze nie masz jeszcze dostępu do właściwości Controller::$container. A konkretnie, to nie jest do niej przypisany jeszcze DIC.
misi3kk
Jeśli ta metoda potrzebna jest w tylko jednym kontrolerze to proponuję wydzielić kod z konstruktora do innej metody i wywoływać ją kiedy jest potrzebna.

Jeśli planujesz używać tej metody w kilku kontrolerach to poza podpinaniem Eventów można jeszcze dodać sobie Service który będzie dostarczał takie dane.
Szymciosek
Możesz napisać coś więcej o service ? Jak z tego korzystać itd ?
Taka metoda będzie wykorzystywana praktycznie za każdym razem w moim przypadku ze względu na to, że nie chcę powtarzać zbędnego kodu w każdej uruchamianej metodzie i myślałem nad wykorzystaniem do tego właśnie konstruktora, który będzie uruchamiany za każdym razem.
misi3kk
http://symfony.com/doc/current/book/service_container.html

Idąc tą drogą, możesz nawet Twój kontroler zdefiniować jako service - wtedy możesz wymóc wywołanie odpowiednich metod z odpowiednimi parametrami:
http://symfony.com/doc/current/cookbook/co...er/service.html
Crozin
@misi3kk: Utworzenie samego serwisu/usługi nie powoduje kompletnie niczego - i tak trzeba to podpiąć pod jakieś zdarzenie.
misi3kk
Może swobodnie utworzyć controller jako service i w definicji podać wywołanie metody, która wczyta interesujące go dane.

Zdarzenie jest OK, ale nie wydaje mi się, aby potrzebne było wywoływanie zdarzenia dla każdego wywołania strony, w przypadku gdy te dane potrzebne są tylko dla jednego kontrolera (lub kilku, ale w mniejszości).

Service domyślnie wywołuje konstruktor, a entity manager można mu swobodnie przekazać. Dodatkowo można wywołać dowolną metodę w czasie jego tworzenia.
Crozin
Cytat
[...] w przypadku gdy te dane potrzebne są tylko dla jednego kontrolera (lub kilku, ale w mniejszości).
Już dwa razy napisano, że ma to być dla niemal każdego kontrolera odpalane.

Samo zdefiniowanie usługi nie powoduje jej uruchomienia. Musi być ona jawnie pobrana z DIC-a, użyta jako zależność czy też przekazana jako innego rodzaju argument obsługiwany przez DIC-a (np. jako listener dla zdarzenia).
misi3kk
Napisane zostało, że metoda ta uruchamiana będzie prawie za każdym razem, ale nie napisanego czy dla każdego kontrolera. Kod metody pobiera dane powiązane z użytkownikami, dlatego założyłem, że będzie to używane tylko w części serwisu.

Opisywałem moją propozycję rozwiązania, nie dlatego, aby powiedzieć, że Twój pomysł jest zły - jest dobry i w wybranych zastosowaniach znacznie lepszy od mojego. Po prostu chciałem pokazać alternatywne wyjście, na wypadek gdyby całość była używana np. w jednym kontrolerze.
Szymciosek
No akurat panowie powiem wam, że będzie to używane w każdym przypadku, bo za każdym razem, gdy coś będzie uruchomione muszę pobrać listę dostępnych kolumn z entity = bazy danych. Więc w każdym kontrolerze i każdej metodzie, która będzie publiczna - mam na myśli uruchamia z poziomu routingu.

Zacząłem coś czytać np tutaj: http://symfony.com/doc/2.0/book/internals....ontroller-event czy http://symfony.com/doc/current/cookbook/se...t_listener.html

ale pytanie skąd symfony wie, że EventListener ma być odpalany przed kontrolerem i ma być wykonana akcja ? Gdzieś to deklaruję ?
Crozin
Przeczytaj sobie dokumentację EventDispatchera, który jest jednym z kluczowych mechanizmów całego FW. Wszelkie wątpliwości powinny się rozwiać.
Szymciosek
Ciężko mi coś wchodzą te eventy itd, ale znalazłem: https://groups.google.com/d/msg/symfony2/qP...LU/sw3ueQh0n_oJ

Możesz mi wytłumaczyć ? To jest jakiś container, ale do czego ?

Tutaj wynalazłem Twoje posty jakieś chyba na ten temat http://forum.php.pl/index.php?showtopic=192743 ? To jest to samo ?
Crozin
Tworzysz sobie usługę, tj.:
1. Klasę która w konstruktorze przyjmuje wszystkie wymagane zależności (tutaj: DoctrineRegistry, czyli to co masz dostępne pod kluczem "doctrine" w DIC-u) dla jej poprawnego działania.
2. Odpowiedni wpis w definicji DIC-a. Chodzi o element <service /> w XML-owskiej konf. DIC-a.

Następnie taką klasę-usługę musisz zarejestrować jako listener dla danego zdarzenia. Możesz zrobić to bezpośrednio w konf. DIC-a: http://symfony.com/doc/current/cookbook/se...t_listener.html
Szymciosek
Opisałeś mi tutaj ten container czy to coś na zasadzie services ? Czym jest DIC ?
Crozin
DIC: Dependency Injection Container: http://symfony.com/doc/current/book/service_container.html
Szymciosek
Zaczynam ogólnie pojmować DI, ale pytanie co do tego:
  1. <?php
  2.  
  3. namespace ApiRest\ServiceBundle;
  4.  
  5. use Symfony\Component\HttpKernel\Bundle\Bundle;
  6.  
  7. class ApiRestServiceBundle extends Bundle
  8. {
  9. private static $containerInstance = null;
  10.  
  11. public function setContainer(\Symfony\Component\DependencyInjection\ContainerInterface $container = null)
  12. {
  13. parent::setContainer($container);
  14. self::$containerInstance = $container;
  15. }
  16. public static function getContainer()
  17. {
  18. return self::$containerInstance;
  19. }
  20. }


a w controller / construct:
  1. $em = \ApiRest\ServiceBundle\ApiRestServiceBundle::getContainer()->get('doctrine')->getEntityManager();
  2. var_dump($em);


Czemu działa tylko w ten sposób ?
Gdy zrobię:
  1. ...->getDoctrine()->getEntityManager();


to dostaję błąd:
Kod
Call to undefined method appDevDebugProjectContainer::getDoctrine() in F:\WORK\author\service_rest_api\src\ApiRest\ServiceBundle\Controller\UserController.php on line 17
misi3kk
To co wkleiłeś zupełnie nie ma związku z tym o czym było pisane wcześniej. W Twoim przypadku konieczne jest dodanie listenera - linki do tego jak to zrobić są już podane, podobnie jak podane były przez Crozin eventy które powinieneś nasłuchiwać.

Mam wrażenie, że brakuje Ci wielu informacji dotyczących Sf2. Naprawdę warto odłożyć projekty i przeczytać to co jest w Book + chociaż część tego co jest w Cookbook. Bez znajomości podstaw ciężko pisać sensowne projekty. Niestety, ale Sf2 nie jest łatwy do zrozumienia i trochę czasu należy na naukę poświęcić.

Przykład jak dodać listener: http://symfony.com/doc/current/cookbook/se...t_listener.html
Tylko Event Ci się zmienia. Nie rób tego w klasie bundla, bo to nie jest miejsce na to.
Crozin
1. Nie używaj static o ile nie wiesz co dokładnie robisz. Tutaj używasz go źle, wręcz fatalnie, a co za tym idzie tracisz zalety OOP i IoC (zrealizowanego przez DI). Co więcej nie ma tutaj w ogóle potrzeby do jego stosowania.
2. Klasa appDevDebugProjectContainer() najzwyczajniej w świecie nie definiuje metody getDoctrine() więc masz błąd. To czego szukasz, to getDoctrineService().
3. Dlaczego ciągle brniesz w uruchomienie tego kodu w konstruktorze klasy konstruktora (po którym dziedziczą wszystkie inne), zamiast skorzystać z poprawnego rozwiązania, wspomnianego przeze mnie już kilkukrotnie, tj. listenera dla zdarzenia KernelEvents::REQUEST?
Szymciosek
Service:
  1. services:
  2. kernel.listener.check:
  3. class: ApiRest\ServiceBundle\Listener\CheckListener
  4. arguments: [@doctrine]
  5. tags:
  6. - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }


Listener:
  1. <?php
  2.  
  3. namespace ApiRest\ServiceBundle\Listener;
  4.  
  5. use Doctrine\Bundle\DoctrineBundle\Registry as Doctrine;
  6.  
  7. class CheckListener
  8. {
  9. private $entityManager;
  10.  
  11. public function __construct(Doctrine $doctrine)
  12. {
  13. $this->entityManager = $doctrine->getEntityManager();
  14. }
  15.  
  16. public function onKernelRequest()
  17. {
  18.  
  19. }
  20. }


Czy to jest dobrze ?
Jeśli tak, to jak odebrać te dane z kontrolera ?
misi3kk
O ile się nie mylę, musisz je zapisać w obiekcie Request
Szymciosek
Możesz powiedzieć coś więcej ? Mógłbyś też podać jakieś przykłady z użyciem tego, tutoriale ? Dokumentacja np ma jakiś przykład z service, zrobiłem dokładnie tak samo i co ? Nie działa...
Już mi trochę głupio pisać i zawracać wam tyłki na forum, ale szukanie też nie pomaga, szukam i szukam, odkąd przyszedłem ze szkoły cały czas tylko krążę w okół tego tematu i nic... dopiero niedawno trafiłem na to co podałem post wyżej.
Crozin
Coś słabo szukałeś, bo w dokumentacji (The Book/Cookbook) są odpowiednie przykłady.
Cytat
[...] Dokumentacja np ma jakiś przykład z service, zrobiłem dokładnie tak samo i co ? Nie działa...
No to zrobiłeś coś źle w takim razie. Nie uważasz, że jeżeli w dokumentacji tak dużego projektu były jakieś trywialne błędy już dawno by je wykryto i poprawiono?

By odczytać dane utworzone w metodzie CheckListener::onKernelRequest() mógłbyś je zapisać jako właściwość obiektu (np.: CheckListener::abc), następnie w kontrolerze pobrać referencję do obiektu "kernel.listener.check" z DIC-a i odczytać z owej właściwości. Mógłbyś również rozwiązać to innym sposobem - nieco schludniejszym, tj. przekazać do swojego listenera obiekt Request, i zapisać wyniki pracy jako jego atrybut (Request::$attributes).
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.