vendor/sonata-project/admin-bundle/src/DependencyInjection/Compiler/AddDependencyCallsCompilerPass.php line 122

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of the Sonata Project package.
  5.  *
  6.  * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
  7.  *
  8.  * For the full copyright and license information, please view the LICENSE
  9.  * file that was distributed with this source code.
  10.  */
  11. namespace Sonata\AdminBundle\DependencyInjection\Compiler;
  12. use Sonata\AdminBundle\Admin\Pool;
  13. use Sonata\AdminBundle\Datagrid\Pager;
  14. use Sonata\AdminBundle\DependencyInjection\Admin\TaggedAdminInterface;
  15. use Sonata\AdminBundle\Templating\MutableTemplateRegistry;
  16. use Symfony\Component\DependencyInjection\ChildDefinition;
  17. use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
  18. use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
  19. use Symfony\Component\DependencyInjection\ContainerBuilder;
  20. use Symfony\Component\DependencyInjection\Definition;
  21. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  22. use Symfony\Component\DependencyInjection\Reference;
  23. use Symfony\Component\String\UnicodeString;
  24. /**
  25.  * Add all dependencies to the Admin class, this avoids writing too many lines
  26.  * in the configuration files.
  27.  *
  28.  * @internal
  29.  *
  30.  * @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
  31.  */
  32. final class AddDependencyCallsCompilerPass implements CompilerPassInterface
  33. {
  34.     public function process(ContainerBuilder $container): void
  35.     {
  36.         if (!$container->has('sonata.admin.pool')) {
  37.             return;
  38.         }
  39.         // check if translator service exist
  40.         if (!$container->has('translator')) {
  41.             throw new \RuntimeException('The "translator" service is not yet enabled.
  42.                 It\'s required by SonataAdmin to display all labels properly.
  43.                 To learn how to enable the translator service please visit:
  44.                 http://symfony.com/doc/current/translation.html#configuration
  45.              ');
  46.         }
  47.         $parameterBag $container->getParameterBag();
  48.         $groupDefaults $admins $adminServices $classes = [];
  49.         $pool $container->getDefinition('sonata.admin.pool');
  50.         $defaultController $container->getParameter('sonata.admin.configuration.default_controller');
  51.         \assert(\is_string($defaultController));
  52.         $defaultGroup $container->getParameter('sonata.admin.configuration.default_group');
  53.         \assert(\is_string($defaultGroup));
  54.         // NEXT_MAJOR: Remove this variable.
  55.         $defaultLabelCatalogue $container->getParameter('sonata.admin.configuration.default_label_catalogue');
  56.         \assert(\is_string($defaultLabelCatalogue));
  57.         // NEXT_MAJOR: Remove the fallback.
  58.         $defaultTranslationDomain $container->getParameter('sonata.admin.configuration.default_translation_domain') ?? $defaultLabelCatalogue;
  59.         \assert(\is_string($defaultTranslationDomain));
  60.         $defaultIcon $container->getParameter('sonata.admin.configuration.default_icon');
  61.         \assert(\is_string($defaultIcon));
  62.         $defaultValues = [
  63.             'group' => $defaultGroup,
  64.             'translation_domain' => $defaultTranslationDomain,
  65.             'label_catalogue' => $defaultLabelCatalogue// NEXT_MAJOR: Remove this line.
  66.             'icon' => $defaultIcon,
  67.         ];
  68.         foreach ($container->findTaggedServiceIds(TaggedAdminInterface::ADMIN_TAG) as $id => $tags) {
  69.             if (\count($tags) > 1) {
  70.                 // NEXT_MAJOR: Remove deprecation error with the exception below.
  71.                 @trigger_error(sprintf(
  72.                     'Found multiple sonata.admin tags in service %s. Tagging a service with sonata.admin more
  73.                     than once is not supported, and will result in a RuntimeException in 5.0.',
  74.                     $id
  75.                 ), \E_USER_DEPRECATED);
  76.                 // NEXT_MAJOR: Enable this exception.
  77.                 // throw new \RuntimeException(sprintf(
  78.                 //    'Found multiple sonata.admin tags in service %s. Tagging a service with sonata.admin more
  79.                 //    than once is not supported. Consider defining multiple services with different sonata.admin tag
  80.                 //    parameters if this is really needed.',
  81.                 //    $id
  82.                 // ));
  83.             }
  84.             foreach ($tags as $attributes) {
  85.                 $code $attributes['code'] ?? $id;
  86.                 $adminServices[$code] = new Reference($id);
  87.                 $definition $container->getDefinition($id);
  88.                 $parentDefinition null;
  89.                 if ($definition instanceof ChildDefinition) {
  90.                     $parentDefinition $container->getDefinition($definition->getParent());
  91.                 }
  92.                 // NEXT_MAJOR: Remove the following code.
  93.                 if (!isset($attributes['model_class'])) {
  94.                     // Since the model_class attribute will be mandatory we're assuming that
  95.                     // - if it's used the new syntax is used, so we don't need to replace the arguments
  96.                     // - if it's not used, the old syntax is used, so we still need to
  97.                     $this->replaceDefaultArguments([
  98.                         => $code,
  99.                         => $defaultController,
  100.                     ], $definition$parentDefinition);
  101.                 }
  102.                 $definition->setMethodCalls(array_merge(
  103.                     $this->getDefaultMethodCalls($container$id$attributes),
  104.                     $definition->getMethodCalls()
  105.                 ));
  106.                 $this->fixTemplates($id$container$definition);
  107.                 $arguments null !== $parentDefinition ?
  108.                     array_merge($parentDefinition->getArguments(), $definition->getArguments()) :
  109.                     $definition->getArguments();
  110.                 $admins[] = $code;
  111.                 // NEXT_MAJOR: Remove the fallback to $arguments[1].
  112.                 $modelClass $attributes['model_class'] ?? $arguments[1];
  113.                 if (!isset($classes[$modelClass])) {
  114.                     $classes[$modelClass] = [];
  115.                 }
  116.                 $default = (bool) (isset($attributes['default']) ? $parameterBag->resolveValue($attributes['default']) : false);
  117.                 if ($default) {
  118.                     if (isset($classes[$modelClass][Pool::DEFAULT_ADMIN_KEY])) {
  119.                         throw new \RuntimeException(sprintf(
  120.                             'The class %s has two admins %s and %s with the "default" attribute set to true. Only one is allowed.',
  121.                             $modelClass,
  122.                             $classes[$modelClass][Pool::DEFAULT_ADMIN_KEY],
  123.                             $code
  124.                         ));
  125.                     }
  126.                     $classes[$modelClass][Pool::DEFAULT_ADMIN_KEY] = $code;
  127.                 } else {
  128.                     $classes[$modelClass][] = $code;
  129.                 }
  130.                 $showInDashboard = (bool) (isset($attributes['show_in_dashboard']) ? $parameterBag->resolveValue($attributes['show_in_dashboard']) : true);
  131.                 if (!$showInDashboard) {
  132.                     continue;
  133.                 }
  134.                 $resolvedGroupName = isset($attributes['group']) ?
  135.                     $parameterBag->resolveValue($attributes['group']) :
  136.                     $defaultValues['group'];
  137.                 \assert(\is_string($resolvedGroupName));
  138.                 // NEXT_MAJOR: Remove this deprecation and the $labelCatalogue variable.
  139.                 if (isset($attributes['label_catalogue'])) {
  140.                     @trigger_error(
  141.                         'The "label_catalogue" attribute is deprecated'
  142.                         .' since sonata-project/admin-bundle 4.9 and will throw an error in 5.0.',
  143.                         \E_USER_DEPRECATED
  144.                     );
  145.                 }
  146.                 $labelCatalogue $attributes['label_catalogue'] ?? $defaultValues['label_catalogue'];
  147.                 // NEXT_MAJOR: Remove the `label_catalogue` fallback.
  148.                 $groupTranslationDomain $attributes['translation_domain'] ?? $attributes['label_catalogue'] ?? $defaultValues['translation_domain'];
  149.                 $icon $attributes['icon'] ?? $defaultValues['icon'];
  150.                 $onTop $attributes['on_top'] ?? false;
  151.                 $keepOpen $attributes['keep_open'] ?? false;
  152.                 if (!isset($groupDefaults[$resolvedGroupName])) {
  153.                     $groupDefaults[$resolvedGroupName] = [
  154.                         'label' => $resolvedGroupName,
  155.                         'translation_domain' => $groupTranslationDomain,
  156.                         'label_catalogue' => $labelCatalogue// NEXT_MAJOR: Remove this line.
  157.                         'icon' => $icon,
  158.                         'items' => [],
  159.                         'roles' => [],
  160.                         'on_top' => false,
  161.                         'keep_open' => false,
  162.                     ];
  163.                 }
  164.                 $groupDefaults[$resolvedGroupName]['priority'] = max($groupDefaults[$resolvedGroupName]['priority'] ?? 0$attributes['priority'] ?? 0);
  165.                 $groupDefaults[$resolvedGroupName]['items'][] = [
  166.                     'admin' => $code,
  167.                     'label' => $attributes['label'] ?? ''// NEXT_MAJOR: Remove this line.
  168.                     'route' => ''// NEXT_MAJOR: Remove this line.
  169.                     'route_params' => [],
  170.                     'route_absolute' => false,
  171.                     'priority' => $attributes['priority'] ?? 0,
  172.                 ];
  173.                 if (isset($groupDefaults[$resolvedGroupName]['on_top']) && true === $groupDefaults[$resolvedGroupName]['on_top']
  174.                     || true === $onTop && (\count($groupDefaults[$resolvedGroupName]['items']) > 1)) {
  175.                     throw new \RuntimeException('You can\'t use "on_top" option with multiple same name groups.');
  176.                 }
  177.                 $groupDefaults[$resolvedGroupName]['on_top'] = $onTop;
  178.                 $groupDefaults[$resolvedGroupName]['keep_open'] = $keepOpen;
  179.             }
  180.         }
  181.         $dashboardGroupsSettings $container->getParameter('sonata.admin.configuration.dashboard_groups');
  182.         \assert(\is_array($dashboardGroupsSettings));
  183.         $sortAdmins $container->getParameter('sonata.admin.configuration.sort_admins');
  184.         \assert(\is_bool($sortAdmins));
  185.         $sortAdminsByPriority true;
  186.         if ([] !== $dashboardGroupsSettings) {
  187.             $groups $dashboardGroupsSettings;
  188.             foreach ($dashboardGroupsSettings as $groupName => $group) {
  189.                 $resolvedGroupName $parameterBag->resolveValue($groupName);
  190.                 \assert(\is_string($resolvedGroupName));
  191.                 if (!isset($groupDefaults[$resolvedGroupName])) {
  192.                     $groupDefaults[$resolvedGroupName] = [
  193.                         'items' => [],
  194.                         'label' => $resolvedGroupName,
  195.                         'translation_domain' => $defaultValues['translation_domain'],
  196.                         'label_catalogue' => $defaultValues['label_catalogue'], // NEXT_MAJOR: Remove this line.
  197.                         'icon' => $defaultValues['icon'],
  198.                         'roles' => [],
  199.                         'on_top' => false,
  200.                         'keep_open' => false,
  201.                     ];
  202.                 }
  203.                 if (!isset($group['items']) || [] === $group['items']) {
  204.                     $groups[$resolvedGroupName]['items'] = $groupDefaults[$resolvedGroupName]['items'];
  205.                 } else {
  206.                     $sortAdminsByPriority false;
  207.                 }
  208.                 if (!isset($group['label']) || '' === $group['label']) {
  209.                     $groups[$resolvedGroupName]['label'] = $groupDefaults[$resolvedGroupName]['label'];
  210.                 }
  211.                 if (!isset($group['translation_domain']) || '' === $group['translation_domain']) {
  212.                     $groups[$resolvedGroupName]['translation_domain'] = $groupDefaults[$resolvedGroupName]['translation_domain'];
  213.                 }
  214.                 // NEXT_MAJOR: Remove the whole if/else.
  215.                 if (!isset($group['label_catalogue']) || '' === $group['label_catalogue']) {
  216.                     $groups[$resolvedGroupName]['label_catalogue'] = $groupDefaults[$resolvedGroupName]['label_catalogue'];
  217.                 } elseif (!isset($group['translation_domain']) || '' === $group['translation_domain']) {
  218.                     // BC-layer if label_catalogue is provided.
  219.                     $groups[$resolvedGroupName]['translation_domain'] = $group['label_catalogue'];
  220.                 }
  221.                 if (!isset($group['icon']) || '' === $group['icon']) {
  222.                     $groups[$resolvedGroupName]['icon'] = $groupDefaults[$resolvedGroupName]['icon'];
  223.                 }
  224.                 if (!isset($group['roles']) || [] === $group['roles']) {
  225.                     $groups[$resolvedGroupName]['roles'] = $groupDefaults[$resolvedGroupName]['roles'];
  226.                 }
  227.                 if (
  228.                     isset($groups[$resolvedGroupName]['on_top'])
  229.                     && true === ($group['on_top'] ?? false)
  230.                     && \count($groups[$resolvedGroupName]['items']) > 1
  231.                 ) {
  232.                     throw new \RuntimeException('You can\'t use "on_top" option with multiple same name groups.');
  233.                 }
  234.                 if (!isset($group['on_top'])) {
  235.                     $groups[$resolvedGroupName]['on_top'] = $groupDefaults[$resolvedGroupName]['on_top'];
  236.                 }
  237.                 if (!isset($group['keep_open'])) {
  238.                     $groups[$resolvedGroupName]['keep_open'] = $groupDefaults[$resolvedGroupName]['keep_open'];
  239.                 }
  240.             }
  241.         } elseif ($sortAdmins) {
  242.             $groups $groupDefaults;
  243.             $elementSort = static function (array &$element): void {
  244.                 usort(
  245.                     $element['items'],
  246.                     static function (array $a, array $b): int {
  247.                         $labelA = isset($a['label']) && '' !== $a['label'] ? $a['label'] : $a['admin'];
  248.                         $labelB = isset($b['label']) && '' !== $b['label'] ? $b['label'] : $b['admin'];
  249.                         return $labelA <=> $labelB;
  250.                     }
  251.                 );
  252.             };
  253.             /*
  254.              * 1) sort the groups by their index
  255.              * 2) sort the elements within each group by label/admin
  256.              */
  257.             ksort($groups);
  258.             array_walk($groups$elementSort);
  259.             $sortAdminsByPriority false;
  260.         } else {
  261.             $groups $groupDefaults;
  262.             uasort($groups, static fn (array $a, array $b): int => $b['priority'] <=> $a['priority']);
  263.         }
  264.         if ($sortAdminsByPriority) {
  265.             $elementSort = static function (array &$element): void {
  266.                 usort(
  267.                     $element['items'],
  268.                     static fn (array $a, array $b): int => ($b['priority'] ?? 0) <=> ($a['priority'] ?? 0)
  269.                 );
  270.             };
  271.             array_walk($groups$elementSort);
  272.         }
  273.         $pool->replaceArgument(0ServiceLocatorTagPass::register($container$adminServices));
  274.         $pool->replaceArgument(1$admins);
  275.         $pool->replaceArgument(2$groups);
  276.         $pool->replaceArgument(3$classes);
  277.     }
  278.     /**
  279.      * Apply the default values required by the AdminInterface to the Admin service definition.
  280.      *
  281.      * @param array<string, mixed> $attributes
  282.      *
  283.      * @return array<array{string, array<mixed>}>
  284.      */
  285.     private function getDefaultMethodCalls(ContainerBuilder $containerstring $serviceId, array $attributes = []): array
  286.     {
  287.         $definition $container->getDefinition($serviceId);
  288.         $methodCalls = [];
  289.         $definition->setShared(false);
  290.         $managerType $attributes['manager_type'] ?? null;
  291.         if (!\is_string($managerType)) {
  292.             throw new InvalidArgumentException(sprintf('Missing tag information "manager_type" on service "%s".'$serviceId));
  293.         }
  294.         $overwriteAdminConfiguration $container->getParameter('sonata.admin.configuration.default_admin_services');
  295.         \assert(\is_array($overwriteAdminConfiguration));
  296.         $defaultAddServices = [
  297.             'model_manager' => sprintf('sonata.admin.manager.%s'$managerType),
  298.             'data_source' => sprintf('sonata.admin.data_source.%s'$managerType),
  299.             'field_description_factory' => sprintf('sonata.admin.field_description_factory.%s'$managerType),
  300.             'form_contractor' => sprintf('sonata.admin.builder.%s_form'$managerType),
  301.             'show_builder' => sprintf('sonata.admin.builder.%s_show'$managerType),
  302.             'list_builder' => sprintf('sonata.admin.builder.%s_list'$managerType),
  303.             'datagrid_builder' => sprintf('sonata.admin.builder.%s_datagrid'$managerType),
  304.             'translator' => 'translator',
  305.             'configuration_pool' => 'sonata.admin.pool',
  306.             'route_generator' => 'sonata.admin.route.default_generator',
  307.             'security_handler' => 'sonata.admin.security.handler',
  308.             'menu_factory' => 'knp_menu.factory',
  309.             'route_builder' => 'sonata.admin.route.path_info',
  310.             'label_translator_strategy' => 'sonata.admin.label.strategy.native',
  311.         ];
  312.         $methodCalls[] = ['setManagerType', [$managerType]];
  313.         foreach ($defaultAddServices as $attr => $addServiceId) {
  314.             $method $this->generateSetterMethodName($attr);
  315.             if ($definition->hasMethodCall($method)) {
  316.                 continue;
  317.             }
  318.             $args = [new Reference($attributes[$attr] ?? $overwriteAdminConfiguration[$attr] ?? $addServiceId)];
  319.             if ('translator' === $attr) {
  320.                 $args[] = false;
  321.             }
  322.             $methodCalls[] = [$method$args];
  323.         }
  324.         $defaultController $container->getParameter('sonata.admin.configuration.default_controller');
  325.         \assert(\is_string($defaultController));
  326.         $modelClass $attributes['model_class'] ?? null;
  327.         if (null === $modelClass) {
  328.             @trigger_error(
  329.                 'Not setting the "model_class" attribute is deprecated'
  330.                 .' since sonata-project/admin-bundle 4.8 and will throw an error in 5.0.',
  331.                 \E_USER_DEPRECATED
  332.             );
  333.         // NEXT_MAJOR: Uncomment the exception instead of the deprecation.
  334.         // throw new InvalidArgumentException(sprintf('Missing tag information "model_class" on service "%s".', $serviceId));
  335.         } else {
  336.             $methodCalls[] = ['setModelClass', [$modelClass]];
  337.             $controller $attributes['controller'] ?? $defaultController;
  338.             $methodCalls[] = ['setBaseControllerName', [$controller]];
  339.             $code $attributes['code'] ?? $serviceId;
  340.             $methodCalls[] = ['setCode', [$code]];
  341.         }
  342.         $pagerType $overwriteAdminConfiguration['pager_type'] ?? $attributes['pager_type'] ?? Pager::TYPE_DEFAULT;
  343.         $methodCalls[] = ['setPagerType', [$pagerType]];
  344.         $label $attributes['label'] ?? null;
  345.         $methodCalls[] = ['setLabel', [$label]];
  346.         // NEXT_MAJOR: Remove the fallback.
  347.         $defaultTranslationDomain $container->getParameter('sonata.admin.configuration.default_translation_domain') ?? 'messages';
  348.         \assert(\is_string($defaultTranslationDomain));
  349.         $translationDomain $attributes['translation_domain'] ?? $defaultTranslationDomain;
  350.         $methodCalls[] = ['setTranslationDomain', [$translationDomain]];
  351.         $persistFilters $attributes['persist_filters']
  352.             ?? $container->getParameter('sonata.admin.configuration.filters.persist');
  353.         \assert(\is_bool($persistFilters));
  354.         $filtersPersister $attributes['filter_persister']
  355.             ?? $container->getParameter('sonata.admin.configuration.filters.persister');
  356.         \assert(\is_string($filtersPersister));
  357.         // configure filters persistence, if configured to
  358.         if ($persistFilters) {
  359.             $methodCalls[] = ['setFilterPersister', [new Reference($filtersPersister)]];
  360.         }
  361.         $showMosaicButton $attributes['show_mosaic_button']
  362.             ?? $container->getParameter('sonata.admin.configuration.show.mosaic.button');
  363.         \assert(\is_bool($showMosaicButton));
  364.         $listModes TaggedAdminInterface::DEFAULT_LIST_MODES;
  365.         if (!$showMosaicButton) {
  366.             unset($listModes['mosaic']);
  367.         }
  368.         $methodCalls[] = ['setListModes', [$listModes]];
  369.         if ($container->hasParameter('sonata.admin.configuration.security.information') && !$definition->hasMethodCall('setSecurityInformation')) {
  370.             $methodCalls[] = ['setSecurityInformation', ['%sonata.admin.configuration.security.information%']];
  371.         }
  372.         $defaultTemplates $container->getParameter('sonata.admin.configuration.templates');
  373.         \assert(\is_array($defaultTemplates));
  374.         if (!$definition->hasMethodCall('setFormTheme')) {
  375.             $formTheme $defaultTemplates['form_theme'] ?? [];
  376.             $methodCalls[] = ['setFormTheme', [$formTheme]];
  377.         }
  378.         if (!$definition->hasMethodCall('setFilterTheme')) {
  379.             $filterTheme $defaultTemplates['filter_theme'] ?? [];
  380.             $methodCalls[] = ['setFilterTheme', [$filterTheme]];
  381.         }
  382.         return $methodCalls;
  383.     }
  384.     private function fixTemplates(
  385.         string $serviceId,
  386.         ContainerBuilder $container,
  387.         Definition $definition
  388.     ): void {
  389.         $definedTemplates $container->getParameter('sonata.admin.configuration.templates');
  390.         \assert(\is_array($definedTemplates));
  391.         $methods = [];
  392.         $pos 0;
  393.         foreach ($definition->getMethodCalls() as [$method$args]) {
  394.             if ('setTemplates' === $method) {
  395.                 $definedTemplates array_merge($definedTemplates$args[0]);
  396.                 continue;
  397.             }
  398.             if ('setTemplate' === $method) {
  399.                 $definedTemplates[$args[0]] = $args[1];
  400.                 continue;
  401.             }
  402.             // set template for simple pager if it is not already overwritten
  403.             if ('setPagerType' === $method
  404.                 && Pager::TYPE_SIMPLE === $args[0]
  405.                 && (
  406.                     !isset($definedTemplates['pager_results'])
  407.                     || '@SonataAdmin/Pager/results.html.twig' === $definedTemplates['pager_results']
  408.                 )
  409.             ) {
  410.                 $definedTemplates['pager_results'] = '@SonataAdmin/Pager/simple_pager_results.html.twig';
  411.             }
  412.             $methods[$pos] = [$method$args];
  413.             ++$pos;
  414.         }
  415.         $definition->setMethodCalls($methods);
  416.         $templateRegistryId sprintf('%s.template_registry'$serviceId);
  417.         $templateRegistryDefinition $container
  418.             ->register($templateRegistryIdMutableTemplateRegistry::class)
  419.             ->addTag('sonata.admin.template_registry')
  420.             ->setPublic(true); // Temporary fix until we can support service locators
  421.         if ($container->getParameter('sonata.admin.configuration.templates') !== $definedTemplates) {
  422.             $templateRegistryDefinition->addArgument($definedTemplates);
  423.         } else {
  424.             $templateRegistryDefinition->addArgument('%sonata.admin.configuration.templates%');
  425.         }
  426.         $definition->addMethodCall('setTemplateRegistry', [new Reference($templateRegistryId)]);
  427.     }
  428.     /**
  429.      * NEXT_MAJOR: Remove this method.
  430.      *
  431.      * Replace the empty arguments required by the Admin service definition.
  432.      *
  433.      * @param string[] $defaultArguments
  434.      */
  435.     private function replaceDefaultArguments(
  436.         array $defaultArguments,
  437.         Definition $definition,
  438.         ?Definition $parentDefinition null
  439.     ): void {
  440.         $arguments $definition->getArguments();
  441.         $parentArguments null !== $parentDefinition $parentDefinition->getArguments() : [];
  442.         foreach ($defaultArguments as $index => $value) {
  443.             $declaredInParent null !== $parentDefinition && \array_key_exists($index$parentArguments);
  444.             $argumentValue $declaredInParent $parentArguments[$index] : $arguments[$index] ?? null;
  445.             if (null === $argumentValue || '' === $argumentValue) {
  446.                 $arguments[$declaredInParent sprintf('index_%s'$index) : $index] = $value;
  447.             }
  448.         }
  449.         $definition->setArguments($arguments);
  450.     }
  451.     private function generateSetterMethodName(string $key): string
  452.     {
  453.         return 'set'.(new UnicodeString($key))->camel()->title(true)->toString();
  454.     }
  455. }