Jak uzyskać dostęp do parametrów aplikacji z usługi?


81

Z moich kontrolerów mam dostęp do parametrów aplikacji (tych w /app/config) z

$this->container->getParameter('my_param')

Ale nie wiem, jak uzyskać do niego dostęp z usługi (wyobrażam sobie, że moja klasa usług nie powinna się rozszerzać Symfony\Bundle\FrameworkBundle\Controller\Controller).

Czy powinienem zmapować potrzebne parametry do rejestracji mojej usługi w następujący sposób:

#src/Me/MyBundle/Service/my_service/service.yml
parameters:
    my_param1: %my_param1%
    my_param2: %my_param2%
    my_param3: %my_param3%

czy coś podobnego? Jak mogę uzyskać dostęp do parametrów mojej aplikacji z usługi?


To pytanie wydaje się takie samo, ale moje faktycznie na nie odpowiada (parametry ze sterownika), mówię o dostępie z usługi.



Moje pytanie faktycznie odpowiada na to (parametry ze sterownika), mówię tutaj o dostępie z usługi
— Pierre de LESPINAY

Nie jestem pewien, czy cię rozumiem. Czy zgadzasz się z duplikatem? Obecnie kontrolery są usługami w Symfony.
— Tomáš Votruba

Nie zgadzam się z duplikatem. Drugie pytanie dotyczy konkretnie kontrolerów, za pomocą których łatwo uzyskuje się parametry $this->getParameter().
— Pierre de LESPINAY

To prawda, zgadzam się. I nadal jest to możliwe. Istnieje również tendencja do odchodzenia od wstrzykiwania pojemnika w dowolne miejsce i przechodzenia do wstrzykiwania konstruktora. Dzięki automatycznemu wykrywaniu usługi PSR-4 i wiązaniu parametrów: symfony.com/blog/new-in-symfony-3-4-local-service-binding jest czysty i znacznie krótszy w użyciu.
— Tomáš Votruba

Odpowiedzi:


123

Parametry można przekazywać do usługi w taki sam sposób, jak wstrzykujesz inne usługi, określając je w definicji usługi. Na przykład w YAML:

services:
    my_service:
        class:  My\Bundle\Service\MyService
        arguments: [%my_param1%, %my_param2%]

gdzie %my_param1%etc odpowiada parametrowi o nazwie my_param1. Wtedy twój konstruktor klasy usług mógłby wyglądać następująco:

public function __construct($myParam1, $myParam2)
{
    // ...
}

czy istnieje sposób postępowania w przypadku, gdy parametr nie istnieje? pochodzi z wyjątku IOC w symfony.
— Mohammed Yassine CHABLI,

i skąd my_param1bierze się wartość ?
— Sliq

@Sliq, definiujesz to w parameters.yml
— Graph

35

Czysta droga 2018

Od 2018 roku i Symfony 3.4 istnieje znacznie czystszy sposób - łatwy w konfiguracji i obsłudze.

Zamiast używać kontenera i anty-wzorca lokalizatora usługi / parametrów, możesz przekazać parametry do klasy za pośrednictwem jej konstruktora . Nie martw się, nie jest to czasochłonna praca, ale raczej konfiguracja raz i zapomnij .

Jak to ustawić w 2 krokach?

1. config.yml

# config.yml
parameters:
    api_pass: 'secret_password'
    api_user: 'my_name'

services:
    _defaults:
        autowire: true
        bind:
            $apiPass: '%api_pass%'
            $apiUser: '%api_user%'

    App\:
        resource: ..

2. Dowolne Controller

<?php declare(strict_types=1);

final class ApiController extends SymfonyController
{
    /**
     * @var string 
     */
    private $apiPass;

    /**
     * @var string
     */
    private $apiUser;

    public function __construct(string $apiPass, string $apiUser)
    {
        $this->apiPass = $apiPass;
        $this->apiUser = $apiUser;
    }

    public function registerAction(): void
    {
        var_dump($this->apiPass); // "secret_password"
        var_dump($this->apiUser); // "my_name"
    }
}

Gotowe do natychmiastowej aktualizacji!

Jeśli korzystasz ze starszego podejścia, możesz je zautomatyzować za pomocą Rectora .

Czytaj więcej

Nazywa się to wstrzyknięciem konstruktora do lokalizatora usług podejściem .

Aby dowiedzieć się więcej na ten temat, zajrzyj do mojego postu Jak uzyskać parametry w kontrolerze Symfony w czysty sposób .

(Jest przetestowany i aktualizuję go pod kątem nowej głównej wersji Symfony (5, 6 ...)).



Dzięki za komentarz. Powyższa konfiguracja działa dla dowolnej usługi, kontrolera, repozytorium lub własnej usługi. Nie ma różnicy.
— Tomáš Votruba

18

Zamiast mapować potrzebne parametry jeden po drugim, dlaczego nie pozwolić usłudze na bezpośredni dostęp do kontenera? W ten sposób nie musisz aktualizować mapowania, jeśli dodano nowe parametry (które dotyczą Twojej usługi).

Aby to zrobić:

Wprowadź następujące zmiany w klasie usługi

use Symfony\Component\DependencyInjection\ContainerInterface; // <- Add this

class MyServiceClass
{
    private $container; // <- Add this
    public function __construct(ContainerInterface $container) // <- Add this
    {
        $this->container = $container;
    }
    public function doSomething()
    {
        $this->container->getParameter('param_name_1'); // <- Access your param
    }
}

Dodaj @service_container jako „argumenty” w swoim services.yml

services:
  my_service_id:
    class: ...\MyServiceClass
    arguments: ["@service_container"]  // <- Add this

1
Dokładnie to, czego szukałem, dlatego lubię wtrysk zależności :)
— klimpond

44
-1. Przekazywanie pojemnika w całości mija się z celem wstrzyknięcia zależności. Twoja klasa powinna mieć tylko to, czego faktycznie potrzebuje do działania, a nie cały kontener.
— Richsage

@richsage, czy istnieje alternatywa dla osiągnięcia podobnych wyników - więc deklaracja usługi nie jest aktualizowana dla każdego parametru? To również wygląda trochę lepiej niż wprowadzanie parametrów jeden po drugim.
— Batandwa

1
Przekazanie całego kontenera do serwisu to naprawdę zły pomysł. Jak mówi @richsage, nie pasuje do celu wstrzykiwania zależności. Jeśli nie chcesz używać wstrzykiwania zależności, nie używaj Symfony2 :)
— tersakyan

2
@tersakyan, ale co z kontrolerami? domyślnie wszystkie kontrolery mają dostęp do kontrolera. Czy zatem nie powinniśmy również używać kontrolerów? :)
— Alex Zheka

9

Od czasów symfony 4.1 jest nowy, bardzo czysty sposób na osiągnięcie tego celu

<?php
// src/Service/MessageGeneratorService.php

use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

class MessageGeneratorService
{
 private $params;
 public function __construct(ParameterBagInterface $params)
 {
      $this->params = $params;
 }
 public function someMethod()
 {
     $parameterValue = $this->params->get('parameter_name');
...
 }
}

źródło: https://symfony.com/blog/new-in-symfony-4-1-getting-container-parameters-as-a-service .


6

Aby rozwiązać niektóre z wymienionych problemów, definiuję parametr tablicy, a następnie wstrzykuję go. Dodanie nowego parametru później wymaga jedynie dodania do tablicy parametrów bez zmiany argumentów lub konstrukcji service_container.

A więc rozszerzenie @richsage answer:

parameters.yml

parameters:
    array_param_name:
        param_name_1:   "value"
        param_name_2:   "value"

services.yml

services:
    my_service:
        class:  My\Bundle\Service\MyService
        arguments: [%array_param_name%]

Następnie wejdź do klasy

public function __construct($params)
{
    $this->param1 = array_key_exists('param_name_1',$params)
        ? $params['param_name_1'] : null;
    // ...
}

W chwili pisania tego komentarza, niestety zagnieżdżanie parametrów nie jest możliwe w Symfony, patrz docs: symfony.com/doc/current/service_container/ ...
— Tomáš Votruba

5

Dzięki Symfony 4.1 rozwiązanie jest dość proste.

Oto fragment oryginalnego posta:

// src/Service/MessageGenerator.php
// ...

use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

class MessageGenerator
{
    private $params;

    public function __construct(ParameterBagInterface $params)
    {
        $this->params = $params;
    }

    public function someMethod()
    {
        $parameterValue = $this->params->get('parameter_name');
        // ...
    }
}

Link do oryginalnego posta: https://symfony.com/blog/new-in-symfony-4-1-getting-container-parameters-as-a-service


0

@richsage jest poprawne (dla Symfony 3.?), ale nie działało z moim Symfony 4.x. A więc tutaj jest Symfony 4.

w pliku services.yaml

parameters:
    param1: 'hello'

Services:
    App\Service\routineCheck:
            arguments:
                $toBechecked: '%param1%'  # argument must match in class constructor

w pliku klasy usług routineCheck.php wykonaj taki konstruktor

private $toBechecked;

public function __construct($toBechecked)
{
    $this->toBechecked = $toBechecked;
}

public function echoSomething()
{
    echo $this->toBechecked;
}

Gotowe.


Czy możesz to dalej wyjaśnić? Co dokładnie nie zadziałało z innym rozwiązaniem - czy są wyświetlane jakieś komunikaty o błędach?
— Nico Haase

Użył ParameterBagInterface $ params w swoim konstruktorze, ale aby w pełni wykorzystać konfigurację parametrów w services.yaml, użyłem iniekcji zależności.
— Dung

Czy możesz to dalej wyjaśnić? Odpowiedź Richsage nie zawiera tego ParameterBagInterface, ale listę parametrów do wstrzyknięcia, tak jak twój kod
— Nico Haase,

Moja odpowiedź została opublikowana w 2012 roku, kiedy ekosystemem był tylko Symfony 2. Nie używam już Symfony, więc nie aktualizowałem ich dla kolejnych wersji.
— richsage

-1

Symfony 3.4 tutaj.

Po kilku badaniach uważam, że przekazywanie parametrów do klasy / usługi za pośrednictwem jej konstruktora nie jest zawsze dobrym pomysłem. Wyobraź sobie, że musisz przekazać do kontrolera / usługi więcej parametrów niż 2 lub 3. Co wtedy? Byłoby śmieszne, gdybyśmy przekazali, powiedzmy, do 10 parametrów.

Zamiast tego użyj ParameterBagklasy jako zależności podczas deklarowania usługi w yml, a następnie użyj dowolnej liczby parametrów.

Konkretny przykład, powiedzmy, że masz usługę pocztową, taką jak PHPMailer, i chcesz mieć parametry połączenia PHPMailer w paramters.ymlpliku:

#parameters.yml
parameters:
    mail_admin: abc@abc.abc
    mail_host: mail.abc.com
    mail_username: noreply@abc.com
    mail_password: pass
    mail_from: contact@abc.com
    mail_from_name: contact@abc.com
    mail_smtp_secure: 'ssl'
    mail_port: 465

#services.yml
services:
    app.php_mailer:
        class: AppBundle\Services\PHPMailerService
        arguments: ['@assetic.parameter_bag'] #here one could have other services to be injected
        public: true

# AppBundle\Services\PHPMailerService.php
...
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
...
class PHPMailerService
{
    private $parameterBag;
    private $mailAdmin;
    private $mailHost;
    private $mailUsername;
    private $mailPassword;
    private $mailFrom;
    private $mailFromName;
    private $mailSMTPSecure;
    private $mailPort;
}
public function __construct(ParameterBag $parameterBag)
{
    $this->parameterBag = $parameterBag;

    $this->mailAdmin      = $this->parameterBag->get('mail_admin');
    $this->mailHost       = $this->parameterBag->get('mail_host');
    $this->mailUsername   = $this->parameterBag->get('mail_username');
    $this->mailPassword   = $this->parameterBag->get('mail_password');
    $this->mailFrom       = $this->parameterBag->get('mail_from');
    $this->mailFromName   = $this->parameterBag->get('mail_from_name');
    $this->mailSMTPSecure = $this->parameterBag->get('mail_smtp_secure');
    $this->mailPort       = $this->parameterBag->get('mail_port');
}
public function sendEmail()
{
    //...
}

Myślę, że to lepszy sposób.


-1

W symfony 4 możemy uzyskać dostęp do parametrów poprzez wstrzyknięcie zależności:

Usługi:

   use Symfony\Component\DependencyInjection\ContainerInterface as Container;

   MyServices {

         protected $container;
         protected $path;

         public function __construct(Container $container)
         {
             $this->container = $container;
             $this->path = $this->container->getParameter('upload_directory');
         }
    }

parameters.yml:

parameters:
     upload_directory: '%kernel.project_dir%/public/uploads'

Dostarczony kod nie używa poprawnie DI - wstrzyknięcie całego kontenera jest uważane za zły styl, ponieważ ukrywasz prawdziwe zależności
— Nico Haase,

Myślę, że mylisz koncepcje, w przykładzie pokazuję tylko ogólny przypadek. W razie wątpliwości zapoznaj się z oficjalną dokumentacją symfony przed oddaniem głosu. symfony.com/doc/current/components/dependency_injection.html
— shades3002

Czy możesz to dalej wyjaśnić? Połączona dokumentacja jasno stwierdza, że ​​wstrzyknięcie pojemnika nie jest dobrym pomysłem i nie pokazuje żadnego przykładu, który używa tego typu wtrysku - tak jak wyraźnie, nie wstrzykujesz zależności, kiedy wstrzykujesz cały pojemnik
— Nico Haase
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.