hanish singla
hanish singla

Reputation: 792

Symfony3: Service not able to get arguments

I have made a service to get Doctrine connection in my models (Not sure if it is a nice approach but I dont want to pass connection from controller to model constructor each time).

So lets say I want products in my controller

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

I have Product model Which will access a helper to get "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;
    }
}

Now this helper is defined in 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;
    }

}

Lets say this service needs "service container" so in app/config/services.yml

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

But it gives me error:

Catchable Fatal Error: Argument 1 passed to AppBundle\Helper\ContainerHelper::__construct() must implement interface Symfony\Component\DependencyInjection\ContainerInterface, none given, called in \src\AppBundle\Model\ProductModel.php on line 148 and defined

While I believe that I have implemented it correctly according to http://symfony.com/doc/current/book/service_container.html and http://anjanasilva.com/blog/injecting-services-in-symfony-2/, its certain that I have missed something or just got the whole bad idea. I need to know if it is a correct concept or what I have missed

Upvotes: 2

Views: 983

Answers (3)

hanish singla
hanish singla

Reputation: 792

With new version of Symfony 3.3, a new feature is added (Auto-wired Services Dependencies)

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

Using this feature, I solved this issue in following way:

  1. Added a new directory /src/AppBundle/Model
  2. Added my model classes in this directory

    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. Added in my app/config/services.yml

    AppBundle\Modal\:
       resource: '../../src/AppBundle/Modal/*'
       public: true
    
  4. In my controller I can use it like

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

P.S. Dont forget to add use AppBundle\Entity\Product\Product; in controller

Upvotes: 1

Tomas Votruba
Tomas Votruba

Reputation: 24280

Rather than using helpers, I'd recommend using constructor injection and autowiring. It's more safe, future proof and easier to extend and test.

In such case, you'd have to create ProductRepository (more common and standard name for ProductModel) and pass it to controller.

1. Controller

<?php

class SomeController
{
    /**
     * @var ProductRepository
     */
    private $productRepository;

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

    public function getProductsAction()
    {
        return $this->productRepository->getProducts();
    }
}

If you have difficulties to register controller as a service, just use Symplify\ControllerAutowire bundle.

2. ProductRepository

// 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. Service registration

# app/cofig/servies.yml

services:
    product_repository:
        class: AppBundle\Repository\ProductRepository
        autowire: true

For more you can see similar question with answer here: Symfony 3 - Outsourcing Controller Code into Service Layer

Upvotes: 1

Cerad
Cerad

Reputation: 48865

While @pavlovich is trying to fix your existing code, I really think you are making this much more convoluted than it has to be. ProductModel itself should be a service with your database connection injected into it.

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();

Upvotes: 2

Related Questions