【问题标题】:Symfony3: Service not able to get argumentsSymfony3:服务无法获取参数
【发布时间】:2016-07-22 10:44:56
【问题描述】:

我已经在我的模型中提供了获取 Doctrine 连接的服务(不确定这是否是一个不错的方法,但我不想每次都将连接从控制器传递到模型构造函数)。

假设我想要控制器中的产品

public function getProductsAction(Request $request) {
    $product_model = new ProductModel();
    return $product_model->getProducts();
}

我有 Product 模型,它将访问帮助器以获取“database_connection”

use AppBundle\Helper\ContainerHelper;

class ProductModel {
    function getProducts() {
        $helper = new ContainerHelper();
        $db = $helper->getDoctrine();

        $query = "SELECT * FROM customer_products;";
        $statement = $db->prepare($query);

        $statement->execute();
        $result = $statement->fetchAll(PDO::FETCH_ASSOC);
        return $result;
    }
}

现在这个助手定义在 src/AppBundle/Helper/ContainerHelper.php 中

namespace AppBundle\Helper;

use Symfony\Component\DependencyInjection\ContainerInterface as Container;

class ContainerHelper {

    private $container;

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

    public static function getDoctrine() {
        $database_connection = $this->container->get('database_connection');
        return $database_connection;
    }

}

假设这个服务需要“服务容器”所以在 app/config/services.yml

services:
    app.container_helper:
        class: AppBundle\Helper\ContainerHelper
        arguments: ['@service_container']

但它给了我错误:

可捕获的致命错误:参数 1 传递给 AppBundle\Helper\ContainerHelper::__construct() 必须实现 接口 Symfony\Component\DependencyInjection\ContainerInterface, 没有给出,在 \src\AppBundle\Model\ProductModel.php 中调用 在第 148 行并定义

虽然我相信我已经根据http://symfony.com/doc/current/book/service_container.htmlhttp://anjanasilva.com/blog/injecting-services-in-symfony-2/ 正确实现了它,但可以肯定的是我错过了一些东西或者只是得到了整个坏主意。我需要知道这是一个正确的概念还是我错过了什么

【问题讨论】:

    标签: service dependency-injection model doctrine symfony


    【解决方案1】:

    在 Symfony 3.3 的新版本中,添加了一个新功能(自动连接服务依赖项)

    https://symfony.com/doc/current/service_container/autowiring.html https://symfony.com/doc/current/service_container/3.3-di-changes.html

    使用此功能,我通过以下方式解决了此问题:

    1. 添加了一个新目录 /src/AppBundle/Model
    2. 在这个目录中添加了我的模型类

      namespace AppBundle\Modal;
      
      use Doctrine\ORM\EntityManagerInterface;
      
      class ProductModal
      {
      
         private $em;
      
         // We need to inject this variables later.
         public function __construct(EntityManagerInterface $entityManager)
         {
             $this->em = $entityManager;
         }
      
         // We need to inject this variables later.
         public function getProducts()
         {
             $statement = $this->em->getConnection()->prepare("SELECT * FROM product WHERE 1");
             $statement->execute();
             $results = $statement->fetchAll();
      
             return $results;
          }
      }
      
    3. 添加到我的 app/config/services.yml

      AppBundle\Modal\:
         resource: '../../src/AppBundle/Modal/*'
         public: true
      
    4. 在我的控制器中,我可以像使用它一样使用它

      $products = $this->get(ProductModal::class)->getProducts();
      

    P.S.别忘了在控制器中添加use AppBundle\Entity\Product\Product;

    【讨论】:

      【解决方案2】:

      我建议使用构造函数注入和自动装配,而不是使用助手。它更安全、面向未来且更易于扩展和测试。

      在这种情况下,您必须创建ProductRepositoryProductModel 的更常用和标准名称)并将其传递给控制器​​。

      1。控制器

      <?php
      
      class SomeController
      {
          /**
           * @var ProductRepository
           */
          private $productRepository;
      
          public function __construct(ProductRepository $productRepository)
          {
              $this->productRepository = $productRepository;
          }
      
          public function getProductsAction()
          {
              return $this->productRepository->getProducts();
          }
      }
      

      如果您将控制器注册为服务有困难,请使用Symplify\ControllerAutowire bundle

      2。产品存储库

      // src/AppBundle/Repository/ProductRepository.php
      
      namespace AppBundle\Repository;
      
      class ProductRepository
      {
          /**
           * @var Doctrine\DBAL\Connection
           */
          private $connection;
      
          public function __construct(Doctrine\DBAL\Connection $connection)
          {
      
              $this->connection = $connection;
          }
      
          public function fetchAll()
          {
              $query = "SELECT * FROM customer_products;";
      
              $statement = $this->connection->prepare($query);
              $statement->execute();
              return $statement->fetchAll(PDO::FETCH_ASSOC);
          }
      }
      

      3。服务注册

      # app/cofig/servies.yml
      
      services:
          product_repository:
              class: AppBundle\Repository\ProductRepository
              autowire: true
      

      有关更多信息,您可以在此处查看类似问题的答案:Symfony 3 - Outsourcing Controller Code into Service Layer

      【讨论】:

      • 这也是一个非常好的和干净的方法。 .但并非一直可行。假设我需要客户控制器中的产品。还需要订单、代金券和许多其他东西。我想在构造函数上自动连接所有这些存储库不是一个好主意
      • 为什么?许多依赖关系应该导致解耦和架构改进。拥有大约 5 个依赖项通常很好。超过 10 是做某事的警报。
      【解决方案3】:

      虽然@pavlovich 正在尝试修复您现有的代码,但我真的认为您让这变得比它必须的要复杂得多。 ProductModel 本身应该是一个服务,将您的数据库连接注入其中。

      class ProductModel {
          public function __construct($conn) {
              $this->conn = $conn;
          }
          public function getProducts() {
              $stmt = $this->conn->executeQuery('SELECT * FROM customer_products');
              return $stmt->fetchAll();
         }
      
      services:
          product_model:
              class: AppBundle\...\ProductModel
              arguments: ['@database_connection']
      
      // controller.php
      $productModel = $this->get('product_model'); // Pull from container
      $products = $productModel->getProducts();
      

      【讨论】:

      • 感谢您的快速帮助。 .我知道我失去了一些东西。额外的辅助层可以避免注入所有模型。我必须为 service.yml 中的所有模型定义相同的注入。是否有任何其他可能的解决方法,例如它们扩展基本模型的其他框架,因此它们会自动获得数据库连接,因为我拥有更多模型,service.yml 将填充相同的重复内容。
      • 看看父服务:symfony.com/doc/current/components/dependency_injection/… 顺便说一句,在回复特定评论时使用@Cerad 之类的东西,这样该人就会收到通知。
      猜你喜欢
      • 2016-01-11
      • 1970-01-01
      • 1970-01-01
      • 2013-08-12
      • 2021-08-08
      • 2013-10-07
      • 1970-01-01
      • 1970-01-01
      • 2018-07-03
      相关资源
      最近更新 更多