vendor/pimcore/pimcore/bundles/EcommerceFrameworkBundle/DependencyInjection/Configuration.php line 172

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4.  * Pimcore
  5.  *
  6.  * This source file is available under two different licenses:
  7.  * - GNU General Public License version 3 (GPLv3)
  8.  * - Pimcore Enterprise License (PEL)
  9.  * Full copyright and license information is available in
  10.  * LICENSE.md which is distributed with this source code.
  11.  *
  12.  * @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  13.  * @license    http://www.pimcore.org/license     GPLv3 and PEL
  14.  */
  15. namespace Pimcore\Bundle\EcommerceFrameworkBundle\DependencyInjection;
  16. use Pimcore\Bundle\CoreBundle\DependencyInjection\Config\Processor\PlaceholderProcessor;
  17. use Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\AbstractCart;
  18. use Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\Cart;
  19. use Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\CartFactory;
  20. use Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\CartPriceCalculator;
  21. use Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\CartPriceCalculatorFactory;
  22. use Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\MultiCartManager;
  23. use Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\SessionCart;
  24. use Pimcore\Bundle\EcommerceFrameworkBundle\CheckoutManager\CheckoutManagerFactory;
  25. use Pimcore\Bundle\EcommerceFrameworkBundle\CheckoutManager\CommitOrderProcessor;
  26. use Pimcore\Bundle\EcommerceFrameworkBundle\DependencyInjection\Config\Processor\TenantProcessor;
  27. use Pimcore\Bundle\EcommerceFrameworkBundle\DependencyInjection\IndexService\DefaultWorkerConfigMapper;
  28. use Pimcore\Bundle\EcommerceFrameworkBundle\Factory;
  29. use Pimcore\Bundle\EcommerceFrameworkBundle\FilterService\FilterService;
  30. use Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Config\DefaultMysql;
  31. use Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\IndexService;
  32. use Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\ProductCentricBatchProcessingWorker;
  33. use Pimcore\Bundle\EcommerceFrameworkBundle\OfferTool\DefaultService as DefaultOfferToolService;
  34. use Pimcore\Bundle\EcommerceFrameworkBundle\OrderManager\Order\AgentFactory;
  35. use Pimcore\Bundle\EcommerceFrameworkBundle\OrderManager\Order\Listing;
  36. use Pimcore\Bundle\EcommerceFrameworkBundle\OrderManager\OrderManager;
  37. use Pimcore\Bundle\EcommerceFrameworkBundle\PaymentManager\PaymentManager;
  38. use Pimcore\Bundle\EcommerceFrameworkBundle\PricingManager\Environment;
  39. use Pimcore\Bundle\EcommerceFrameworkBundle\PricingManager\PriceInfo;
  40. use Pimcore\Bundle\EcommerceFrameworkBundle\PricingManager\PricingManager;
  41. use Pimcore\Bundle\EcommerceFrameworkBundle\PricingManager\Rule;
  42. use Pimcore\Bundle\EcommerceFrameworkBundle\SessionEnvironment;
  43. use Pimcore\Bundle\EcommerceFrameworkBundle\Tracking\TrackingItemBuilder;
  44. use Pimcore\Bundle\EcommerceFrameworkBundle\Tracking\TrackingManager;
  45. use Pimcore\Bundle\EcommerceFrameworkBundle\VoucherService\DefaultService as DefaultVoucherService;
  46. use Pimcore\Bundle\EcommerceFrameworkBundle\VoucherService\TokenManager\TokenManagerFactory;
  47. use Pimcore\Model\DataObject\OfferToolOffer;
  48. use Pimcore\Model\DataObject\OfferToolOfferItem;
  49. use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
  50. use Symfony\Component\Config\Definition\Builder\NodeDefinition;
  51. use Symfony\Component\Config\Definition\Builder\TreeBuilder;
  52. use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition;
  53. use Symfony\Component\Config\Definition\ConfigurationInterface;
  54. use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
  55. class Configuration implements ConfigurationInterface
  56. {
  57.     /**
  58.      * @var TenantProcessor
  59.      */
  60.     private $tenantProcessor;
  61.     /**
  62.      * @var PlaceholderProcessor
  63.      */
  64.     private $placeholderProcessor;
  65.     /**
  66.      * @var DefaultWorkerConfigMapper
  67.      */
  68.     private $indexWorkerConfigMapper;
  69.     public function __construct()
  70.     {
  71.         $this->tenantProcessor = new TenantProcessor();
  72.         $this->placeholderProcessor = new PlaceholderProcessor();
  73.         $this->indexWorkerConfigMapper = new DefaultWorkerConfigMapper();
  74.     }
  75.     /**
  76.      * @inheritDoc
  77.      */
  78.     public function getConfigTreeBuilder()
  79.     {
  80.         $treeBuilder = new TreeBuilder();
  81.         $rootNode $treeBuilder->root('pimcore_ecommerce_framework');
  82.         $rootNode->addDefaultsIfNotSet();
  83.         $this->addRootNodeChildren($rootNode);
  84.         $rootNode
  85.             ->append($this->buildPimcoreNode())
  86.             ->append($this->buildFactoryNode())
  87.             ->append($this->buildEnvironmentNode())
  88.             ->append($this->buildCartManagerNode())
  89.             ->append($this->buildOrderManagerNode())
  90.             ->append($this->buildPricingManagerNode())
  91.             ->append($this->buildPriceSystemsNode())
  92.             ->append($this->buildAvailabilitySystemsNode())
  93.             ->append($this->buildCheckoutManagerNode())
  94.             ->append($this->buildPaymentManagerNode())
  95.             ->append($this->buildIndexServiceNode())
  96.             ->append($this->buildFilterServiceNode())
  97.             ->append($this->buildVoucherServiceNode())
  98.             ->append($this->buildOfferToolNode())
  99.             ->append($this->buildTrackingManagerNode());
  100.         return $treeBuilder;
  101.     }
  102.     private function addRootNodeChildren(ArrayNodeDefinition $rootNode)
  103.     {
  104.         $rootNode
  105.             ->children()
  106.                ->integerNode('decimal_scale')
  107.                     ->info('Default scale used for Decimal objects')
  108.                     ->min(0)
  109.                     ->defaultValue(4)
  110.                 ->end()
  111.             ->end();
  112.     }
  113.     private function buildPimcoreNode(): NodeDefinition
  114.     {
  115.         $builder = new TreeBuilder();
  116.         $pimcore $builder->root('pimcore');
  117.         $pimcore
  118.             ->addDefaultsIfNotSet()
  119.             ->info('Configuration of Pimcore backend menu entries');
  120.         $pimcore
  121.             ->children()
  122.                 ->arrayNode('menu')
  123.                     ->addDefaultsIfNotSet()
  124.                     ->children()
  125.                         ->arrayNode('pricing_rules')
  126.                             ->addDefaultsIfNotSet()
  127.                             ->canBeDisabled()
  128.                             ->info('Enabling/Disabling Pricing Rules menu entry. User specific settings can be done via permissions.')
  129.                         ->end()
  130.                         ->arrayNode('order_list')
  131.                             ->addDefaultsIfNotSet()
  132.                             ->canBeDisabled()
  133.                             ->info('Configuring order list menu - enabling/disabling and defining route of order list to inject custom implementations of order backend.')
  134.                             ->children()
  135.                                 ->scalarNode('route')
  136.                                     ->defaultValue('pimcore_ecommerce_backend_admin-order_list')
  137.                                 ->end()
  138.                                 ->scalarNode('path')
  139.                                     ->defaultNull()
  140.                                 ->end()
  141.                             ->end()
  142.                         ->end()
  143.                     ->end()
  144.                 ->end()
  145.             ->end();
  146.         return $pimcore;
  147.     }
  148.     private function buildFactoryNode(): NodeDefinition
  149.     {
  150.         $builder = new TreeBuilder();
  151.         $factory $builder->root('factory');
  152.         $factory
  153.             ->addDefaultsIfNotSet()
  154.             ->info('Configuration of e-commerce framework factory');
  155.         $factory
  156.             ->children()
  157.                 ->scalarNode('factory_id')
  158.                     ->defaultValue(Factory::class)
  159.                     ->cannotBeEmpty()
  160.                     ->info('Service Id of factory implementation')
  161.                 ->end()
  162.                 ->booleanNode('strict_tenants')
  163.                     ->defaultFalse()
  164.                     ->info('If true the factory will not fall back to the default tenant if a tenant is passed but not existing')
  165.                 ->end()
  166.             ->end();
  167.         return $factory;
  168.     }
  169.     private function buildEnvironmentNode(): NodeDefinition
  170.     {
  171.         $builder = new TreeBuilder();
  172.         $environment $builder->root('environment');
  173.         $environment
  174.             ->addDefaultsIfNotSet()
  175.             ->info('Configuration of environment');
  176.         $environment
  177.             ->children()
  178.                 ->scalarNode('environment_id')
  179.                     ->defaultValue(SessionEnvironment::class)
  180.                 ->end()
  181.                 ->append($this->buildOptionsNode('options'))
  182.             ->end();
  183.         return $environment;
  184.     }
  185.     private function buildCartManagerNode(): NodeDefinition
  186.     {
  187.         $builder = new TreeBuilder();
  188.         $cartManager $builder->root('cart_manager');
  189.         $cartManager
  190.             ->addDefaultsIfNotSet()
  191.             ->info('Settings for cart manager');
  192.         $cartManager
  193.             ->addDefaultsIfNotSet()
  194.             ->children()
  195.                 ->arrayNode('tenants')
  196.                     ->info('Configuration per tenant. If a _defaults key is set, it will be merged into every tenant. It needs to be set in every file. A tenant named "default" is mandatory.')
  197.                     ->example([
  198.                         '_defaults' => [
  199.                             'cart' => [
  200.                                 'factory_id' => 'CartFactory',
  201.                             ],
  202.                         ],
  203.                         'default' => [
  204.                             'cart' => [
  205.                                 'factory_options' => [
  206.                                     'cart_class_name' => 'Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\Cart',
  207.                                 ],
  208.                             ],
  209.                             'price_calculator' => [
  210.                                 'modificators' => [
  211.                                     'shipping' => [
  212.                                         'class' => 'Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\CartPriceModificator\Shipping',
  213.                                         'options' => [
  214.                                             'charge' => '5.90',
  215.                                         ],
  216.                                     ],
  217.                                 ],
  218.                             ],
  219.                         ],
  220.                         'noShipping' => [
  221.                             'price_calculator' => [
  222.                                 'factory_id' => 'PriceCalculatorFactory',
  223.                                 'modificators' => '~',
  224.                             ],
  225.                         ],
  226.                     ])
  227.                     ->useAttributeAsKey('name')
  228.                     ->validate()
  229.                         ->ifTrue(function (array $v) {
  230.                             return !array_key_exists('default'$v);
  231.                         })
  232.                         ->thenInvalid('Cart manager needs at least a default tenant')
  233.                     ->end()
  234.                     ->beforeNormalization()
  235.                     ->always(function ($v) {
  236.                         if (empty($v) || !is_array($v)) {
  237.                             $v = [];
  238.                         }
  239.                         return $this->tenantProcessor->mergeTenantConfig($v);
  240.                     })
  241.                     ->end()
  242.                     ->prototype('array')
  243.                         ->children()
  244.                             ->scalarNode('cart_manager_id')
  245.                                 ->defaultValue(MultiCartManager::class)
  246.                                 ->info('Service id of cart service')
  247.                             ->end()
  248.                             ->arrayNode('cart')
  249.                                 ->addDefaultsIfNotSet()
  250.                                 ->info('Configuration for carts')
  251.                                 ->children()
  252.                                     ->scalarNode('factory_id')
  253.                                         ->cannotBeEmpty()
  254.                                         ->defaultValue(CartFactory::class)
  255.                                         ->info('Service id of cart factory and configuration array')
  256.                                     ->end()
  257.                                     ->append($this->buildOptionsNode('factory_options', [
  258.                                         'cart_class_name' => Cart::class,
  259.                                         'guest_cart_class_name' => SessionCart::class,
  260.                                         'cart_readonly_mode' => AbstractCart::CART_READ_ONLY_MODE_STRICT,
  261.                                     ]))
  262.                                 ->end()
  263.                             ->end()
  264.                             ->arrayNode('price_calculator')
  265.                                 ->addDefaultsIfNotSet()
  266.                                 ->children()
  267.                                     ->scalarNode('factory_id')
  268.                                         ->cannotBeEmpty()
  269.                                         ->defaultValue(CartPriceCalculatorFactory::class)
  270.                                     ->end()
  271.                                     ->append($this->buildOptionsNode(
  272.                                         'factory_options',
  273.                                         [
  274.                                             'class' => CartPriceCalculator::class,
  275.                                         ],
  276.                                         "'class' defines a class name of the price calculator, which the factory instantiates. If you wish to replace or extend price calculation routine shipped with e-commerce framework provide your custom class name here."
  277.                                     ))
  278.                                     ->arrayNode('modificators')
  279.                                         ->info('List price modificators for cart, e.g. for shipping-cost, special discounts, etc. Key is name of modificator.')
  280.                                         ->useAttributeAsKey('name')
  281.                                         ->prototype('array')
  282.                                             ->children()
  283.                                                 ->scalarNode('class')->isRequired()->end()
  284.                                                 ->append($this->buildOptionsNode())
  285.                                             ->end()
  286.                                         ->end()
  287.                                     ->end()
  288.                                 ->end()
  289.                             ->end()
  290.                         ->end()
  291.                     ->end()
  292.                 ->end();
  293.         return $cartManager;
  294.     }
  295.     private function buildOrderManagerNode(): NodeDefinition
  296.     {
  297.         $builder = new TreeBuilder();
  298.         $orderManager $builder->root('order_manager');
  299.         $orderManager
  300.             ->info('Configuration of Order Manager')
  301.             ->addDefaultsIfNotSet();
  302.         $orderManager
  303.             ->addDefaultsIfNotSet()
  304.             ->children()
  305.                 ->arrayNode('tenants')
  306.                     ->info('Configuration per tenant. If a _defaults key is set, it will be merged into every tenant. A tenant named "default" is mandatory.')
  307.                     ->useAttributeAsKey('name')
  308.                     ->validate()
  309.                         ->ifTrue(function (array $v) {
  310.                             return !array_key_exists('default'$v);
  311.                         })
  312.                         ->thenInvalid('Order manager needs at least a default tenant')
  313.                     ->end()
  314.                     ->beforeNormalization()
  315.                     ->always(function ($v) {
  316.                         if (empty($v) || !is_array($v)) {
  317.                             $v = [];
  318.                         }
  319.                         return $this->tenantProcessor->mergeTenantConfig($v);
  320.                     })
  321.                     ->end()
  322.                     ->prototype('array')
  323.                         ->children()
  324.                             ->scalarNode('order_manager_id')
  325.                                 ->info('Service id for order manager implementation')
  326.                                 ->defaultValue(OrderManager::class)
  327.                             ->end()
  328.                             ->arrayNode('options')
  329.                                 ->info('Options for order manager')
  330.                                 ->addDefaultsIfNotSet()
  331.                                 ->children()
  332.                                     ->scalarNode('order_class')
  333.                                         ->info('Pimcore object class for orders')
  334.                                         ->defaultValue('\\Pimcore\\Model\\DataObject\\OnlineShopOrder')
  335.                                         ->cannotBeEmpty()
  336.                                     ->end()
  337.                                     ->scalarNode('order_item_class')
  338.                                         ->info('Pimcore object class for order items')
  339.                                         ->defaultValue('\\Pimcore\\Model\\DataObject\\OnlineShopOrderItem')
  340.                                         ->cannotBeEmpty()
  341.                                     ->end()
  342.                                     ->scalarNode('list_class')
  343.                                         ->info('Class for order listing')
  344.                                         ->defaultValue(Listing::class)
  345.                                         ->cannotBeEmpty()
  346.                                     ->end()
  347.                                     ->scalarNode('list_item_class')
  348.                                         ->info('Class for order item listing')
  349.                                         ->defaultValue(Listing\Item::class)
  350.                                         ->cannotBeEmpty()
  351.                                     ->end()
  352.                                     ->scalarNode('parent_order_folder')
  353.                                         ->info('Default parent folder for new orders')
  354.                                         ->defaultValue('/order/%%Y/%%m/%%d')
  355.                                         ->cannotBeEmpty()
  356.                                     ->end()
  357.                                 ->end()
  358.                             ->end()
  359.                             ->arrayNode('order_agent')
  360.                                 ->addDefaultsIfNotSet()
  361.                                 ->children()
  362.                                     ->scalarNode('factory_id')
  363.                                         ->info('Service id for order agent factory')
  364.                                         ->defaultValue(AgentFactory::class)
  365.                                         ->cannotBeEmpty()
  366.                                     ->end()
  367.                                     ->append($this->buildOptionsNode('factory_options'))
  368.                                 ->end()
  369.                             ->end()
  370.                         ->end()
  371.                     ->end()
  372.                 ->end();
  373.         return $orderManager;
  374.     }
  375.     private function buildPricingManagerNode(): NodeDefinition
  376.     {
  377.         $builder = new TreeBuilder();
  378.         $pricingManager $builder->root('pricing_manager');
  379.         $pricingManager
  380.             ->info('Configuration of Pricing Manager')
  381.             ->addDefaultsIfNotSet();
  382.         $pricingManager
  383.             // support deprecated options at the root level of the pricing_manager
  384.             // values set here will OVERWRITE the value in every tenant, even if the
  385.             // tenant defines the value!
  386.             // TODO remove in Pimcore 7
  387.             ->validate()
  388.                 ->always(function ($v) {
  389.                     $enabled null;
  390.                     if (isset($v['enabled']) && is_bool($v['enabled'])) {
  391.                         $enabled $v['enabled'];
  392.                         unset($v['enabled']);
  393.                     }
  394.                     $pricingManagerId null;
  395.                     if (isset($v['pricing_manager_id'])) {
  396.                         $pricingManagerId $v['pricing_manager_id'];
  397.                         unset($v['pricing_manager_id']);
  398.                     }
  399.                     $pricingManagerOptions null;
  400.                     if (isset($v['pricing_manager_options']) && !empty($v['pricing_manager_options'])) {
  401.                         $pricingManagerOptions $v['pricing_manager_options'];
  402.                         unset($v['pricing_manager_options']);
  403.                     }
  404.                     if (null === $enabled && null === $pricingManagerId && null === $pricingManagerOptions) {
  405.                         return $v;
  406.                     }
  407.                     foreach ($v['tenants'] as $tenant => &$tenantConfig) {
  408.                         if (null !== $enabled) {
  409.                             $tenantConfig['enabled'] = $enabled;
  410.                         }
  411.                         if (null !== $pricingManagerId) {
  412.                             $tenantConfig['pricing_manager_id'] = $pricingManagerId;
  413.                         }
  414.                         if (null !== $pricingManagerOptions) {
  415.                             $tenantConfig['pricing_manager_options'] = array_merge(
  416.                                 $tenantConfig['pricing_manager_options'],
  417.                                 $pricingManagerOptions
  418.                             );
  419.                         }
  420.                     }
  421.                     return $v;
  422.                 })
  423.             ->end()
  424.             ->children()
  425.                 ->booleanNode('enabled')
  426.                     ->setDeprecated('The child node "%node%" at the root level path "%path%" is deprecated. Please migrate to the new tenant structure.')
  427.                 ->end()
  428.                 ->scalarNode('pricing_manager_id')
  429.                     ->setDeprecated('The child node "%node%" at the root level path "%path%" is deprecated. Please migrate to the new tenant structure.')
  430.                 ->end()
  431.                 ->arrayNode('pricing_manager_options')
  432.                     ->children()
  433.                         ->scalarNode('rule_class')
  434.                             ->setDeprecated('The child node "%node%" at the root level path "%path%" is deprecated. Please migrate to the new tenant structure.')
  435.                         ->end()
  436.                         ->scalarNode('price_info_class')
  437.                             ->setDeprecated('The child node "%node%" at the root level path "%path%" is deprecated. Please migrate to the new tenant structure.')
  438.                         ->end()
  439.                         ->scalarNode('environment_class')
  440.                             ->setDeprecated('The child node "%node%" at the root level path "%path%" is deprecated. Please migrate to the new tenant structure.')
  441.                         ->end()
  442.                     ->end()
  443.                 ->end()
  444.                 ->arrayNode('conditions')
  445.                     ->info('Condition mapping from name to used class')
  446.                     ->useAttributeAsKey('name')
  447.                     ->prototype('scalar')
  448.                         ->cannotBeEmpty()
  449.                     ->end()
  450.                 ->end()
  451.                 ->arrayNode('actions')
  452.                     ->info('Action mapping from name to used class')
  453.                     ->useAttributeAsKey('name')
  454.                     ->prototype('scalar')
  455.                         ->cannotBeEmpty()
  456.                     ->end()
  457.                 ->end()
  458.                 ->arrayNode('tenants')
  459.                     ->info('Configuration per tenant. If a _defaults key is set, it will be merged into every tenant. A tenant named "default" is mandatory.')
  460.                     ->useAttributeAsKey('name')
  461.                     ->validate()
  462.                         ->ifTrue(function (array $v) {
  463.                             return !array_key_exists('default'$v);
  464.                         })
  465.                         ->thenInvalid('Pricing manager needs at least a default tenant')
  466.                     ->end()
  467.                     ->beforeNormalization()
  468.                         ->always(function ($v) {
  469.                             if (empty($v) || !is_array($v)) {
  470.                                 $v = [];
  471.                             }
  472.                             return $this->tenantProcessor->mergeTenantConfig($v);
  473.                         })
  474.                     ->end()
  475.                     ->prototype('array')
  476.                         ->canBeDisabled()
  477.                         ->children()
  478.                             ->scalarNode('pricing_manager_id')
  479.                                 ->info('Service id of pricing manager')
  480.                                 ->cannotBeEmpty()
  481.                                 ->defaultValue(PricingManager::class)
  482.                             ->end()
  483.                             ->arrayNode('pricing_manager_options')
  484.                                 ->info('Options for pricing manager')
  485.                                 ->addDefaultsIfNotSet()
  486.                                 ->children()
  487.                                     ->scalarNode('rule_class')
  488.                                         ->cannotBeEmpty()
  489.                                         ->defaultValue(Rule::class)
  490.                                     ->end()
  491.                                     ->scalarNode('price_info_class')
  492.                                         ->cannotBeEmpty()
  493.                                         ->defaultValue(PriceInfo::class)
  494.                                     ->end()
  495.                                     ->scalarNode('environment_class')
  496.                                         ->cannotBeEmpty()
  497.                                         ->defaultValue(Environment::class)
  498.                                     ->end()
  499.                                 ->end()
  500.                             ->end()
  501.                         ->end()
  502.                     ->end()
  503.                 ->end()
  504.             ->end();
  505.         return $pricingManager;
  506.     }
  507.     private function buildPriceSystemsNode(): NodeDefinition
  508.     {
  509.         $builder = new TreeBuilder();
  510.         $priceSystems $builder->root('price_systems');
  511.         $priceSystems
  512.             ->info('Configuration of price systems - key is name of price system.')
  513.             ->useAttributeAsKey('name')
  514.             ->prototype('array')
  515.                 ->beforeNormalization()
  516.                     ->ifString()
  517.                     ->then(function ($v) {
  518.                         return ['id' => $v];
  519.                     })
  520.                 ->end()
  521.                 ->children()
  522.                     ->scalarNode('name')->end()
  523.                     ->scalarNode('id')
  524.                         ->isRequired()
  525.                     ->end()
  526.                 ->end()
  527.             ->end();
  528.         return $priceSystems;
  529.     }
  530.     private function buildAvailabilitySystemsNode(): NodeDefinition
  531.     {
  532.         $builder = new TreeBuilder();
  533.         $availabilitySystems $builder->root('availability_systems');
  534.         $availabilitySystems
  535.             ->useAttributeAsKey('name')
  536.             ->info('Configuration of availability systems - key is name of price system.')
  537.             ->prototype('array')
  538.                 ->beforeNormalization()
  539.                     ->ifString()
  540.                     ->then(function ($v) {
  541.                         return ['id' => $v];
  542.                     })
  543.                 ->end()
  544.                 ->children()
  545.                     ->scalarNode('name')->end()
  546.                     ->scalarNode('id')
  547.                         ->isRequired()
  548.                     ->end()
  549.                 ->end()
  550.             ->end();
  551.         return $availabilitySystems;
  552.     }
  553.     private function buildCheckoutManagerNode(): NodeDefinition
  554.     {
  555.         $builder = new TreeBuilder();
  556.         $checkoutManager $builder->root('checkout_manager');
  557.         $checkoutManager
  558.             ->info('Configuration of checkout manager')
  559.             ->addDefaultsIfNotSet();
  560.         $checkoutManager
  561.             ->children()
  562.                 ->arrayNode('tenants')
  563.                     ->info('Configuration per tenant. If a _defaults key is set, it will be merged into every tenant. A tenant named "default" is mandatory.')
  564.                     ->useAttributeAsKey('name')
  565.                     ->validate()
  566.                         ->ifTrue(function (array $v) {
  567.                             return !array_key_exists('default'$v);
  568.                         })
  569.                         ->thenInvalid('Checkout manager needs at least a default tenant')
  570.                     ->end()
  571.                     ->beforeNormalization()
  572.                         ->always(function ($v) {
  573.                             if (empty($v) || !is_array($v)) {
  574.                                 $v = [];
  575.                             }
  576.                             return $this->tenantProcessor->mergeTenantConfig($v);
  577.                         })
  578.                     ->end()
  579.                     ->prototype('array')
  580.                         ->children()
  581.                             ->scalarNode('factory_id')
  582.                                 ->defaultValue(CheckoutManagerFactory::class)
  583.                                 ->cannotBeEmpty()
  584.                             ->end()
  585.                             ->append($this->buildOptionsNode('factory_options'))
  586.                             ->arrayNode('payment')
  587.                                 ->info('Define payment provider which should be used for payment. Payment providers are defined in payment_manager section.')
  588.                                 ->addDefaultsIfNotSet()
  589.                                 ->children()
  590.                                     ->scalarNode('provider')
  591.                                         ->defaultNull()
  592.                                     ->end()
  593.                                 ->end()
  594.                             ->end()
  595.                             ->arrayNode('commit_order_processor')
  596.                                 ->info('Define used commit order processor')
  597.                                 ->addDefaultsIfNotSet()
  598.                                 ->children()
  599.                                     ->scalarNode('id')
  600.                                         ->defaultValue(CommitOrderProcessor::class)
  601.                                         ->cannotBeEmpty()
  602.                                     ->end()
  603.                                     ->append($this->buildOptionsNode())
  604.                                 ->end()
  605.                             ->end()
  606.                             ->arrayNode('steps')
  607.                                 ->info('Define different checkout steps which need to be committed before commit of order is possible')
  608.                                 ->requiresAtLeastOneElement()
  609.                                 ->useAttributeAsKey('name')
  610.                                 ->prototype('array')
  611.                                     ->children()
  612.                                         ->scalarNode('class')
  613.                                             ->isRequired()
  614.                                         ->end()
  615.                                         ->append($this->buildOptionsNode())
  616.                                     ->end()
  617.                                 ->end()
  618.                             ->end()
  619.                         ->end()
  620.                     ->end()
  621.                 ->end()
  622.             ->end();
  623.         return $checkoutManager;
  624.     }
  625.     private function buildPaymentManagerNode(): NodeDefinition
  626.     {
  627.         $builder = new TreeBuilder();
  628.         $paymentManager $builder->root('payment_manager');
  629.         $paymentManager
  630.             ->info('Configuration of payment manager and payment providers')
  631.             ->addDefaultsIfNotSet();
  632.         $paymentManager
  633.             ->children()
  634.                 ->scalarNode('payment_manager_id')
  635.                     ->cannotBeEmpty()
  636.                     ->defaultValue(PaymentManager::class)
  637.                 ->end()
  638.                 ->arrayNode('providers')
  639.                     ->info('Configuration of payment providers, key is name of provider.')
  640.                     ->useAttributeAsKey('name')
  641.                     ->prototype('array')
  642.                         ->children()
  643.                             ->scalarNode('name')->end()
  644.                             ->scalarNode('provider_id')
  645.                                 ->info('Service id of payment provider implementation')
  646.                                 ->isRequired()
  647.                             ->end()
  648.                             ->scalarNode('profile')
  649.                                 ->info('Currently active profile')
  650.                                 ->isRequired()
  651.                             ->end()
  652.                             ->arrayNode('profiles')
  653.                                 ->info('Available profiles with options')
  654.                                 ->beforeNormalization()
  655.                                     ->always(function ($v) {
  656.                                         if (empty($v) || !is_array($v)) {
  657.                                             $v = [];
  658.                                         }
  659.                                         return $this->tenantProcessor->mergeTenantConfig($v);
  660.                                     })
  661.                                 ->end()
  662.                                 ->useAttributeAsKey('name')
  663.                                 ->prototype('array')
  664.                                     ->useAttributeAsKey('name')
  665.                                     ->prototype('variable')->end()
  666.                                 ->end()
  667.                             ->end()
  668.                         ->end()
  669.                     ->end()
  670.                 ->end()
  671.             ->end();
  672.         return $paymentManager;
  673.     }
  674.     private function buildIndexServiceNode(): NodeDefinition
  675.     {
  676.         $builder = new TreeBuilder();
  677.         $indexService $builder->root('index_service');
  678.         $indexService
  679.             ->addDefaultsIfNotSet()
  680.             ->info('Configuration of index service');
  681.         $indexService
  682.             ->children()
  683.                 ->scalarNode('index_service_id')
  684.                     ->cannotBeEmpty()
  685.                     ->defaultValue(IndexService::class)
  686.                 ->end()
  687.                 // @TODO Pimcore 7 - remove this
  688.                 ->enumNode('worker_mode')
  689.                     ->values([ProductCentricBatchProcessingWorker::WORKER_MODE_PRODUCT_CENTRICProductCentricBatchProcessingWorker::WORKER_MODE_LEGACY])
  690.                     ->cannotBeEmpty()
  691.                     ->setDeprecated('will be removed in Pimcore 7 as then ' ProductCentricBatchProcessingWorker::WORKER_MODE_PRODUCT_CENTRIC ' will be default mode.')
  692.                     ->info('Worker mode for ' ProductCentricBatchProcessingWorker::class . ' workers.')
  693.                     ->defaultValue(ProductCentricBatchProcessingWorker::WORKER_MODE_LEGACY)
  694.                 ->end()
  695.                 ->scalarNode('default_tenant')
  696.                     ->cannotBeEmpty()
  697.                     ->defaultValue('default')
  698.                 ->end()
  699.                 ->arrayNode('tenants')
  700.                     ->info('Configure assortment tenants - at least one tenant has to be configured. If a _defaults key is set, it will be merged into every tenant.')
  701.                     ->useAttributeAsKey('name'false)
  702.                     ->validate()
  703.                         ->always(function (array $v) {
  704.                             // check if all search attributes are defined as attribute
  705.                             foreach ($v as $tenant => $tenantConfig) {
  706.                                 foreach ($tenantConfig['search_attributes'] as $searchAttribute) {
  707.                                     $attributeFound false;
  708.                                     if (isset($tenantConfig['attributes'][$searchAttribute])) {
  709.                                         $attributeFound true;
  710.                                     }
  711.                                     $delimiters = ['.''^'];
  712.                                     foreach ($delimiters as $delimiter) {
  713.                                         if (!$attributeFound && strpos($searchAttribute$delimiter) !== false) {
  714.                                             $fieldNameParts explode($delimiter$searchAttribute);
  715.                                             if (isset($tenantConfig['attributes'][$fieldNameParts[0]])) {
  716.                                                 $attributeFound true;
  717.                                             }
  718.                                         }
  719.                                     }
  720.                                     if (!$attributeFound) {
  721.                                         throw new InvalidConfigurationException(sprintf(
  722.                                             'The search attribute "%s" in product index tenant "%s" is not defined as attribute.',
  723.                                             $searchAttribute,
  724.                                             $tenant
  725.                                         ));
  726.                                     }
  727.                                 }
  728.                             }
  729.                             return $v;
  730.                         })
  731.                     ->end()
  732.                     ->beforeNormalization()
  733.                         ->always(function ($v) {
  734.                             if (empty($v) || !is_array($v)) {
  735.                                 $v = [];
  736.                             }
  737.                             $config $this->tenantProcessor->mergeTenantConfig($v);
  738.                             foreach ($config as $tenant => $tenantConfig) {
  739.                                 if (isset($tenantConfig['placeholders']) && is_array($tenantConfig['placeholders']) && count($tenantConfig['placeholders']) > 0) {
  740.                                     $placeholders $tenantConfig['placeholders'];
  741.                                     // remove placeholders while replacing as we don't want to replace the placeholders
  742.                                     unset($tenantConfig['placeholders']);
  743.                                     $config[$tenant] = $this->placeholderProcessor->mergePlaceholders($tenantConfig$placeholders);
  744.                                     // re-add placeholders
  745.                                     $config[$tenant]['placeholders'] = $placeholders;
  746.                                 }
  747.                                 $config[$tenant]['config_id'] = $config[$tenant]['config_id'] ?? null;
  748.                                 $config[$tenant]['worker_id'] = $config[$tenant]['worker_id'] ?? null;
  749.                                 // if only config or worker is set, try to auto resolve missing config/worker
  750.                                 if (!($config[$tenant]['config_id'] && $config[$tenant]['worker_id'])) {
  751.                                     // nothing is set - set default value
  752.                                     if (!$config[$tenant]['config_id'] && !$config[$tenant]['worker_id']) {
  753.                                         $config[$tenant]['config_id'] = DefaultMysql::class;
  754.                                     }
  755.                                     // resolve default matching part
  756.                                     if ($config[$tenant]['config_id']) {
  757.                                         $config[$tenant]['worker_id'] = $this->indexWorkerConfigMapper->getWorkerForConfig($config[$tenant]['config_id']);
  758.                                     } elseif ($config[$tenant]['worker_id']) {
  759.                                         $config[$tenant]['config_id'] = $this->indexWorkerConfigMapper->getConfigForWorker($config[$tenant]['worker_id']);
  760.                                     }
  761.                                 }
  762.                             }
  763.                             return $config;
  764.                         })
  765.                     ->end()
  766.                     ->prototype('array')
  767.                         ->addDefaultsIfNotSet()
  768.                         ->canBeDisabled()
  769.                         ->children()
  770.                             ->scalarNode('config_id')
  771.                                 ->info('Service id of config implementation')
  772.                                 ->cannotBeEmpty()
  773.                                 ->defaultValue(DefaultMysql::class)
  774.                             ->end()
  775.                             ->append($this->buildOptionsNode('config_options'))
  776.                             ->scalarNode('worker_id')
  777.                                 ->info('Worker id of worker implementation. Can be omitted, then default worker id of configured config is used.')
  778.                                 ->cannotBeEmpty()
  779.                             ->end()
  780.                             ->arrayNode('placeholders')
  781.                                 ->info('Placeholder values in this tenant attributes definition (locale: "%%locale%%") will be replaced by the given placeholder value (eg. "de_AT")')
  782.                                 ->example([
  783.                                     'placeholders' => [
  784.                                         '%%locale%%' => 'de_AT',
  785.                                     ],
  786.                                 ])
  787.                                 ->defaultValue([])
  788.                                 ->beforeNormalization()
  789.                                     ->castToArray()
  790.                                 ->end()
  791.                                 ->prototype('scalar')->end()
  792.                             ->end()
  793.                             ->arrayNode('search_attributes')
  794.                                 ->info('Add columns for general fulltext search index of product list - they must be part of the column configuration below')
  795.                                 ->defaultValue([])
  796.                                 ->beforeNormalization()
  797.                                     ->castToArray()
  798.                                 ->end()
  799.                                 ->prototype('scalar')->end()
  800.                             ->end()
  801.                             ->arrayNode('attributes')
  802.                                 ->info('Attributes definition for product index - key is name of attribute')
  803.                                 ->useAttributeAsKey('name'false)
  804.                                 ->beforeNormalization()
  805.                                     ->always(function ($v) {
  806.                                         if (empty($v) || !is_array($v)) {
  807.                                             $v = [];
  808.                                         }
  809.                                         // make sure the name property is set
  810.                                         foreach (array_keys($v) as $name) {
  811.                                             if (!isset($v[$name]['name'])) {
  812.                                                 $v[$name]['name'] = $name;
  813.                                             }
  814.                                         }
  815.                                         return $v;
  816.                                     })
  817.                                 ->end()
  818.                                 ->prototype('array')
  819.                                     ->beforeNormalization()
  820.                                         ->always(function ($v) {
  821.                                             if (empty($v) || !is_array($v)) {
  822.                                                 return $v;
  823.                                             }
  824.                                             $v $this->remapProperties($v, [
  825.                                                 'fieldname' => 'field_name',
  826.                                                 'filtergroup' => 'filter_group',
  827.                                                 'getter' => 'getter_id',
  828.                                                 'interpreter' => 'interpreter_id',
  829.                                                 'config' => 'options',
  830.                                                 'hideInFieldlistDatatype' => 'hide_in_fieldlist_datatype',
  831.                                             ]);
  832.                                             // this option was never properly supported
  833.                                             // and is ignored
  834.                                             if (isset($v['mapping'])) {
  835.                                                 @trigger_error('The "mapping" config entry on the ecommerce index attribute level is unsupported and will be removed in Pimcore 7. Please set "options.mapping" instead.'E_USER_DEPRECATED);
  836.                                                 unset($v['mapping']);
  837.                                             }
  838.                                             return $v;
  839.                                         })
  840.                                     ->end()
  841.                                     ->children()
  842.                                         ->scalarNode('name')->isRequired()->end()
  843.                                         ->scalarNode('field_name')->defaultNull()->info('Defines object attribute field name, can be omitted if the same like name of index attribute')->end()
  844.                                         ->scalarNode('type')->defaultNull()->info('Type of index attribute (database column or elastic search data type)')->end()
  845.                                         ->scalarNode('locale')->defaultNull()->info('Locale for localized fields, can be omitted if not necessary')->end()
  846.                                         ->scalarNode('filter_group')->defaultNull()->info('Defines filter group for filter definition in filter service')->end()
  847.                                         ->append($this->buildOptionsNode())
  848.                                         ->scalarNode('getter_id')->defaultNull()->info('Service id of getter for this field')->end()
  849.                                         ->append($this->buildOptionsNode('getter_options'))
  850.                                         ->scalarNode('interpreter_id')->defaultNull()->info('Service id of interpreter for this field')->end()
  851.                                         ->append($this->buildOptionsNode('interpreter_options'))
  852.                                         ->append($this->buildOptionsNode('mapping')) // TODO Symfony 3.4 set as deprecated. TODO Pimcore 7 remove option completely.
  853.                                         ->booleanNode('hide_in_fieldlist_datatype')->defaultFalse()->info('Hides field in field list selection data type of filter service - default to false')->end()
  854.                                     ->end()
  855.                                 ->end()
  856.                             ->end()
  857.                         ->end()
  858.                     ->end()
  859.                 ->end()
  860.             ->end();
  861.         return $indexService;
  862.     }
  863.     private function buildFilterServiceNode(): NodeDefinition
  864.     {
  865.         $builder = new TreeBuilder();
  866.         $filterService $builder->root('filter_service');
  867.         $filterService
  868.             ->info('Configuration of filter service')
  869.             ->addDefaultsIfNotSet();
  870.         $filterService
  871.             ->children()
  872.                 ->arrayNode('tenants')
  873.                     ->info('Configuration per tenant. If a _defaults key is set, it will be merged into every tenant.')
  874.                     ->useAttributeAsKey('name'false)
  875.                     ->beforeNormalization()
  876.                         ->always(function ($v) {
  877.                             if (empty($v) || !is_array($v)) {
  878.                                 $v = [];
  879.                             }
  880.                             return $this->tenantProcessor->mergeTenantConfig($v);
  881.                         })
  882.                     ->end()
  883.                     ->prototype('array')
  884.                         ->addDefaultsIfNotSet()
  885.                         ->canBeDisabled()
  886.                         ->children()
  887.                             ->scalarNode('service_id')
  888.                                 ->cannotBeEmpty()
  889.                                 ->defaultValue(FilterService::class)
  890.                             ->end()
  891.                             ->arrayNode('filter_types')
  892.                                 ->info('Assign backend implementations and views to filter type field collections')
  893.                                 ->useAttributeAsKey('name')
  894.                                 ->prototype('array')
  895.                                     ->addDefaultsIfNotSet()
  896.                                     ->beforeNormalization()
  897.                                         ->always(function ($v) {
  898.                                             if (empty($v) || !is_array($v)) {
  899.                                                 return $v;
  900.                                             }
  901.                                             return $this->remapProperties($v, [
  902.                                                 'class' => 'filter_type_id',
  903.                                                 'script' => 'template',
  904.                                             ]);
  905.                                         })
  906.                                     ->end()
  907.                                     ->children()
  908.                                         ->scalarNode('filter_type_id')
  909.                                             ->info('Service id for filter type implementation')
  910.                                             ->isRequired()
  911.                                         ->end()
  912.                                         ->scalarNode('template')
  913.                                             ->info('Default template for filter, can be overwritten in filter definition')
  914.                                             ->isRequired()
  915.                                         ->end()
  916.                                         ->append($this->buildOptionsNode())
  917.                                     ->end()
  918.                                 ->end()
  919.                             ->end()
  920.                         ->end()
  921.                     ->end()
  922.                 ->end()
  923.             ->end();
  924.         return $filterService;
  925.     }
  926.     private function buildVoucherServiceNode(): NodeDefinition
  927.     {
  928.         $builder = new TreeBuilder();
  929.         $voucherService $builder->root('voucher_service');
  930.         $voucherService
  931.             ->info('Configuration of voucher service')
  932.             ->addDefaultsIfNotSet();
  933.         $voucherService
  934.             ->children()
  935.                 ->scalarNode('voucher_service_id')
  936.                     ->info('Service id of voucher service implementation')
  937.                     ->cannotBeEmpty()
  938.                     ->defaultValue(DefaultVoucherService::class)
  939.                 ->end()
  940.                 ->arrayNode('voucher_service_options')
  941.                     ->addDefaultsIfNotSet()
  942.                     ->children()
  943.                         ->integerNode('reservation_minutes_threshold')
  944.                             ->info('Reservations older than x MINUTES get removed by maintenance task')
  945.                             ->defaultValue(5)
  946.                             ->min(0)
  947.                         ->end()
  948.                         ->integerNode('statistics_days_threshold')
  949.                             ->info('Statistics older than x DAYS get removed by maintenance task')
  950.                             ->defaultValue(30)
  951.                             ->min(0)
  952.                         ->end()
  953.                     ->end()
  954.                 ->end()
  955.                 ->arrayNode('token_managers')
  956.                     ->info('Configuration of token managers')
  957.                     ->addDefaultsIfNotSet()
  958.                     ->children()
  959.                         ->scalarNode('factory_id')
  960.                             ->info('Service id of token manager factory')
  961.                             ->cannotBeEmpty()
  962.                             ->defaultValue(TokenManagerFactory::class)
  963.                         ->end()
  964.                         ->arrayNode('mapping')
  965.                             ->info('Mapping for token manager implementations')
  966.                             ->useAttributeAsKey('name')
  967.                             ->prototype('scalar')
  968.                                 ->cannotBeEmpty()
  969.                             ->end()
  970.                         ->end()
  971.                     ->end()
  972.                 ->end()
  973.             ->end();
  974.         return $voucherService;
  975.     }
  976.     private function buildOfferToolNode(): NodeDefinition
  977.     {
  978.         $builder = new TreeBuilder();
  979.         $offerTool $builder->root('offer_tool');
  980.         $offerTool
  981.             ->info('Configuration of offer tool')
  982.             ->addDefaultsIfNotSet();
  983.         $offerTool
  984.             ->children()
  985.                 ->scalarNode('service_id')
  986.                     ->info('Service id for offer tool service')
  987.                     ->defaultValue(DefaultOfferToolService::class)
  988.                     ->cannotBeEmpty()
  989.                 ->end()
  990.                 ->arrayNode('order_storage')
  991.                     ->addDefaultsIfNotSet()
  992.                     ->children()
  993.                         ->scalarNode('offer_class')
  994.                             ->info('Pimcore object class for offers')
  995.                             ->cannotBeEmpty()
  996.                             ->defaultValue(OfferToolOffer::class)
  997.                         ->end()
  998.                         ->scalarNode('offer_item_class')
  999.                             ->info('Pimcore object class for offer items')
  1000.                             ->cannotBeEmpty()
  1001.                             ->defaultValue(OfferToolOfferItem::class)
  1002.                         ->end()
  1003.                         ->scalarNode('parent_folder_path')
  1004.                             ->info('default path for new offers')
  1005.                             ->cannotBeEmpty()
  1006.                             ->defaultValue('/offertool/offers/%%Y/%%m')
  1007.                         ->end()
  1008.                     ->end()
  1009.                 ->end()
  1010.             ->end();
  1011.         return $offerTool;
  1012.     }
  1013.     private function buildTrackingManagerNode(): NodeDefinition
  1014.     {
  1015.         $builder = new TreeBuilder();
  1016.         $trackingManager $builder->root('tracking_manager');
  1017.         $trackingManager
  1018.             ->info('Configuration of Tracking Manager')
  1019.             ->addDefaultsIfNotSet();
  1020.         $trackingManager
  1021.             ->children()
  1022.                 ->scalarNode('tracking_manager_id')
  1023.                     ->info('Service id of tracking manager')
  1024.                     ->defaultValue(TrackingManager::class)
  1025.                 ->end()
  1026.                 ->arrayNode('trackers')
  1027.                     ->info('Enable/Disable trackers and configure them')
  1028.                     ->useAttributeAsKey('name')
  1029.                     ->prototype('array')
  1030.                         ->canBeDisabled()
  1031.                         ->children()
  1032.                             ->scalarNode('id')
  1033.                                 ->info('Service id for tracker')
  1034.                                 ->isRequired()
  1035.                             ->end()
  1036.                             ->append($this->buildOptionsNode())
  1037.                             ->scalarNode('item_builder_id')
  1038.                                 ->info('Service id for item builder for tracker')
  1039.                                 ->defaultValue(TrackingItemBuilder::class)
  1040.                             ->end()
  1041.                             ->arrayNode('tenants')
  1042.                                 ->addDefaultsIfNotSet()
  1043.                                 ->info('List of assortment and checkout tenants where this tracker should be activated for.')
  1044.                                 ->children()
  1045.                                     ->arrayNode('assortment')
  1046.                                         ->info('Add list of assortment tenants where the tracker should be activated for. Empty array means activated for all tenants.')
  1047.                                         ->defaultValue([])
  1048.                                         ->beforeNormalization()
  1049.                                             ->castToArray()
  1050.                                         ->end()
  1051.                                         ->prototype('scalar')->end()
  1052.                                     ->end()
  1053.                                     ->arrayNode('checkout')
  1054.                                         ->info('Add list of checkout tenants where the tracker should be activated for. Empty array means activated for all tenants.')
  1055.                                         ->defaultValue([])
  1056.                                         ->beforeNormalization()
  1057.                                             ->castToArray()
  1058.                                         ->end()
  1059.                                         ->prototype('scalar')->end()
  1060.                                     ->end()
  1061.                                 ->end()
  1062.                             ->end()
  1063.                         ->end()
  1064.                     ->end()
  1065.                 ->end()
  1066.             ->end();
  1067.         return $trackingManager;
  1068.     }
  1069.     private function buildOptionsNode(string $name 'options', array $defaultValue = [], string $documentation null): NodeDefinition
  1070.     {
  1071.         $node = new VariableNodeDefinition($name);
  1072.         if ($documentation) {
  1073.             $node->info($documentation);
  1074.         }
  1075.         $node
  1076.             ->defaultValue($defaultValue)
  1077.             ->treatNullLike([])
  1078.             ->beforeNormalization()
  1079.                ->castToArray()
  1080.             ->end();
  1081.         return $node;
  1082.     }
  1083.     /**
  1084.      * Normalizes properties from old to new names to easy migration
  1085.      *
  1086.      * @param array $data
  1087.      * @param array $map
  1088.      *
  1089.      * @return array
  1090.      */
  1091.     private function remapProperties(array $data, array $map): array
  1092.     {
  1093.         foreach ($map as $old => $new) {
  1094.             if (isset($data[$old]) && !isset($data[$new])) {
  1095.                 $data[$new] = $data[$old];
  1096.                 unset($data[$old]);
  1097.             }
  1098.         }
  1099.         return $data;
  1100.     }
  1101. }