Symfony3: Сервис не может получать аргументы

Я создал службу для подключения к Doctrine в своих моделях (не уверен, что это хороший подход, но я не хочу каждый раз передавать соединение от контроллера к конструктору модели).

Допустим, мне нужны продукты в моем контроллере.

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

У меня есть модель продукта, которая будет обращаться к помощнику, чтобы получить "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.html и http://anjanasilva.com/blog/injecting-services-in-symfony-2/, я уверен, что я что-то упустил или только что понял всю плохую идею. Мне нужно знать, правильная ли это концепция или что я упустил


person hanish singla    schedule 22.07.2016    source источник


Ответы (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();
person Cerad    schedule 22.07.2016
comment
Спасибо за быструю помощь. . Я знал, что мне чего-то не хватает. Дополнительный вспомогательный слой позволяет избежать внедрения во все модели. Мне нужно будет определить одну и ту же инъекцию для всех моделей в service.yml. Есть ли какой-либо другой возможный обходной путь, как и другие фреймворки, они расширяют базовую модель и, таким образом, автоматически получают соединение с БД, поскольку у меня есть больше моделей, service.yml будет заполнен одним и тем же повторяющимся элементом. - person hanish singla; 22.07.2016
comment
Взгляните на родительские службы: symfony.com/doc/current/components/dependency_injection / Кстати, используйте что-то вроде @Cerad при ответе на конкретный комментарий, чтобы человек получил уведомление. - person Cerad; 25.07.2016

Вместо использования помощников я бы рекомендовал использовать внедрение конструктора и автоматическое подключение. Это более безопасно, ориентировано на будущее, его проще расширять и тестировать.

В таком случае вам нужно будет создать ProductRepository (более распространенное и стандартное имя для ProductModel) и передать его контроллеру.

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.

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. Аутсорсинг кода контроллера на уровне сервисов

person Tomas Votruba    schedule 22.07.2016
comment
Это тоже очень хороший и чистый подход. . но не всегда возможно. Предположим, мне нужны продукты в контроллере клиента. Также нужны заказы, путевки и многое другое. Я полагаю, что не очень хорошая идея автоматически подключать все эти репозитории к конструктору. - person hanish singla; 26.07.2016
comment
Почему? Многие зависимости должны привести к разделению и улучшению архитектуры. Обычно нормально иметь около 5 зависимостей. Больше 10 - это сигнал тревоги, чтобы что-то сделать. - person Tomas Votruba; 26.07.2016

В новой версии 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. Добавлено в моем приложении / 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; в контроллер.

person hanish singla    schedule 04.08.2017