symfony 5.1: how to define a controller as a service

3.1k views Asked by At

I'm a bit of a noob when it comes to Symfony. I am attempting to create a bundle with a controller that accepts a service as a constructor argument; however, I am receiving this error:

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

This is the 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 http://symfony.com/schema/dic/services/services-1.0.xsd">
    <services>
        <service id="acme_heartbeat.service.heartbeat_service"
                 class="Acme\HeartbeatBundle\Service\HeartbeatService">
            <argument type="string" id="heartbeat.address"/>
        </service>
        <service id="acme_heartbeat.controller.heartbeat_controller"
                 class="Acme\HeartbeatBundle\Controller\HeartbeatController">
            <argument type="service" id="acme_heartbeat.service.heartbeat_service"/>
            <tag name="controller.service_arguments" />
        </service>
        ...
    </services>
</container>

This is the Resources/config/routes.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/routing
        https://symfony.com/schema/routing/routing-1.0.xsd">
    <route id="heartbeat" path="/heartbeat" controller="Acme\HeartbeatBundle\Controller\HeartbeatController::heartbeat" methods="GET" />
</routes>

The main Symfony app's config/routes.yaml:

heartbeat:
    resource: '@AcmeHeartbeatBundle/Resources/config/routes.xml'

The Controller/HeartbeatController.php:

<?php

declare(strict_types=1);

namespace Acme\HeartbeatBundle\Controller;

use Acme\HeartbeatBundle\Service\HeartbeatService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class HeartbeatController extends AbstractController
{
    public function __construct(HeartbeatService $hbs)
    {
        ...
    }

    ...
}

This is the Service/HeartbeatService.php:

<?php

namespace Acme\HeartbeatBundle\Service;

class HeartbeatService
{
    ...
}

Could someone tell me what I'm doing wrong?

1

There are 1 answers

5
msg On BEST ANSWER

Since Symfony 4, the preferred way of service naming is to use the FQCN as the service id in order to ease autowiring. You have used an old-style string id so even though the class matches, when Symfony looks it up by the expected id, it cannot find it.

It's an easy fix, either change the definition to

<service id="Acme\HeartbeatBundle\Controller\HeartbeatController">

Or add an alias:

<service id="Acme\HeartbeatBundle\Controller\HeartbeatController" 
         alias="acme_heartbeat.controller.heartbeat_controller" />

You could also change your route definition to the registered service id (acme_heartbeat.controller.heartbeat_controller::index), but I'd follow the recommendations and go with the first option.