0

I'm creating a bundle for a few Symfony projects. This bundle contains one service and this service is created with a constructor containing an array as first and unique parameter. Developers who will use my bundle have to declare this array in the config/properties.yaml file.

Here is how the array in the config/properties.yaml is loaded. I use the new Symfony6 AbstractBundle class.

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

    public function configure(DefinitionConfigurator $definition): void
    {
        $definition->rootNode()
            ->children()
            ->arrayNode('extendable_entities')
            ->defaultValue([])
            ->scalarPrototype()->end()
            ->end()
            ->end()
            ->end();
    }

    public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
    {
        $loader = new PhpFileLoader($builder, new FileLocator(__DIR__ . '/Resources/config/'));
        $loader->load('services.php');
//        dump($container->services()->get('longitude-one.property_bundle.property_service'));

        $container->services()
            ->get('longitude-one.property_bundle.property_service')
            ->arg(0, $config['extendable_entities']);
//        dump($container->services()->get('longitude-one.property_bundle.property_service'));
    }
}

My service is very simple: a constructor with an array as parameter:

//src/Service.php
namespace LongitudeOne\PropertyBundle\Service;

class PropertyService
{
    /**
     * @var string[] list of all extendable classes
     */
    private array $classes;

    /**
     * @param string[] $classes list of all extendable classes
     */
    public function __construct(array $classes)
    {
        die('constructor is called');
    }

Here is how I configure my services inside the bundle:

//src/Resources/config/services.php
return static function (ContainerConfigurator $container) {
    $container->services()
        ->set('longitude-one.property_bundle.property_service', PropertyService::class)
        ->class(PropertyService::class)
        ->autoconfigure()
        ->autowire()
        ->public()
        ->arg(0, param('lopb.extendable_entities'));

    dump($container->services()->get(PropertyService::class));
};

In my test application, as soon as my controller call my service with Dependency Injection Pattern, I got this error.

Cannot resolve argument $propertyService of "App\Controller\Admin\DashboardController::list()": Cannot autowire service "LongitudeOne\PropertyBundle\Service\PropertyService": argument "$classes" of method "__construct()" is type-hinted "array", you should configure its value explicitly.

In the service.php file, I added a dump to check that the bundle is well implemented. The container is dumped:

Symfony\Component\DependencyInjection\Loader\Configurator\ServiceConfigurator {#4798 ▼
  #definition: Symfony\Component\DependencyInjection\Definition {#4800 ▼
    -class: 'LongitudeOne\PropertyBundle\Service\PropertyService'
...
    -autowired: true
...
    #arguments: array:1 [▼
      0 => []
    ]
  }
...
  #id: "LongitudeOne\PropertyBundle\Service\PropertyService"
  -defaultTags: []
  -container: Symfony\Component\DependencyInjection\Compiler
  • I tried to replace the array parameter set by developer in the config/property.yaml file by an empty array ( [] ) ;
  • I checked that bundle is well declared ;
  • I checked that my services.php file (dump is well written) ;
  • I tried to replace the constructor by a method and I added call('setClasses'), but method isn't called.
  • I checked my service with symfony console debug:container longitude-one
  • After reading this question, I set my service as a public service.
/var/www # symfony console debug:container longitude-one
Information for Service "longitude-one.property_bundle.property_service"
========================================================================

 ---------------- ----------------------------------------------------- 
  Option           Value                                                
 ---------------- ----------------------------------------------------- 
  Service ID       longitude-one.property_bundle.property_service       
  Class            LongitudeOne\PropertyBundle\Service\PropertyService  
  Tags             -                                                    
  Public           yes                                                  
  Synthetic        no                                                   
  Lazy             no                                                   
  Shared           yes                                                  
  Abstract         no                                                   
  Autowired        yes                                                  
  Autoconfigured   yes                                                  
  Usages           none

What am I missing?

Alexandre Tranchant
  • 4,426
  • 4
  • 43
  • 70
  • Could you perhaps explain in a bit more detail how you expect data from the application's config/properties.yaml file to end up in your service? – Cerad Dec 27 '22 at 22:54
  • Why don't you use the YamlFileLoader() (or XML one) method from your DependencyInjection file and register your service ? – Skuti Dec 28 '22 at 16:31
  • @Skuti because Symfony6 now recommends Php file . Main argument is PhpFile loader are "faster" than Yaml and Xml. As this code is internal, it has not to be fluent like yaml or precise like XML. (Cerad, I will edit my answer to explain it and tag you tomorrow) – Alexandre Tranchant Dec 28 '22 at 22:01
  • Ok so how did you check that your service is well known by your Container ? Using the symfony command or by refresh your browser ? It could be just a registration order problem. Or maybe the cache – Skuti Dec 29 '22 at 03:07
  • I checked it in the both ways. I edited the question to add the output of `symfony console debug:container` . The cache was regularly cleared. What do you mean by a registration order problem? – Alexandre Tranchant Dec 29 '22 at 13:38
  • @Cerad I added my bundle declaration. It explains how I expect data from the application's config/properties.yaml file to end up in my service. – Alexandre Tranchant Dec 29 '22 at 15:14

1 Answers1

0

First of all, a small miss-configuration was present in my services.yaml of my application overriding the vendor service declarations. Because of it, I got a wrong error message. As soon as I remove it, I got a more explicit message:

Cannot autowire argument $propertyService of "LongitudeOne\PropertyBundle\Tests\App\Controller\Admin\DashboardController::list()": it references class "LongitudeOne\PropertyBundle\Service\PropertyService" but no such service exists. You should maybe alias this class to the existing "longitude-one.property_bundle.property_service" service.

I added it:

->alias(PropertyService::class, 'longitude-one.property_bundle.property_service')

It works with this services.php file:

<?php

/**
 * This file is part of the LongitudeOne/PropertyBundle
 *
 * PHP 8.1 | Symfony 6.1+
 *
 * Copyright LongitudeOne - Alexandre Tranchant
 * Copyright 2021 - 2022
 */

namespace LongitudeOne\PropertyBundle\DependencyInjection\Loader\Configurator;

use LongitudeOne\PropertyBundle\Service\PropertyService;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use function Symfony\Component\DependencyInjection\Loader\Configurator\param;
//use function Symfony\Component\DependencyInjection\Loader\Configurator\service;


return static function (ContainerConfigurator $container) {
    $container->services()
        ->set('longitude-one.property_bundle.property_service', PropertyService::class)
        ->class(PropertyService::class)
        ->arg(0, param('extendable_entities'))
        ->alias(PropertyService::class, 'longitude-one.property_bundle.property_service');
};
Alexandre Tranchant
  • 4,426
  • 4
  • 43
  • 70