0

I am trying to implement Symfony Dependency Injection Component (https://symfony.com/doc/current/components/dependency_injection.html) in a non Symfony project.

It works well if I put all my services and parameters into a single "services.yaml" file. Ex:

# services.yaml

parameters:
    mysql.host: "127.0.0.1"
    mysql.database: "database"
    mysql.username: "root"
    mysql.password: ""

    oracle.database: ""
    oracle.username: ""
    oracle.password: ""

    cookie.domain: ".site.local"
    cookie.lifetime: 0
    cookie.useHttps: true

services:
    _defaults:
        autowire: true
        autoconfigure: true

    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'

    OtherNamespace\:
        resource: '../someHomemadeLibrary/src/*'
    
    OtherNamespace\Database\Drivers\MySqlDriver:
        class: OtherNamespace\Database\Drivers\MySqlDriver
        arguments:
            $host: "%mysql.host%"
            $database: "%mysql.database%"
            $username: "%mysql.username%"
            $password: "%mysql.password%"

    # ...    

As the file is getting very large, I wanted to split it into different files, as explained here : https://symfony.com/doc/current/service_container/import.html#importing-configuration-with-imports

So I tried :

# index.php

$containerBuilder = new ContainerBuilder();
$loader = new YamlFileLoader($containerBuilder, new FileLocator('config/'));
$loader->load('services.yaml');
# services.yaml

imports:
    - { resource: "databases.yaml" }
    - { resource: "sessions.yaml" }

services:
    _defaults:
        autowire: true
        autoconfigure: true

    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'


    OtherNamespace\:
        resource: '../someHomemadeLibrary/src/*'

# databases.yaml

parameters:
    mysql.host: "127.0.0.1"
    mysql.database: "database"
    mysql.username: "root"
    mysql.password: ""

services:
    OtherNamespace\Database\Drivers\MySqlDriver:
        class: OtherNamespace\Database\Drivers\MySqlDriver
        arguments:
            $host: "%mysql.host%"
            $database: "%mysql.database%"
            $username: "%mysql.username%"
            $password: "%mysql.password%"

    # ...  

But in this configuration, I get the following error :

Fatal error: Uncaught Symfony\Component\DependencyInjection\Exception\RuntimeException: Cannot autowire service "OtherNamespace\Database\Drivers\MySqlDriver": argument "$host" of method "__construct()" is type-hinted "string", you should configure its value explicitly.

If I switch the import order, then it is the CookieSession that has a problem. It is like the import is overriding the parameters and services to only use the last one ? How can I split my services and their parameters in multiple files ?

Thank you !

RynnHeldeD
  • 51
  • 6
  • Where is that class `OtherNamespace\Database\Drivers\MySqlDriver` stored? Within `src` or anywhere else? – Nico Haase Oct 27 '21 at 15:29
  • 3
    Each service file needs it's own _defaults section for autowiring to work. Adding the section may or may not fix the various issues but it would be a start. You might also run into issues where autowire is trying to wire the same service multiple times. For example, you are trying to explicitly configure MySqlDriver so you will want to ensure it is excluded in services.yaml. It can be a bit of mess until you get used to the various interactions. – Cerad Oct 27 '21 at 15:50
  • Where is `database.yaml` stored? You should not need to `import` it when using the standard configuration schema `config/services.yaml` and `config/packages/database.yaml` or `config/database.yaml`. Symfony will automatically load and process the files in the order they loaded in the [`App\Kernel`](https://github.com/symfony/demo/blob/main/src/Kernel.php#L27) file, with the last to load is the definition used. Typically a Symfony install has the `src/` directory in your project configured as the `App` namespace unless you change it in composer.json. – Will B. Oct 27 '21 at 21:49
  • Hello, thanks for your replies all. @NicoHaase, my `OtherNamespace\Database\Drivers\MySqlDriver` class is in another folder (and subfolders), next to `src` (it is a kind of homemade vendors folder). I updated my `services.yaml` in the post to show that it is also included and loaded. – RynnHeldeD Oct 28 '21 at 15:02
  • Hello @Cerad ! I will try to use the `_defaults` in every yaml file, I'll let you know if it works. What do you mean to "exclude MySqlDrive from services.yaml if it is defined elsewhere" ? Should I explicitly excluded it in the file ? Or just do not have it defined in the two files (which is already the case, every db services are defined in databases.yaml). Thank you – RynnHeldeD Oct 28 '21 at 15:07
  • Hi @WillB. ! `database.yaml` is in my `/config` folder, where I put every yaml files (services.yaml, logger.yaml, etc). I am not using Symfony framework, I am in a custom project where I loaded various libraries with composer and here, I require `symfony/dependency-injection` to use it in my project. So I build my container myself in `index.php` and tell the loader to load my services.yaml file, and that's all, there is no whole framework doing nice things automatically (because my team does not want to use a framework....). – RynnHeldeD Oct 28 '21 at 15:10
  • 1
    @RynnHeldeD The resource section in your main file will automatically try to convert every class under src to a service unless you specifically exclude them. It neither knows nor cares about any additional files so you can potentially end up with conflicts. Key word is potential. Just something to be aware of. – Cerad Oct 28 '21 at 15:16
  • Hello everyone! I managed to make it works thanks to your inputs! It was indeed a problem with services being redefined in my `services.yaml` : in this file, I have the `OtherNamespace\: resources; "......"` block which indeed, as @Cerad pointed out, converts all classes in those paths into services. So the ones I defined explicitly in other yaml were redefined. I had to add an `exclude; ....` parameters, like for `App\` namespace to avoid having them parsed and loaded again and it is now working fine! Thanks and happy Halloween :) – RynnHeldeD Oct 28 '21 at 15:24
  • @RynnHeldeD I had intended to ask "where is the service in `databases.yaml` stored?", since if it was stored in `src/` directory as well, the namespace(s) may be confiscatory. My comment was to compare the merging of the services to your container builder with the `App\Kernel` methodology. and that the definition may conflict with the `resource:` declaration and `OtherNamespace` if it was also located in the same directory. EG: `services.yaml > databases.yaml` as opposed to the `import databases.yaml > services.yaml` – Will B. Oct 28 '21 at 15:35

1 Answers1

0

The problem was that my services were correctly imported from my databases.yaml and other yaml into my services.yaml file, but in this file, I also had automatic classes conversion into services.

So my services defined were reloaded/converted, but without the parameters and all. I added exclusions and it works!

    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'


    OtherNamespace\:
        resource: '../someHomemadeLibrary/src/*'
        exclude: 
        [
            '.../theClassFileDeclaredInAnotherYaml.php',
            '../or/a/whole/folder/not/to/be/loaded/again/*.php'
        ]

Thanks to @NicoHaase, @Cerad and @WillB. for their help !

RynnHeldeD
  • 51
  • 6