itinance
itinance

Reputation: 12418

Sylius: how to create my own Custom Resource with custom controller without getting "can not autowire"-exception?

I'd like to create my own custom resource in a Sylius application as described here. After many pitfalls and errors as described blow I looked also at some Sylius plugins and found this one, where everything works fine.

However, following Docs and such examples won't work in my cases.

I have defined the resource this way:

resources.yml:

    app.custody:
        driver: doctrine/orm
        classes:
            model: AppBundle\Entity\CustodyWallet
            form: AppBundle\Form\Type\CustodyType
            controller: AppBundle\Controller\Shop\CustodyController

routing.yml:

account_token_custody:
    path: /account/custody
    methods: [GET, POST]
    defaults:
        _controller: app.controller.custody:custodyAction
        _sylius:
            template: "@AppBundle/custody.html.twig"
            redirect: sylius_shop_account_dashboard

The CustodyController looks like this:

use AppBundle\Entity\CustodyWallet;
use AppBundle\Form\Type\CustodyType;
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class CustodyController extends ResourceController
{
    public function custodyAction(Request $request): Response
    {
        // .... code .....
    }
}

However, it results in the following error:

Cannot autowire service "AppBundle\Controller\Shop\CustodyController": argument "$metadata" of method "Sylius\Bundle\ResourceBundle\Controller\ResourceController::__construct()" references interface "Sylius\Component\Resource\Metadata\MetadataInterface" but no such service exists. Did you create a class that implements this interface?

Searching for this error sent me to this GitHub issue, where people recommend to set autowire to false for this particular Controller. So I did:

services.yml:

AppBundle\Controller\Shop\CustodyController:
    autowire: false
    public: true

But this way the constructor is called without any arguments:

Too few arguments to function Sylius\Bundle\ResourceBundle\Controller\ResourceController::__construct(), 0 passed in /var/www/var/cache/dev/Container1MQRWcB/getCustodyControllerService.php on line 16 and exactly 17 expected

I'm curious why a similar configuration works in the CmsPlugin which I have mentioned above but not in my case.

How can I achieve that?

Upvotes: 0

Views: 2781

Answers (1)

Victor Vasiloi
Victor Vasiloi

Reputation: 517

Based on this configuration

sylius_resource:
    resources:
        app.custody:
            driver: doctrine/orm
            classes:
                model: AppBundle\Entity\CustodyWallet
                controller: AppBundle\Controller\Shop\CustodyController

Sylius will generate a few services, including the resource controller, as per resource bundle documentation

Just define the controller class and it will define the service and will wire the right contructor arguments.

In this case, it will generate a service with id app.controller.custody, which definition can be seen by running php bin/console debug:container app.controller.custody.

Then, in services.yaml there's this configuration

AppBundle\Controller\Shop\CustodyController:
    autowire: false
    public: true

which defines another service with id AppBundle\Controller\Shop\CustodyController that's not processed by Sylius.

Even if that configuration is removed, the error will still be there because there's an automatic service loading configured, here's another example of it on the same page.

The solution is simple: exclude resource controllers from that import:

# config/services.yaml
services:
    # makes classes in src/ available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php,CustodyController.php}'

If there are many resource controllers and/or potentially more to be added, then it might be easier to put them in a common folder and add it to the exlude glob pattern, like:

# config/services.yaml
services:
    # makes classes in src/ available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php,ResourceController}'

Upvotes: 2

Related Questions