Configuration Extensions 

Configuration extensions help add new options to existing configuration sections and new configuration sections.

Creating a Configuration Extension 

Each configuration extension must implement ConfigExtensionInterface (you can also use AbstractConfigExtension as a superclass). To register a new configuration extension, add it to Resources/config/oro/app.yml in your bundle or use config/config.yml of your application. Here is an example:

namespace Acme\Bundle\DemoBundle\Api;

use Oro\Bundle\ApiBundle\Config\Extension\AbstractConfigExtension;

class MyConfigExtension extends AbstractConfigExtension
{
}
config/config.yml 
services:
    acme.api.my_config_extension:
        class: Acme\Bundle\DemoBundle\Api\MyConfigExtension
        public: false

oro_api:
    config_extensions:
        - acme.api.my_config_extension

Add Options to an Existing Configuration Section 

To add options to an existing configuration section, implement the getConfigureCallbacks method of ConfigExtensionInterface. If you need to add logic before the normalization during the configuration validation, implement the getPreProcessCallbacks and getPostProcessCallbacks methods.

The following table describes existing sections to which you can add new options.

Section Name

When to use

entities.entity

Add entity options

entities.entity.field

Add field options

filters

Add options to filters section

filters.field

Add filter options

sorters

Add options to sorters section

sorters.field

Add sorter options

actions.action

Add action options

actions.action.status_code

Add response status code options

actions.action.field

Add field options specific for a particular action. These options override options defined in entities.entity.field

subresources.subresource

Add sub-resource options

subresources.subresource.action

Add sub-resource action options

subresources.subresource.action.field

Add field options specific for a particular action of a sub-resource. These options override options defined in entities.entity.field

Example:

namespace Acme\Bundle\DemoBundle\Api;

use Symfony\Component\Config\Definition\Builder\NodeBuilder;
use Oro\Bundle\ApiBundle\Config\Extension\AbstractConfigExtension;

class MyConfigExtension extends AbstractConfigExtension
{
    /**
     * {@inheritDoc}
     */
    public function getConfigureCallbacks(): array
    {
        return [
            'entities.entity' => function (NodeBuilder $node) {
                $node->scalarNode('some_option');
            }
        ];
    }

    /**
     * {@inheritDoc}
     */
    public function getPreProcessCallbacks(): array
    {
        return [
            'entities.entity' => function (array $config) {
                // do something
                return $config;
            }
        ];
    }

    /**
     * {@inheritDoc}
     */
    public function getPostProcessCallbacks(): array
    {
        return [
            'entities.entity' => function (array $config) {
                // do something
                return $config;
            }
        ];
    }
}

Add New Configuration Section 

To add new configuration section, create a class implements ConfigurationSectionInterface and return instance of it in the getEntityConfigurationSections method of your configuration extension.

By default, the configuration is returned as an array, but if you want to provide a class that represents the configuration of your section, you can implement a configuration loader. The loader is a class that implements ConfigLoaderInterface. An instance of the loader should be returned by the getEntityConfigurationLoaders method of your configuration extension.

An example of a simple configuration section:

namespace Acme\Bundle\DemoBundle\Api\Config\Definition;

use Oro\Bundle\ApiBundle\Config\Definition\AbstractConfigurationSection;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;

class MyConfiguration extends AbstractConfigurationSection
{
    /**
     * {@inheritDoc}
     */
    public function configure(NodeBuilder $node): void
    {
        $node->scalarNode('some_option');
    }
}

An example of a configuration section that other bundles can extend:

namespace Acme\Bundle\DemoBundle\Api\Config\Definition;

use Oro\Bundle\ApiBundle\Config\Definition\AbstractConfigurationSection;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;

class MyConfiguration extends AbstractConfigurationSection
{
    /**
     * {@inheritDoc}
     */
    public function configure(NodeBuilder $node): void
    {
        $sectionName = 'my_section';

        /** @var ArrayNodeDefinition $parentNode */
        $parentNode = $node->end();
        $this->callConfigureCallbacks($node, $sectionName);
        $this->addPreProcessCallbacks($parentNode, $sectionName);
        $this->addPostProcessCallbacks($parentNode, $sectionName);

        $node->scalarNode('some_option');
    }
}

An example of a configuration section loader:

namespace Acme\Bundle\DemoBundle\Api\Config\Loader;

use Acme\Bundle\DemoBundle\Api\Config\MyConfigSection;
use Oro\Bundle\ApiBundle\Config\Loader\AbstractConfigLoader;

class MyConfigLoader extends AbstractConfigLoader
{
    /**
     * {@inheritDoc}
     */
    public function load(array $config): mixed
    {
        $result = new MyConfigSection();
        foreach ($config as $key => $value) {
            $this->loadConfigValue($result, $key, $value);
        }

        return $result;
    }
}

An example of a configuration extension:

namespace Acme\Bundle\DemoBundle\Api\Config\Extension;

use Acme\Bundle\DemoBundle\Api\Config\Definition\MyConfiguration;
use Acme\Bundle\DemoBundle\Api\Config\Loader\MyConfigLoader;
use Oro\Bundle\ApiBundle\Config\Extension\AbstractConfigExtension;

class MyConfigExtension extends AbstractConfigExtension
{
    /**
     * {@inheritDoc}
     */
    public function getEntityConfigurationSections(): array
    {
        return ['my_section' => new MyConfiguration()];
    }

    /**
     * {@inheritDoc}
     */
    public function getEntityConfigurationLoaders(): array
    {
        return ['my_section' => new MyConfigLoader()];
    }
}

An example of how to use the created configuration section:

api:
    entities:
        Acme\Bundle\DemoBundle\Entity\SomeEntity:
            my_section:
                my_option: value

To check that your configuration section is added correctly, run php bin/console oro:api:config:dump-reference. The output should look similar to the following:

Resources/config/oro/api.yml 
api:
    entities:
        name:
            my_section:
                my_option: ~