Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [SF][SF2]DI w konrolerze
Forum PHP.pl > Forum > PHP > Frameworki
Matrix12
Witam,


mam pytanie. Posiadam kilka kontrolerów, są to czyste CRUD'y i stworzyłem dodatkowy kontroler który będzie nimi wszystkimi zarządzał.
Próbuje wrzucić w konstruktorze np.


  1. public function __construct(NewsController $news)
  2. {
  3. $this->news = $news;
  4.  
  5. }
  6.  


Niestety wyświetla mi cały czas, że $news powinna być instancją klasy NewsController( wiem że tak powinno być ) ale dlaczego automatycznie nie jest to ładowane? Czy jakiś serwis muszę do tego napisać? Jak wygląda przykładowa implementacja takiego rozwiązania ?
kpt_lucek
Bo musisz zdefiniować DI poprzez konfigurację bundle'a.

  1. services:
  2. base_controller:
  3. class: My\Bundle\Controller\BaseController
  4. arguments: [ @xyz, @abc ]
  5. controler_1:
  6. class: My\Bundle\Controller\Controller1
  7. arguments: [ @base_controller ]
  8. #itd
  9. #gdzie @xyz jest definicją serwisu (usługi) i tylko w ten sposób możesz się do niego odwoływać aby DI działało poprawnie
Matrix12
Czyli rozumiem, że jest to np. tak:
  1. services:
  2. news_controller:
  3. class: My\Bundle\Controller\NewsController
  4. admin_controler:
  5. class: My\Bundle\Controller\AdminController
  6. arguments: [ @base_controller ]


gdzie potem odwołuje się do tego tak :


  1.  
  2. <?php
  3.  
  4.  
  5. class AdminController extends Controller
  6. {
  7.  
  8. public function__construct(NewsController $news)
  9. {
  10. $this->news = $news;
  11. }
  12.  
kpt_lucek
Zacznijmy może od tego co chcesz zrobić, bo w praktyce nie wiem jak Ci pomóc/wytłumaczyć/naprowadzić na to co mógłbyś zrobić.
Matrix12
Mam kilka crudów którymi chciałbym zarządzać z AdminController... dlatego chce nauczyć wstrzykiwać zależności bo wtedy będę mógł się odwołać do każdego z tych crudów.
kpt_lucek
Zacznijmy od tego że Twój controller rozszerza BaseController.

Miej na uwadze iż BaseController zawiera swój constructor, a w nim są wstrzykiwane zależności chociażby do container'a.

Jeżeli chcesz, aby Twój AdminController zbierał wszystkie pozostałe, możesz do tego użyć serwisów tagowanych + compiler pass, dzięki temu nie musisz wstrzykiwać każdego z osobna smile.gif

Zainteresuj się Tym, tym i tym.
Matrix12
Próbowałem zdefiniować kontroler jako serwis ale to nie działa;/
kpt_lucek
Stwierdzenie "nie działa" mówi tyle co nic. Napisz konkrety proszę.

Co zrobiłeś, w jaki sposób, jak wygląda Twój controller (1.,2.,3.,n.), jaki błąd dostajesz i jak się do tego odwołujesz
Matrix12
Dostaje taki bład:


  1.  
  2. There is no extension able to load the configuration for "news_controller" (in \src\Acme\AdminBundle\DependencyInjection/../Resources/config\services.yml). Looked for namespace "news_controller", found none




services.yml wygląda tak :

  1. news_controller:
  2. class: Acme\NewsBundle\Controller\NewsController
  3.  
  4. admin_controller:
  5. class: Acme\AdminBundle\Controller\AdminController
  6. arguments : [@news_controller]




kontroler wygląda tak:
  1.  
  2. <?php
  3.  
  4. namespace Acme\AdminBundle\Controller;
  5.  
  6.  
  7. use Symfony\Component\HttpFoundation\Request;
  8. use Symfony\Bundle\FrameworkBundle\Controller\Controller;
  9. use Symfony\Component\HttpFoundation\Response;
  10.  
  11. use Acme\NewsBundle\Controller\NewsController;
  12. /**
  13. *
  14. */
  15. class AdminController extends Controller
  16. {
  17. public $news;
  18. function __construct(NewsController $news)
  19. {
  20. $this->news = $news;
  21. }
  22.  
  23.  
  24. }
kpt_lucek
  1. services:
  2. news_controller:
  3. class: Acme\NewsBundle\Controller\NewsController
  4.  
  5. admin_controller:
  6. class: Acme\ExampleBundle\Controller\AdminController
  7. arguments : [@news_controller]

To po pierwsze,

  1. <?php
  2.  
  3. namespace Acme\AdminBundle\Controller;
  4.  
  5.  
  6. use Symfony\Component\HttpFoundation\Request;
  7. use Symfony\Bundle\FrameworkBundle\Controller\Controller;
  8. use Symfony\Component\HttpFoundation\Response;
  9.  
  10. use Acme\NewsBundle\Controller\NewsController;
  11. /**
  12. *
  13. */
  14. class AdminController extends Controller
  15. {
  16. public $news;
  17. function __construct(NewsController $news)
  18. {
  19. // pamiętaj co pisałem o constructorze w controllerach extendujących BaseController
  20. $this->news = $news;
  21. }
  22.  
  23.  
  24. }


Pamiętaj o jeszcze jednej rzeczy,

Rejestrując controller jako usługę (serwis), nie powinieneś (o ile pamiętam, to nie możesz) extendować Symfony\Bundle\FrameworkBundle\Controller\Controller, więc Wszystkie zależności dołączasz poprzez DI (wystrzegaj się wstrzykiwania całego container'a).
Forti
Tworzenie DI dla controller to imo zla praktyka. Zrób sobie coś takiego:

  1. services:
  2. acme.controller_service:
  3. class: Acme\ExampleBundle\Service\ControllerService
  4. arguments: ['@service_container']


  1. <?php
  2.  
  3. namespace Acme\ExampleBundle\Service;
  4.  
  5. use Symfony\Component\DependencyInjection\ContainerInterface as Container;
  6.  
  7. class ControllerService
  8. {
  9. protected $container;
  10.  
  11. public function __construct(Container $container)
  12. {
  13. $this->container = $container;
  14. }
  15. }


i teraz normalnie w controller robisz np.
  1. $this->get('service_jakis_sobie')


. Tutaj robisz
  1. $this->container->get('service_jakis_sobie')


Trzymasz cała logike w takich "serwisach" wink.gif
Np. do doctrine robisz $this->container->get('doctrine')->getManager. W controllerze w którym "Zarządzasz" robisz:

  1. $this->get('acme.controller_service')



Nie jest to do końca prawidłowe. Powinno unikać się wstrzykiwania całego kontenera usług, a jedynie to co potrzebujemy.


Troche chaotycznie napisałem ponieważ jestem na uczelni. Jak coś to pytaj.
kpt_lucek
Moim zdaniem, tworzenie controllerów jako usług ma swoje plusy i minusy. Do minusów możesz zaliczyć brak dostępu do każdego zarejestrowanego serwisu (usługi), bo nie ładujesz tam container'a. Plusem jest jasne sprecyzowanie zależności controllera, szybka weryfikacja poziomu złożoności logiki tam będącej, łatwiejsze testowanie jak i budowanie go pod konkretną funkcjonalność, dalej idąc tym tropem - przyspieszasz działanie, bo nie ładujesz mega wielkiego container'a do zależności, przez co sam obiekt jest lżejszy.
Matrix12
Jednak zdecydowałem tak jak radziłeś :
  1. <?php
  2.  
  3. namespace Acme\AdminBundle\Controller;
  4.  
  5.  
  6. use Symfony\Component\HttpFoundation\Request;
  7. use Symfony\Bundle\FrameworkBundle\Controller\Controller;
  8. use Symfony\Component\HttpFoundation\Response;
  9. use Symfony\Component\DependencyInjection\ContainerInterface as Container;
  10.  
  11.  
  12. /**
  13. *
  14. */
  15. class AdminController extends Controller
  16. {
  17. public $container;
  18.  
  19. public function __construct(Container $container)
  20. {
  21. $this->container = $container;
  22. }
  23.  
  24. }




a service.yml wygląda tak :


  1. services:
  2.  
  3.  
  4. acme.admin_controller:
  5. class: Acme\AdminBundle\Controller\AdminController
  6. arguments: ['@admin_controller']



natomiast wyświetla mi taki błąd :

  1. ServiceNotFoundException in CheckExceptionOnInvalidReferenceBehaviorPass.php line 58: The service "acme.admin_controller" has a dependency on a non-existent service "admin_controller".
kpt_lucek
Cytat(Matrix12 @ 11.04.2015, 19:29:59 ) *
Jednak zdecydowałem tak jak radziłeś :
  1. <?php
  2.  
  3. namespace Acme\AdminBundle\Controller;
  4.  
  5.  
  6. use Symfony\Component\HttpFoundation\Request;
  7. use Symfony\Bundle\FrameworkBundle\Controller\Controller;
  8. use Symfony\Component\HttpFoundation\Response;
  9. use Symfony\Component\DependencyInjection\ContainerInterface as Container;
  10.  
  11.  
  12. /**
  13. *
  14. */
  15. class AdminController extends Controller
  16. {
  17. public $container;
  18.  
  19. public function __construct(Container $container)
  20. {
  21. $this->container = $container;
  22. }
  23.  
  24. }


1. Robisz to błędnie, bo "class AdminController extends Controller" już Ci daje dostęp do container'a ($this->container->get(), lub aliasem $this->get()), tak więc tą kwestię możesz pominąć.
2. W jakim celu "AdminController" musi zawierać pozostałę CRUD'y (controllery)? Nie łatwiej utworzyć usługi (nie controllery, dedykowane serwisy zajmujące się konkretną funkcjonalnością), zarejestrowanie ich i używanie wedle potrzeb w innych usługach / controllerach?
Matrix12
Poprzez AdminController chce odwoływać się do metod pozostałych crudów. Nie wiem czy rozumiem dobrze Twoją propozycję ale chyba masz to na myśli co ja chce osiągnąć. Jeżeli się mylę pokaż mi przykładową implementacje
kpt_lucek
  1. <?php
  2.  
  3. namespace Acme\DemoBundle\Manager\NewsService;
  4.  
  5. use Doctrine\ORM\EntityManager;
  6.  
  7. class NewsService
  8. {
  9. /**
  10.   * @var EntityManager
  11.   */
  12. protected $em;
  13.  
  14. /**
  15.   * @param EntityManager $entityManager
  16.   */
  17. public function __construct(EntityManager $entityManager)
  18. {
  19. $this->em = $entityManager;
  20. }
  21.  
  22. /**
  23.   * @param NewsInterface $news
  24.   * @return $this
  25.   */
  26. public function acceptNews(NewsInterface $news)
  27. {
  28. $news->setAccepted(true);
  29.  
  30. return $this;
  31. }
  32.  
  33. /**
  34.   * @param NewsInterface $news
  35.   * @return $this
  36.   */
  37. public function removeNews(NewsInterface $news)
  38. {
  39. $this->em->remove($news);
  40. $this->em->flush($news);
  41.  
  42. return $this;
  43. }
  44. }

  1. #services.yml
  2. services:
  3. news_service:
  4. class: Acme\DemoBundle\Manager\NewsService
  5. arguments: [ @doctrine.orm.entity_manager ]

  1. <?php
  2.  
  3. namespace Acme\DemoBundle\Controller;
  4.  
  5. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  6. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
  7. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  8. use Symfony\Bundle\FrameworkBundle\Controller\Controller;
  9. use Symfony\Component\HttpFoundation\Request;
  10. use Acme\DemoBundle\Entity\News;
  11.  
  12. class AdminController extends Controller
  13. {
  14. /**
  15.   * @Route("/news-accept/{news}", name="accept_news", requirements={"news": "\d+"})
  16.   * @ParamConverter("news", class="AcmeDemoBundle:News")
  17.   * @Template()
  18.   * @param News $news
  19.   * @return array
  20.   */
  21. public function acceptNewsAction(News $news)
  22. {
  23. $newsService = $this->get('news_service')->acceptNews($news);
  24. return [];
  25. }
  26. }


Chociażby tak, wtedy masz nie zależne usługi operujące na jakiejś konretnej funkcjonalności, chociażby News'ach.

Zobacz dodatkowo:
@see
@see

#pisane z palca#
Matrix12
Powiedz mi w routingu lepiej używać adnotacji czy jednak config.yml ?
kpt_lucek
Wedle uznania, ja używam adnotacji, bo jest wszystko w jednym i łatwiej tym zarządzać
Matrix12
Teraz spotykam się z kolejnym ograniczeniem.. Zastanowiłem się i wydaje mi się, że źle podchodzę do tego.. Bo mam kilka bundli np. NewsBundle(który zawiera kontrolery etc.) itd. Chciałem zarządzać tym z poziomu AdminBundle.. Ale czy to wgl ma sens ? Bo z drugiej strony w każdym z bundli w kontrolerach mam metody które są odpowiedzialne za administrowanie danymi bundlami. Czy może nie lepiej zrobić to tak, aby nie powielać dodatkowo kodu tylko w każdym z bundli dodać routing np. /panel/admin/delete( i to będzie odpowiedzialne za usunięcie newsla z bazy)? Jak to rozwiązać. Proszę o wskazówki !
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.