Completed
Push — 3.x ( 799626...732596 )
by Grégoire
04:11
created

AddDependencyCallsCompilerPass::fixTemplates()   B

Complexity

Conditions 9
Paths 10

Size

Total Lines 56
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 56
rs 7.1584
c 0
b 0
f 0
cc 9
eloc 35
nc 10
nop 4

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the Sonata Project package.
5
 *
6
 * (c) Thomas Rabaix <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sonata\AdminBundle\DependencyInjection\Compiler;
13
14
use Doctrine\Common\Inflector\Inflector;
15
use Sonata\AdminBundle\Datagrid\Pager;
16
use Sonata\AdminBundle\Templating\TemplateRegistry;
17
use Symfony\Component\DependencyInjection\ChildDefinition;
18
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
19
use Symfony\Component\DependencyInjection\ContainerBuilder;
20
use Symfony\Component\DependencyInjection\Definition;
21
use Symfony\Component\DependencyInjection\DefinitionDecorator;
22
use Symfony\Component\DependencyInjection\Reference;
23
24
/**
25
 * Add all dependencies to the Admin class, this avoid to write too many lines
26
 * in the configuration files.
27
 *
28
 * @author Thomas Rabaix <[email protected]>
29
 */
30
class AddDependencyCallsCompilerPass implements CompilerPassInterface
31
{
32
    public function process(ContainerBuilder $container)
33
    {
34
        // check if translator service exist
35
        if (!$container->has('translator')) {
36
            throw new \RuntimeException('The "translator" service is not yet enabled.
37
                It\'s required by SonataAdmin to display all labels properly.
38
39
                To learn how to enable the translator service please visit:
40
                http://symfony.com/doc/current/translation.html#configuration
41
             ');
42
        }
43
44
        $parameterBag = $container->getParameterBag();
45
        $groupDefaults = $admins = $classes = [];
46
47
        $pool = $container->getDefinition('sonata.admin.pool');
48
49
        foreach ($container->findTaggedServiceIds('sonata.admin') as $id => $tags) {
50
            foreach ($tags as $attributes) {
51
                $definition = $container->getDefinition($id);
52
                $parentDefinition = null;
53
54
                // Temporary fix until we can support service locators
55
                $definition->setPublic(true);
56
57
                // NEXT_MAJOR: Remove check for DefinitionDecorator instance when dropping Symfony <3.3 support
58
                if ($definition instanceof ChildDefinition ||
59
                    (!class_exists(ChildDefinition::class) && $definition instanceof DefinitionDecorator)) {
60
                    $parentDefinition = $container->getDefinition($definition->getParent());
0 ignored issues
show
Bug introduced by
The method getParent does only exist in Symfony\Component\Depend...jection\ChildDefinition, but not in Symfony\Component\Depend...ion\DefinitionDecorator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
61
                }
62
63
                $this->replaceDefaultArguments([
64
                    0 => $id,
65
                    2 => 'SonataAdminBundle:CRUD',
66
                ], $definition, $parentDefinition);
67
                $this->applyConfigurationFromAttribute($definition, $attributes);
68
                $this->applyDefaults($container, $id, $attributes);
69
70
                $arguments = $parentDefinition ?
71
                    array_merge($parentDefinition->getArguments(), $definition->getArguments()) :
72
                    $definition->getArguments();
73
74
                $admins[] = $id;
75
76
                if (!isset($classes[$arguments[1]])) {
77
                    $classes[$arguments[1]] = [];
78
                }
79
80
                $classes[$arguments[1]][] = $id;
81
82
                $showInDashboard = (bool) (isset($attributes['show_in_dashboard']) ? $parameterBag->resolveValue($attributes['show_in_dashboard']) : true);
83
                if (!$showInDashboard) {
84
                    continue;
85
                }
86
87
                $resolvedGroupName = isset($attributes['group']) ? $parameterBag->resolveValue($attributes['group']) : 'default';
88
                $labelCatalogue = isset($attributes['label_catalogue']) ? $attributes['label_catalogue'] : 'SonataAdminBundle';
89
                $icon = isset($attributes['icon']) ? $attributes['icon'] : '<i class="fa fa-folder"></i>';
90
                $onTop = isset($attributes['on_top']) ? $attributes['on_top'] : false;
91
                $keepOpen = isset($attributes['keep_open']) ? $attributes['keep_open'] : false;
92
93
                if (!isset($groupDefaults[$resolvedGroupName])) {
94
                    $groupDefaults[$resolvedGroupName] = [
95
                        'label' => $resolvedGroupName,
96
                        'label_catalogue' => $labelCatalogue,
97
                        'icon' => $icon,
98
                        'roles' => [],
99
                        'on_top' => false,
100
                        'keep_open' => false,
101
                    ];
102
                }
103
104
                $groupDefaults[$resolvedGroupName]['items'][] = [
105
                    'admin' => $id,
106
                    'label' => !empty($attributes['label']) ? $attributes['label'] : '',
107
                    'route' => '',
108
                    'route_params' => [],
109
                    'route_absolute' => false,
110
                ];
111
112
                if (isset($groupDefaults[$resolvedGroupName]['on_top']) && $groupDefaults[$resolvedGroupName]['on_top']
113
                    || $onTop && (count($groupDefaults[$resolvedGroupName]['items']) > 1)) {
114
                    throw new \RuntimeException('You can\'t use "on_top" option with multiple same name groups.');
115
                }
116
                $groupDefaults[$resolvedGroupName]['on_top'] = $onTop;
117
118
                $groupDefaults[$resolvedGroupName]['keep_open'] = $keepOpen;
119
            }
120
        }
121
122
        $dashboardGroupsSettings = $container->getParameter('sonata.admin.configuration.dashboard_groups');
123
        if (!empty($dashboardGroupsSettings)) {
124
            $groups = $dashboardGroupsSettings;
125
126
            foreach ($dashboardGroupsSettings as $groupName => $group) {
127
                $resolvedGroupName = $parameterBag->resolveValue($groupName);
128
                if (!isset($groupDefaults[$resolvedGroupName])) {
129
                    $groupDefaults[$resolvedGroupName] = [
130
                        'items' => [],
131
                        'label' => $resolvedGroupName,
132
                        'roles' => [],
133
                        'on_top' => false,
134
                        'keep_open' => false,
135
                    ];
136
                }
137
138
                if (empty($group['items'])) {
139
                    $groups[$resolvedGroupName]['items'] = $groupDefaults[$resolvedGroupName]['items'];
140
                }
141
142
                if (empty($group['label'])) {
143
                    $groups[$resolvedGroupName]['label'] = $groupDefaults[$resolvedGroupName]['label'];
144
                }
145
146
                if (empty($group['label_catalogue'])) {
147
                    $groups[$resolvedGroupName]['label_catalogue'] = 'SonataAdminBundle';
148
                }
149
150
                if (empty($group['icon'])) {
151
                    $groups[$resolvedGroupName]['icon'] = $groupDefaults[$resolvedGroupName]['icon'];
152
                }
153
154
                if (!empty($group['item_adds'])) {
155
                    $groups[$resolvedGroupName]['items'] = array_merge($groups[$resolvedGroupName]['items'], $group['item_adds']);
156
                }
157
158
                if (empty($group['roles'])) {
159
                    $groups[$resolvedGroupName]['roles'] = $groupDefaults[$resolvedGroupName]['roles'];
160
                }
161
162
                if (isset($groups[$resolvedGroupName]['on_top']) && !empty($group['on_top']) && $group['on_top']
163
                    && (count($groups[$resolvedGroupName]['items']) > 1)) {
164
                    throw new \RuntimeException('You can\'t use "on_top" option with multiple same name groups.');
165
                }
166
                if (empty($group['on_top'])) {
167
                    $groups[$resolvedGroupName]['on_top'] = $groupDefaults[$resolvedGroupName]['on_top'];
168
                }
169
170
                if (empty($group['keep_open'])) {
171
                    $groups[$resolvedGroupName]['keep_open'] = $groupDefaults[$resolvedGroupName]['keep_open'];
172
                }
173
            }
174
        } elseif ($container->getParameter('sonata.admin.configuration.sort_admins')) {
175
            $groups = $groupDefaults;
176
177
            $elementSort = function (&$element) {
178
                usort(
179
                    $element['items'],
180
                    function ($a, $b) {
181
                        $a = !empty($a['label']) ? $a['label'] : $a['admin'];
182
                        $b = !empty($b['label']) ? $b['label'] : $b['admin'];
183
184
                        if ($a === $b) {
185
                            return 0;
186
                        }
187
188
                        return $a < $b ? -1 : 1;
189
                    }
190
                );
191
            };
192
193
            /*
194
             * 1) sort the groups by their index
195
             * 2) sort the elements within each group by label/admin
196
             */
197
            ksort($groups);
198
            array_walk($groups, $elementSort);
199
        } else {
200
            $groups = $groupDefaults;
201
        }
202
203
        $pool->addMethodCall('setAdminServiceIds', [$admins]);
204
        $pool->addMethodCall('setAdminGroups', [$groups]);
205
        $pool->addMethodCall('setAdminClasses', [$classes]);
206
207
        $routeLoader = $container->getDefinition('sonata.admin.route_loader');
208
        $routeLoader->replaceArgument(1, $admins);
209
    }
210
211
    /**
212
     * This method read the attribute keys and configure admin class to use the related dependency.
213
     */
214
    public function applyConfigurationFromAttribute(Definition $definition, array $attributes)
215
    {
216
        $keys = [
217
            'model_manager',
218
            'form_contractor',
219
            'show_builder',
220
            'list_builder',
221
            'datagrid_builder',
222
            'translator',
223
            'configuration_pool',
224
            'router',
225
            'validator',
226
            'security_handler',
227
            'menu_factory',
228
            'route_builder',
229
            'label_translator_strategy',
230
        ];
231
232
        foreach ($keys as $key) {
233
            $method = 'set'.Inflector::classify($key);
234
            if (!isset($attributes[$key]) || $definition->hasMethodCall($method)) {
235
                continue;
236
            }
237
238
            $definition->addMethodCall($method, [new Reference($attributes[$key])]);
239
        }
240
    }
241
242
    /**
243
     * Apply the default values required by the AdminInterface to the Admin service definition.
244
     *
245
     * @param string $serviceId
246
     *
247
     * @return Definition
248
     */
249
    public function applyDefaults(ContainerBuilder $container, $serviceId, array $attributes = [])
250
    {
251
        $definition = $container->getDefinition($serviceId);
252
        $settings = $container->getParameter('sonata.admin.configuration.admin_services');
253
254
        $definition->setShared(false);
255
256
        $manager_type = $attributes['manager_type'];
257
258
        $overwriteAdminConfiguration = isset($settings[$serviceId]) ? $settings[$serviceId] : [];
259
260
        $defaultAddServices = [
261
            'model_manager' => sprintf('sonata.admin.manager.%s', $manager_type),
262
            'form_contractor' => sprintf('sonata.admin.builder.%s_form', $manager_type),
263
            'show_builder' => sprintf('sonata.admin.builder.%s_show', $manager_type),
264
            'list_builder' => sprintf('sonata.admin.builder.%s_list', $manager_type),
265
            'datagrid_builder' => sprintf('sonata.admin.builder.%s_datagrid', $manager_type),
266
            'translator' => 'translator',
267
            'configuration_pool' => 'sonata.admin.pool',
268
            'route_generator' => 'sonata.admin.route.default_generator',
269
            'validator' => 'validator',
270
            'security_handler' => 'sonata.admin.security.handler',
271
            'menu_factory' => 'knp_menu.factory',
272
            'route_builder' => 'sonata.admin.route.path_info'.
273
                (('doctrine_phpcr' == $manager_type) ? '_slashes' : ''),
274
            'label_translator_strategy' => 'sonata.admin.label.strategy.native',
275
        ];
276
277
        $definition->addMethodCall('setManagerType', [$manager_type]);
278
279
        foreach ($defaultAddServices as $attr => $addServiceId) {
280
            $method = 'set'.Inflector::classify($attr);
281
282
            if (isset($overwriteAdminConfiguration[$attr]) || !$definition->hasMethodCall($method)) {
283
                $args = [new Reference(isset($overwriteAdminConfiguration[$attr]) ? $overwriteAdminConfiguration[$attr] : $addServiceId)];
284
                if ('translator' === $attr) {
285
                    $args[] = false;
286
                }
287
288
                $definition->addMethodCall($method, $args);
289
            }
290
        }
291
292
        if (isset($overwriteAdminConfiguration['pager_type'])) {
293
            $pagerType = $overwriteAdminConfiguration['pager_type'];
294
        } elseif (isset($attributes['pager_type'])) {
295
            $pagerType = $attributes['pager_type'];
296
        } else {
297
            $pagerType = Pager::TYPE_DEFAULT;
298
        }
299
300
        $definition->addMethodCall('setPagerType', [$pagerType]);
301
302
        if (isset($overwriteAdminConfiguration['label'])) {
303
            $label = $overwriteAdminConfiguration['label'];
304
        } elseif (isset($attributes['label'])) {
305
            $label = $attributes['label'];
306
        } else {
307
            $label = '-';
308
        }
309
310
        $definition->addMethodCall('setLabel', [$label]);
311
312
        $persistFilters = $container->getParameter('sonata.admin.configuration.filters.persist');
313
        // override default configuration with admin config if set
314
        if (isset($attributes['persist_filters'])) {
315
            $persistFilters = $attributes['persist_filters'];
316
        }
317
        $filtersPersister = $container->getParameter('sonata.admin.configuration.filters.persister');
318
        // override default configuration with admin config if set
319
        if (isset($attributes['filter_persister'])) {
320
            $filtersPersister = $attributes['filter_persister'];
321
        }
322
        // configure filters persistence, if configured to
323
        if ($persistFilters) {
324
            $definition->addMethodCall('setFilterPersister', [new Reference($filtersPersister)]);
325
        }
326
327
        if (isset($overwriteAdminConfiguration['show_mosaic_button'])) {
328
            $showMosaicButton = $overwriteAdminConfiguration['show_mosaic_button'];
329
        } elseif (isset($attributes['show_mosaic_button'])) {
330
            $showMosaicButton = $attributes['show_mosaic_button'];
331
        } else {
332
            $showMosaicButton = $container->getParameter('sonata.admin.configuration.show.mosaic.button');
333
        }
334
335
        $definition->addMethodCall('showMosaicButton', [$showMosaicButton]);
336
337
        $this->fixTemplates(
338
            $serviceId,
339
            $container,
340
            $definition,
341
            isset($overwriteAdminConfiguration['templates']) ? $overwriteAdminConfiguration['templates'] : ['view' => []]
342
        );
343
344
        if ($container->hasParameter('sonata.admin.configuration.security.information') && !$definition->hasMethodCall('setSecurityInformation')) {
345
            $definition->addMethodCall('setSecurityInformation', ['%sonata.admin.configuration.security.information%']);
346
        }
347
348
        $definition->addMethodCall('initialize');
349
350
        return $definition;
351
    }
352
353
    /**
354
     * @param string $serviceId
355
     */
356
    public function fixTemplates(
357
        $serviceId,
358
        ContainerBuilder $container,
359
        Definition $definition,
360
        array $overwrittenTemplates = []
361
    ) {
362
        $definedTemplates = $container->getParameter('sonata.admin.configuration.templates');
363
364
        $methods = [];
365
        $pos = 0;
366
        foreach ($definition->getMethodCalls() as $method) {
367
            if ('setTemplates' == $method[0]) {
368
                $definedTemplates = array_merge($definedTemplates, $method[1][0]);
369
370
                continue;
371
            }
372
373
            if ('setTemplate' == $method[0]) {
374
                $definedTemplates[$method[1][0]] = $method[1][1];
375
376
                continue;
377
            }
378
379
            // set template for simple pager if it is not already overwritten
380
            if ('setPagerType' === $method[0]
381
                && $method[1][0] === Pager::TYPE_SIMPLE
382
                && (
383
                    !isset($definedTemplates['pager_results'])
384
                    || '@SonataAdmin/Pager/results.html.twig' === $definedTemplates['pager_results']
385
                )
386
            ) {
387
                $definedTemplates['pager_results'] = '@SonataAdmin/Pager/simple_pager_results.html.twig';
388
            }
389
390
            $methods[$pos] = $method;
391
            ++$pos;
392
        }
393
394
        $definition->setMethodCalls($methods);
395
396
        $definedTemplates = $overwrittenTemplates['view'] + $definedTemplates;
397
398
        $templateRegistryId = $serviceId.'.template_registry';
399
        $templateRegistryDefinition = $container
400
            ->register($templateRegistryId, TemplateRegistry::class)
401
            ->addTag('sonata.admin.template_registry')
402
            ->setPublic(true); // Temporary fix until we can support service locators
403
404
        if ($container->getParameter('sonata.admin.configuration.templates') !== $definedTemplates) {
405
            $templateRegistryDefinition->addArgument($definedTemplates);
406
        } else {
407
            $templateRegistryDefinition->addArgument('%sonata.admin.configuration.templates%');
408
        }
409
410
        $definition->addMethodCall('setTemplateRegistry', [new Reference($templateRegistryId)]);
411
    }
412
413
    /**
414
     * Replace the empty arguments required by the Admin service definition.
415
     */
416
    private function replaceDefaultArguments(
417
        array $defaultArguments,
418
        Definition $definition,
419
        Definition $parentDefinition = null
420
    ) {
421
        $arguments = $definition->getArguments();
422
        $parentArguments = $parentDefinition ? $parentDefinition->getArguments() : [];
423
424
        foreach ($defaultArguments as $index => $value) {
425
            $declaredInParent = $parentDefinition && array_key_exists($index, $parentArguments);
426
            $argumentValue = $declaredInParent ? $parentArguments[$index] : $arguments[$index];
427
428
            if (null === $argumentValue || 0 === strlen($argumentValue)) {
429
                $arguments[$declaredInParent ? sprintf('index_%s', $index) : $index] = $value;
430
            }
431
        }
432
433
        $definition->setArguments($arguments);
434
    }
435
}
436