php - Symfony 4.0.0 Services argument wiring error

117

I am making a ProductBundle based on FOSUserbundle, but I'm having trouble with the autowiring of service arguments to the FormFactory and ProductManager classes.

I keep getting the error:

Cannot autowire service "App\Boa\ProductBundle\Form\Factory\FormFactory": argument "$name" of method "__construct()" must have a type-hint or be given a value explicitly.

FormFactory class:

namespace App\Boa\ProductBundle\Form\Factory;

use Symfony\Component\Form\FormFactoryInterface;

class FormFactory implements FactoryInterface
  {
/**
 * @var FormFactoryInterface
 */
private $formFactory;

/**
 * @var string
 */
private $name;

/**
 * @var string
 */
private $type;

/**
 * @var array
 */
private $validationGroups;

/**
 * FormFactory constructor.
 *
 * @param FormFactoryInterface $formFactory
 * @param string               $name
 * @param string               $type
 * @param array                $validationGroups
 */
public function __construct(FormFactoryInterface $formFactory, $name, 
$type, array $validationGroups = null)
{
    $this->formFactory = $formFactory;
    $this->name = $name;
    $this->type = $type;
    $this->validationGroups = $validationGroups;
}

/**
 * {@inheritdoc}
 */
public function createForm(array $options = array())
{
    $options = array_merge(array('validation_groups' => $this->validationGroups), $options);

    return $this->formFactory->createNamed($this->name, $this->type, null, $options);
}
}

create.xml ( located in App\Boa\ProductBundle\Resources\config )

<?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="boa_product.create.form.factory" public="true">
    <argument type="service" id="form.factory" />
    <argument key="$name">%boa_product.create.form.name%</argument>
    <argument key="$type">%boa_product.create.form.type%</argument>
    <argument>%boa_product.create.form.validation_groups%</argument>
  </service>

  <service id="boa_product.create.form.type" >
    <tag name="form.type" alias="boa_product_create" />
    <argument key="$class">%boa_product.model.product.class%</argument>
  </service>

  <service id="boa_product.create.controller" public="true">
    <argument type="service" id="boa_product.create.form.factory" />
    <argument type="service" id="boa_product.product_manager" />
  </service>
</services>

And the Extension:

<?php

namespace App\Boa\ProductBundle\DependencyInjection;

use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

class BoaProductExtension extends Extension
{
/**
 * @var array
 */
private static $doctrineDrivers = array(
    'orm' => array(
        'registry' => 'doctrine',
        'tag' => 'doctrine.event_subscriber',
    )
);

public function load(array $configs, ContainerBuilder $container)
{
    $configuration = new Configuration();

    $config = $this->processConfiguration($configuration, $configs);

    $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
    //$Yamlloader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));

    $loader->load('services.xml');

    if ('custom' !== $config['db_driver']) {
        if (isset(self::$doctrineDrivers[$config['db_driver']])) {
            $loader->load('doctrine.xml');
            $container->setAlias('boa_product.doctrine_registry', new Alias(self::$doctrineDrivers[$config['db_driver']]['registry'], false));
        } else {
            $loader->load(sprintf('%s.xml', $config['db_driver']));
        }
        $container->setParameter($this->getAlias().'.backend_type_'.$config['db_driver'], true);
    }

    if (isset(self::$doctrineDrivers[$config['db_driver']])) {
        $definition = $container->getDefinition('boa_product.object_manager');
        $definition->setFactory(array(new Reference('boa_product.doctrine_registry'), 'getManager'));
    }
    $container->setAlias('boa_product.product_manager', new Alias($config['service']['product_manager'], true));
    $container->setAlias('App\Boa\ProductBundle\Model\ProductManagerInterface', new Alias('boa_product.product_manager', true));

    $this->remapParametersNamespaces($config, $container, array(
        '' => array(
            'db_driver' => 'boa_product.storage',
            'model_manager_name' => 'boa_product.model_manager_name',
            'product_class' => 'boa_product.model.product.class',
        ),
    ));

    $this->loadCreate($config['create'], $container, $loader);
}

/**
 * @param array            $config
 * @param ContainerBuilder $container
 * @param array            $namespaces
 */
protected function remapParametersNamespaces(array $config, ContainerBuilder $container, array $namespaces)
{
    foreach ($namespaces as $ns => $map) {
        if ($ns) {
            if (!array_key_exists($ns, $config)) {
                continue;
            }
            $namespaceConfig = $config[$ns];
        } else {
            $namespaceConfig = $config;
        }
        if (is_array($map)) {
            $this->remapParameters($namespaceConfig, $container, $map);
        } else {
            foreach ($namespaceConfig as $name => $value) {
                $container->setParameter(sprintf($map, $name), $value);
            }
        }
    }
}

/*
* @param array            $config
* @param ContainerBuilder $container
* @param array            $map
*/
protected function remapParameters(array $config, ContainerBuilder $container, array $map)
{
    foreach ($map as $name => $paramName) {
        if (array_key_exists($name, $config)) {
            $container->setParameter($paramName, $config[$name]);
        }
    }
}

/**
 * @param array            $config
 * @param ContainerBuilder $container
 * @param XmlFileLoader    $loader
 */
private function loadCreate(array $config, ContainerBuilder $container, XmlFileLoader $loader)
{
    $loader->load('create.xml');

    $this->remapParametersNamespaces($config, $container, array(
        'form' => 'boa_product.create.form.%s',
    ));
}
}

So when reading the documentation of symfony it's normal to have this Exception message because scalar types can't be autowired, but I still have the message when having ( what I thought to be ) the correct xml file with services to manually wire them.

When I change one of the keys in the create.xml file, for example change the key "$name" to "$example" I get following message:

Invalid service "boa_product.create.form.factory": method "App\Boa\ProductBundle\Form\Factory\FormFactory::__construct()" has no argument named "$example". Check your service definition.

So I guess they must be connected in anyway? Then again, removing the complete service with all the arguments just gives me the same old exception

Cannot autowire service "App\Boa\ProductBundle\Form\Factory\FormFactory": argument "$name" of method "__construct()" must have a type-hint or be given a value explicitly.

so it seems that my xml isn't doing much at all.

I have spent the entire day yesterday trying stuff and looking up documentation, but no success. Thanks very much in advance for your help.

If you need more code, I'll be happy to provide it.

Thanks again!

321

Answer

Solution:

Did you try doing what the error message says?

argument "$name" of method "__construct()" must have a type-hint or be given a value explicitly

Neither$name nor$type have type-hint in your method signature. Try to change the method signature to:

public function __construct(FormFactoryInterface $formFactory, string $name, string $type, array $validationGroups = null)

Edit:

The other possibility is that you try to use%boa_product.create.form.name% param in the service definition before this parameter is defined.

162

Answer

Solution:

if you work with interfaces you need to override that interface alias or add an explicit definition of your FormFactory.

check out:

Working with Interfaces

621

Answer

Solution:

In the definition of boa_product.create.controller as a service, you are missing the key attribute for your arguments. Try:

<service id="boa_product.create.controller" class="App\Boa\ProductBundle\Controller\CreateController" public="true">
    <argument key="$formFactory" type="service" id="boa_product.create.form.factory" />
    <argument key="$productManager" type="service" id="boa_product.product_manager" />
</service>

where $formFactory and $productManager are the parameters in CreateController s constructor.

In your extension, you define an alias for App\Boa\ProductBundle\Model\ProductManagerInterface:

$container->setAlias('App\Boa\ProductBundle\Model\ProductManagerInterface', new Alias('boa_product.product_manager', true));

So I guess then, you can remove:

<argument key="$productManager" type="service" id="boa_product.product_manager" />

since this will be autowired.

Alternatively, you could autowire, a.k.a. define an alias for App\Boa\ProductBundle\Form\Factory\FactoryInterface also to boa_product.create.form.factory. how to aurowire services

Add this:

<service id="App\Boa\ProductBundle\Form\Factory\FormFactory" alias="boa_product.create.form.factory" />
626

Answer

Solution:

My issue was eventually solved. I think it was due to me experimenting with the services files but not clearing the cache before testing the result. Because of that I thought that my services were not loaded ( because the result obviously didn't change ).

I ended up putting my Bundle in a separate repository and including it in the project via composer. I also rewrote the config files in YAML instead of XML because I think it's easier to read.

That way I had more control over the debugging it and eventually I got rid of the errors.

Most important thing when working with services is to runphp bin/console cache:clear everytime you make a change.

Thanks for the help

People are also looking for solutions to the problem: php - jQuery Autocomplete in Laravel5 not working

Source

Didn't find the answer?

Our community is visited by hundreds of web development professionals every day. Ask your question and get a quick answer for free.

Ask a Question

Write quick answer

Do you know the answer to this question? Write a quick response to it. With your help, we will make our community stronger.

Similar questions

Find the answer in similar questions on our website.