0

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;

LeeTee
  • 6,401
  • 16
  • 79
  • 139
  • You need to define the required arguments in the service definition, in your configuration file. – yivi Aug 18 '21 at 11:24
  • https://symfony.com/doc/current/service_container.html#explicitly-configuring-services-and-arguments – yivi Aug 18 '21 at 11:30
  • Thanks but I have read that page so many times I have lost count and I still am none the wiser as to exactly what I need to add and to where. Please show using my code. – LeeTee Aug 18 '21 at 12:17
  • I've pointed you exactly where the documentation is within that page. Just read the documentation the linked paragraphs and apply the knowledge you'll get. – yivi Aug 18 '21 at 12:19
  • 1
    @LeeTee I know the docs are very confusing. [This answer](https://stackoverflow.com/questions/68683184/how-to-fix-somecontroller-has-no-container-set-on-controllers-defined-in-sy/68686747#68686747) is one approach. You will need to extend the AbstractController class to make it work. It violates some of the recommended practices but it is better than floundering around. – Cerad Aug 18 '21 at 12:24
  • Thankyou @Cerad for confirming to use AbstractController and also for the link. Can I just ask about the services? The symfony cast tutorials I was following, loaded services.xml into the dependancy injection but all the examples given tend to be using services.yaml. I xould prefer to use the XML if possible but cannot find exactly what I need anywhere. – LeeTee Aug 18 '21 at 13:08
  • 1
    @LeeTee In the Symfony docs, most of the container service definitions have tabs which allow you to switch between yaml, xml or php. Most of the internal Symfony stuff now uses php. I have also converted much of my own code to it. Bit verbose perhaps but it has the advantage of being a programing language. Otherwise I just use yaml. Too many traumatic xml memories from the 90s. It's always up to you unless you want to contribute to the core framework. – Cerad Aug 18 '21 at 13:18
  • As it stands, you should remove the `calls` tag, since it's not a `ServiceSubscriber` nor it has a `setContainer` method. And the doctrine service tag should be `doctrine.orm.default_entity_manager`, if I'm not mistaken. – msg Aug 19 '21 at 09:39
  • thanks @msg, I made those changes but get the exact same error btw, I added that info based on this discussion which suggested call tag is needed: https://symfonycasts.com/screencast/symfony-bundle/controller-functional-test#comment-4844889084 – LeeTee Aug 19 '21 at 10:18
  • Then, despite saying otherwise, your config might not be being loaded. Can you show your `Bundle` class? – msg Aug 19 '21 at 10:23

1 Answers1

0

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.

Cerad
  • 48,157
  • 8
  • 90
  • 92
  • Ok I implemented the above but still get the same error, "The controller for URI "/test" 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?" :( – LeeTee Aug 18 '21 at 13:30
  • You did not make the controller service public. Use bin/console debug:container TestController to verify. And if you used the snake case service id as shown in the answer then you need to update your route to use it as well. – Cerad Aug 18 '21 at 13:36
  • Yes it is public so must be the routing. And yes I used the snake case ID as you showed, So where do I add the snake case service ID? In Resources/Config/routes.yaml of the bundle? – LeeTee Aug 18 '21 at 14:55
  • Still no joy, I have updated the original question to replct changes t my code in case I have done something stupid. Which will proabbly be the case! – LeeTee Aug 18 '21 at 15:37
  • Are you actually loading the routes file? Do you have another routes file at the application level that might be overriding it? What does bin/console debug:router bundle1 show? If you are still getting an error about TestController then you still have a route mapped to TestController and not test_controller. By the way, your error message states /list but the bundle1 route you posted is just /. – Cerad Aug 18 '21 at 16:36
  • Im using annotations.yaml for the routes in the app but thats all, I didnt realise I needed something else; sorry I am so lost with this. – LeeTee Aug 18 '21 at 21:40
  • 2
    Probably time for a break. If you are determined to use annotations (not recommended for bundles) then just use the controller class name for the service id. Once you get something working then things will probably click into place. – Cerad Aug 18 '21 at 21:54
  • Ok Im back refreshed hpeing to get this thing finally working :) so I dont need the routes.yaml if I am using annotations? Id prefer to keep annotations for now as it works. And then I go back to using the class path instead of snake case in the service id of the xml file? – LeeTee Aug 19 '21 at 08:30
  • Yep on the class path. The best practices say to use snake case but either way works fine. I might add that in the alternative approaches link I gave in the answer, you can actually use basically the same services.yaml file as the application does and just let autowire do the work. No real reason not to except the best practices says otherwise. – Cerad Aug 19 '21 at 12:13
  • Sorry to hear that. Perhaps you could create a github repo for your test bundle(s). [Here](https://github.com/cerad/bundle5) is what I have been using. The bundle code is under src-my-bundle. – Cerad Aug 19 '21 at 22:06
  • Im not 100% sure what changed for before but I managed to get it working by extending the abstract controller and removing the constructor. Thanks fo rall your help. – LeeTee Aug 23 '21 at 07:40
  • Congrats. Just be aware the getDoctrine will be deprecated in 5.4. So sooner or later you will need to get constructor or action injection working. – Cerad Aug 23 '21 at 13:05