Làm cách nào để truy cập các thông số ứng dụng từ một dịch vụ?


81

Từ bộ điều khiển của mình, tôi truy cập các thông số ứng dụng (những trong đó /app/config) với

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

Nhưng tôi không biết cách truy cập nó từ một dịch vụ (tôi tưởng tượng rằng lớp dịch vụ của tôi không được phép mở rộng Symfony\Bundle\FrameworkBundle\Controller\Controller).

Tôi có nên ánh xạ các thông số cần thiết vào đăng ký dịch vụ của mình như thế này không:

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

hoặc một cái gì đó tương tự? Làm cách nào để truy cập vào các thông số ứng dụng của tôi từ một dịch vụ?


Câu hỏi này có vẻ giống nhau nhưng tôi thực sự trả lời cho nó (các tham số từ bộ điều khiển), tôi đang nói về việc truy cập từ một dịch vụ.



Câu hỏi của tôi thực sự trả lời cho câu hỏi này (các thông số từ bộ điều khiển), tôi đang nói về việc truy cập từ một dịch vụ ở đây
Pierre de LESPINAY

Tôi không chắc mình hiểu bạn. Bạn có đồng ý với trùng lặp? Bộ điều khiển là dịch vụ trong Symfony ngày nay.
Tomáš Votruba

Tôi không đồng ý với bản sao. Câu hỏi còn lại là đặc biệt yêu cầu Bộ điều khiển dễ dàng nhận được các tham số $this->getParameter().
Pierre de LESPINAY

Đó là sự thật, tôi đồng ý. Và nó vẫn có thể. Cũng có xu hướng tránh xa vùng chứa được bơm vào bất cứ đâu và chuyển sang tiêm vào phương thức xây dựng. Nhờ tính năng tự động phát hiện dịch vụ PSR-4 và ràng buộc các tham số: symfony.com/blog/new-in-symfony-3-4-local-service-binding , nó sạch sẽ và ngắn hơn nhiều để làm việc.
Tomáš Votruba

Câu trả lời:


123

Bạn có thể chuyển các tham số cho dịch vụ của mình giống như cách bạn đưa vào các dịch vụ khác, bằng cách chỉ định chúng trong định nghĩa dịch vụ của bạn. Ví dụ, trong YAML:

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

trong đó %my_param1%vv tương ứng với một tham số có tên my_param1. Sau đó, phương thức khởi tạo lớp dịch vụ của bạn sau đó có thể là:

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

có cách nào để xử lý trong trường hợp tham số không tồn tại không? thay thế IOC ngoại lệ của symfony.
Mohammed Yassine CHABLI

và giá trị của my_param1nó đến từ đâu?
Sliq

@Sliq, bạn xác định nó trong tham số.yml
Biểu đồ

35

Con đường sạch sẽ 2018

Kể từ năm 2018 và Symfony 3.4 có nhiều cách gọn gàng hơn - dễ thiết lập và sử dụng.

Thay vì sử dụng vùng chứa và dịch vụ / tham số định vị chống mẫu, bạn có thể chuyển các tham số cho lớp thông qua phương thức khởi tạo của nó . Đừng lo lắng, đó không phải là công việc đòi hỏi thời gian, mà là thiết lập phương pháp tiếp cận một lần và quên .

Làm thế nào để thiết lập nó trong 2 bước?

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. Bất kỳ 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"
    }
}

Sẵn sàng nâng cấp tức thì!

Trong trường hợp bạn sử dụng cách tiếp cận cũ hơn, bạn có thể tự động hóa nó với Hiệu trưởng .

Đọc thêm

Đây được gọi là phương pháp tiếp cận bộ định vị dịch vụ tiêm vào phương thức khởi tạo.

Để đọc thêm về điều này, hãy xem bài đăng của tôi Cách Nhận Tham số trong Bộ điều khiển Symfony theo cách Sạch sẽ .

(Nó đã được thử nghiệm và tôi cập nhật nó cho phiên bản Symfony chính mới (5, 6 ...)).


1
Tôi đã có thể lấy cái gì khác hơn là một lớp điều khiển như một ví dụ mã như OP muốn tiêm tham số trong bất kỳ dịch vụ và autowiring được kích hoạt theo mặc định trong bộ điều khiển SF3
alpadev

Cám ơn bạn đã góp ý. Cấu hình ở trên hoạt động cho mọi dịch vụ, bộ điều khiển, kho lưu trữ hoặc dịch vụ riêng. Không có sự khác biệt.
Tomáš Votruba

18

Thay vì ánh xạ từng thông số cần thiết của bạn, tại sao không cho phép dịch vụ của bạn truy cập trực tiếp vào vùng chứa? Làm như vậy, bạn không phải cập nhật ánh xạ của mình nếu có các tham số mới được thêm vào (liên quan đến dịch vụ của bạn).

Làm như vậy:

Thực hiện các thay đổi sau đối với hạng dịch vụ của bạn

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
    }
}

Thêm @service_container làm "đối số" trong services.yml của bạn

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

1
Chính xác những gì tôi đang tìm kiếm, đó là lý do tại sao tôi như dependency injection :)
klimpond

44
-1. Việc chuyển toàn bộ vùng chứa sẽ làm mất đi mục đích của việc tiêm phụ thuộc. Lớp của bạn chỉ nên được cung cấp những gì nó thực sự cần để hoạt động, không phải toàn bộ vùng chứa.
richsage

@richsage, có giải pháp thay thế nào để đạt được kết quả tương tự - do đó khai báo dịch vụ không được cập nhật cho từng tham số không? Điều này cũng trông gọn gàng hơn một chút so với việc tiêm các thông số từng cái một.
Batandwa

1
Chuyển toàn bộ container cho một dịch vụ là một ý tưởng thực sự tồi. Như @richsage nói, nó không phù hợp với mục đích tiêm phụ thuộc. Nếu bạn không muốn sử dụng dependency injection, sau đó dont sử dụng Symfony2 :)
tersakyan

2
@tersakyan, nhưng còn bộ điều khiển thì sao? theo mặc định, tất cả các bộ điều khiển đều có quyền truy cập vào bộ điều khiển. Sau đó, chúng ta cũng không nên sử dụng bộ điều khiển? :)
Alex Zheka

9

Có một cách mới rất hữu ích để đạt được nó kể từ symfony 4.1

<?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');
...
 }
}

nguồn: https://symfony.com/blog/new-in-symfony-4-1-getting-container-parameters-as-a-service .


6

Giải pháp cho một số vấn đề đã đề cập, tôi xác định một tham số mảng sau đó chèn nó vào. Việc thêm một tham số mới sau đó chỉ yêu cầu thêm vào mảng tham số mà không có bất kỳ thay đổi nào đối với các đối số hoặc cấu trúc service_container.

Vì vậy, mở rộng trên câu trả lời @richsage:

tham số.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%]

Sau đó truy cập vào bên trong lớp

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

Tại thời điểm viết bình luận này, rất tiếc, không thể lồng các tham số trong Symfony, xem docs: symfony.com/doc/current/service_container/…
Tomáš Votruba

5

Với Symfony 4.1 , giải pháp khá đơn giản.

Đây là một đoạn trích từ bài đăng gốc:

// 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');
        // ...
    }
}

Liên kết đến bài viết gốc: https://symfony.com/blog/new-in-symfony-4-1-getting-container-parameters-as-a-service


0

@richsage là đúng (đối với Symfony 3.?) nhưng nó không hoạt động với Symfony 4.x của tôi. Vì vậy, đây là cho Symfony 4.

trong tệp services.yaml

parameters:
    param1: 'hello'

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

trong tệp routineCheck.php lớp dịch vụ của bạn làm hàm tạo như vậy

private $toBechecked;

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

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

Làm xong.


Bạn có thể giải thích thêm không? Chính xác thì điều gì đã không hoạt động với giải pháp khác - có bất kỳ thông báo lỗi nào được đưa ra không?
Nico Haase

Anh ấy đã sử dụng ParameterBagInterface $ params trong constructor của mình, nhưng để sử dụng đầy đủ cấu hình tham số trong services.yaml, tôi đã sử dụng phụ thuộc.
Dung

Bạn có thể giải thích thêm không? Câu trả lời bởi richsage không chứa rằng ParameterBagInterface, nhưng một danh sách các thông số được tiêm, giống như mã của bạn
Nico Haase

Câu trả lời của tôi được đăng vào năm 2012, khi hệ sinh thái chỉ có Symfony 2. Tôi không dùng Symfony nữa nên chưa cập nhật cho các phiên bản tiếp theo.
richsage

-1

Symfony 3.4 tại đây.

Sau một số nghiên cứu, tôi không nghĩ rằng việc truyền các tham số cho một lớp / dịch vụ thông qua phương thức khởi tạo của nó, luôn là một ý kiến ​​hay. Hãy tưởng tượng nếu bạn cần chuyển đến một bộ điều khiển / dịch vụ một số tham số hơn 2 hoặc 3. Sau đó thì sao? Sẽ là vô lý nếu vượt qua, giả sử, tối đa 10 tham số.

Thay vào đó, hãy sử dụng ParameterBaglớp làm phụ thuộc, khi khai báo dịch vụ trong yml, sau đó sử dụng bao nhiêu tham số tùy thích.

Một ví dụ cụ thể, giả sử bạn có một dịch vụ bưu phẩm, như PHPMailer và bạn muốn có các tham số kết nối PHPMailer trong paramters.ymltệp:

#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()
{
    //...
}

Tôi nghĩ đây là một cách tốt hơn.


-1

Trong symfony 4, chúng ta có thể truy cập các tham số bằng cách chèn phụ thuộc:

Dịch vụ:

   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');
         }
    }

tham số.yml:

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

Các mã được cung cấp không sử dụng đúng cách DI - tiêm toàn bộ container được coi phong cách xấu, như bạn ẩn các phụ thuộc thực
Nico Haase

Tôi nghĩ bạn đang nhầm các khái niệm, trong ví dụ tôi chỉ đưa ra một trường hợp chung chung. Nếu nghi ngờ, hãy tham khảo tài liệu chính thức của symfony trước khi bỏ phiếu. symfony.com/doc/current/components/dependency_injection.html
shades3002

Bạn có thể giải thích thêm không? Các tài liệu liên quan nêu rõ rằng tiêm container không phải là một ý tưởng tốt, và không hiển thị bất kỳ ví dụ sử dụng loại tiêm - càng rõ ràng, bạn không tiêm phụ thuộc khi bạn bơm toàn bộ thùng chứa
Nico Haase
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.