LeeTee
LeeTee

Reputation: 6601

How to use doctrine in custom bundle controllers - "Controller has required constructor arguments and does not exist in the container. "

I have a Symfony 5.3 project with two custom reusabale bundles.

I have created an Entity in bundle1, I want to be able to read and write to this from within bundle2

However, I cannot successfully include the Doctrine in any of my bundle controllers.

I have tried everything: extending the Controller, extending AbstractController, adding a constructor to pass the doctrine, defining controller as a service but I cant get anything working.

project/bundle1/src/Controller/testController.php:

namespace Bundle1\TestController;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Doctrine\ORM\EntityManagerInterface;
use Bundle1\Entity;


class TestController 
{

private $entityManager;

public function __construct( EntityManagerInterface $entityManager) {
        $this->em = $entityManager;
}

     /**
     * @Route("/list", name="list")
     */
    public function listingsAction(): Response
    {


    //$this->em = $this->getDoctrine()->getManager();

    return new Response(
            '<html><body><h1>List from DB</h1> 
            
            </body></html>'
        );
    }
}

Error:

The controller for URI "/list" is not callable: Controller "Bundle1\TestController\TestController" has required constructor arguments and does not exist in the container. Did you forget to define the controller as a service?

EDIT** The below has been amended according to help from @Cerad but unfortunately the same error message persists.

I am using autowiring and I have the following services.xml being loaded via dependency injection:

project/bundle1/Resources/config/services.xml:

        <?xml version="1.0" encoding="UTF-8" ?>
        <container xmlns="http://symfony.com/schema/dic/services"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://symfony.com/schema/dic/services
                https://symfony.com/schema/dic/services/services-1.0.xsd">
 <services>
             <service 
                id="Bundle1\Controller\TestController\TestController" 
               public="true">
     <call method="setContainer">
                <argument type="service" id="doctrine.orm.entity_manager"/>
     </call>
                <tag name="controller.service_arguments"/>
            </service>
</services>
        </container>

I have used annotaions for routing

project/config/routes/annotations.yaml file:

controllers-bundle1:
    resource: ../../bundle1/src/Controller/
    type: annotation

When I run php bin/console debug:container 'bundle1.controller.test_controller' in the console, I get:

No services found that match "bundle1.controller.test_controller".

project/bundle1/src/Bundle1.php

namespace Bundle1;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class Bundle1 extends Bundle
{
    public function getPath(): string
    {
        return \dirname(__DIR__);
    }
}

project/config/bundles.php

return [
    Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
    Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
    Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
    Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
    Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
    Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
    Bundle1\Bundle1::class => ['all' => true],
];

It seems to be that I have not correctly defined my controllers as services but cannot find clear information in the documentation on how to do this.

**UPDATE:


just found this in the error stack**

ArgumentCountError Too few arguments to function Bundle1\TestController\TestController::__construct(), 0 passed in /home/Project/vendor/symfony/http-kernel/Controller/ControllerResolver.php on line 147 and exactly 1 expected

in bundle1/src/Controller/TestController.php (line 17) class TestController { private $entityManager; public function __construct( EntityManagerInterface $entityManager) { $this->em = $entityManager;

Upvotes: 0

Views: 1116

Answers (1)

Cerad
Cerad

Reputation: 48883

Let's start with an actual answer that matches the 'best practices' and then discuss a bit.

class TestController // Should not extend anything for bundle controllers
{
    // All services should be injected
    public function __construct(private EntityManagerInterface $em)
...
# Resources/config/services.xml
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <service 
            id="my_bundle.controller.test_controller" 
            class="MyBundle\Controller\TestController" 
            public="true">
            <argument type="service" id="doctrine.orm.entity_manager"/>
            <tag name="controller.service_arguments"/>
        </service>
    </services>
</container>

# and since snake case service ids are recommended
# config/routes.yaml

bundle_test:
    path: /
    controller: my_bundle.controller.test_controller::index

That should give you a working page with the entity manager injected.

We use xml here because it was used in the question but php might be better. The web-profiler-bundle is a good example.

Instead of using the controller class name as the service id we spell one out per the recommended practices. Your routes will need to use it.

The public=true is very important. Symfony uses a container aware controller resolver and ultimately pulls the controller service from the container. So controller services must be public.

The tag is needed if you need to inject services into the action methods. Not sure if you are supposed to do this with bundles or not. If you don't need it then remove it.

And of course you need to manually inject any services or parameters. The container docs have more examples.

Some alternative approaches are discussed here.

Upvotes: 0

Related Questions