Skip to content

Add support for map style array nodes in Configuration #29817

@gingerCodeNinja

Description

@gingerCodeNinja

Currently, the only way to make an arrayNode a map, rather than a list, is to use useAttributeAsKey, the problem with that method is that it requires you to use an attribute within the array as the key. What if you want to use the array key as the .. array key?

For example

            ->arrayNode('servers')
                ->prototype('array')
                    ->children()
                        ->scalarNode('username')
                            ->isRequired()
                        ->end()
                        ->scalarNode('password')
                            ->isRequired()
                        ->end()
                    ->end()
                ->end()
            ->end()

with config

something:
    servers:
        serverA:
            username: userA
            password: passwordA
        serverB:
            username: userB
            password: passwordB

comes out as

servers [
    serverA => [username: userA, password: passwordA],
    serverB => [username: userB, password: passwordB],
]

Great! Until.. you merge config across files. If you put serverA in a file and import it, then add serverB, it comes out like -

servers [
    serverA => [username: userA, password: passwordA],
    0 => [username: userB, password: passwordB],
]

The key for the nodes get lost. This issue is described on https://symfony.com/doc/current/components/config/definition.html

As of writing this, there is an inconsistency: if only one file provides the configuration in question, the keys (i.e. sf_connection and default) are not lost. But if more than one file provides the configuration, the keys are lost as described above.

that's ..not great. Especially if you're relying on referencing that key from somewhere else in your config (i.e. default_server: ..). However, since we've not indicated the arrayNode is a map, I guess this behaviour was undefined (probably odd it kept the string keys in the first place).

You could argue that if the key is important, to put it in the attributes, like

something:
    servers:
        -
            username: userA
            password: passwordA
            server: serverA
        -
            username: userB
            password: passwordB
            server: serverB

with useAttributeAsKey('server'). However, this is not a very efficient way of writing configuration - it's clearly more for XML based processing, not YAML.

One way people have been getting around this is to use useAttributeAsKey with an attribute that doesn't exist, like ->useAttributeAsKey('some_field_that_doesnt_exist') which seems pretty hacky.

Suggest something like

  1. adding ->useAsMap() which turns the array into a map internally, or

  2. breaking arrayNode into listNode and mapNode types (with arrayNode defaulting to listNode for backward compatibility for a while).

Related

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions