Completed
Push — 3.x ( 37ee0f...10863f )
by Vincent
02:54
created

SonataAdminExtension::renderViewElementCompare()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 61
rs 8.8509
c 0
b 0
f 0
cc 3
nc 4
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
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Twig\Extension;
15
16
use Doctrine\Common\Util\ClassUtils;
17
use Psr\Log\LoggerInterface;
18
use Sonata\AdminBundle\Admin\AdminInterface;
19
use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
20
use Sonata\AdminBundle\Admin\Pool;
21
use Sonata\AdminBundle\Exception\NoValueException;
22
use Sonata\AdminBundle\Templating\TemplateRegistryInterface;
23
use Symfony\Component\DependencyInjection\ContainerInterface;
24
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
25
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
26
use Symfony\Component\Security\Acl\Voter\FieldVote;
27
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
28
use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException;
29
use Symfony\Component\Translation\TranslatorInterface as LegacyTranslationInterface;
30
use Symfony\Contracts\Translation\TranslatorInterface;
31
use Twig\Environment;
32
use Twig\Error\LoaderError;
33
use Twig\Extension\AbstractExtension;
34
use Twig\Template;
35
use Twig\TemplateWrapper;
36
use Twig\TwigFilter;
37
use Twig\TwigFunction;
38
39
/**
40
 * @final since sonata-project/admin-bundle 3.52
41
 *
42
 * @author Thomas Rabaix <[email protected]>
43
 */
44
class SonataAdminExtension extends AbstractExtension
45
{
46
    // @todo: there are more locales which are not supported by moment and they need to be translated/normalized/canonicalized here
47
    public const MOMENT_UNSUPPORTED_LOCALES = [
48
        'de' => ['de', 'de-at'],
49
        'es' => ['es', 'es-do'],
50
        'nl' => ['nl', 'nl-be'],
51
        'fr' => ['fr', 'fr-ca', 'fr-ch'],
52
    ];
53
54
    /**
55
     * @var Pool
56
     */
57
    protected $pool;
58
59
    /**
60
     * @var LoggerInterface
61
     */
62
    protected $logger;
63
64
    /**
65
     * @var TranslatorInterface|null
66
     */
67
    protected $translator;
68
69
    /**
70
     * @var string[]
71
     */
72
    private $xEditableTypeMapping = [];
73
74
    /**
75
     * @var ContainerInterface
76
     */
77
    private $templateRegistries;
78
79
    /**
80
     * @var AuthorizationCheckerInterface
81
     */
82
    private $securityChecker;
83
84
    public function __construct(
85
        Pool $pool,
86
        ?LoggerInterface $logger = null,
87
        $translator = null,
88
        ?ContainerInterface $templateRegistries = null,
89
        ?AuthorizationCheckerInterface $securityChecker = null
90
    ) {
91
        // NEXT_MAJOR: make the translator parameter required, move TranslatorInterface check to method signature
92
        // and remove this block
93
94
        if (null === $translator) {
95
            @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
96
                'The $translator parameter will be required fields with the 4.0 release.',
97
                E_USER_DEPRECATED
98
            );
99
        } else {
100
            if (!$translator instanceof TranslatorInterface) {
101
                @trigger_error(sprintf(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
102
                    'The $translator parameter should be an instance of "%s" and will be mandatory in 4.0.',
103
                    TranslatorInterface::class
104
                ), E_USER_DEPRECATED);
105
            }
106
107
            if (!$translator instanceof TranslatorInterface && !$translator instanceof LegacyTranslationInterface) {
108
                throw new \TypeError(sprintf(
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with sprintf('Argument 2 must...get_class($translator)).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
109
                    'Argument 2 must be an instance of "%s" or preferably "%s", "%s given"',
110
                    TranslatorInterface::class,
111
                    LegacyTranslationInterface::class,
112
                    \get_class($translator)
113
                ));
114
            }
115
        }
116
117
        $this->pool = $pool;
118
        $this->logger = $logger;
119
        $this->translator = $translator;
120
        $this->templateRegistries = $templateRegistries;
121
        $this->securityChecker = $securityChecker;
122
    }
123
124
    public function getFilters()
125
    {
126
        return [
127
            new TwigFilter(
128
                'render_list_element',
129
                [$this, 'renderListElement'],
130
                [
131
                    'is_safe' => ['html'],
132
                    'needs_environment' => true,
133
                ]
134
            ),
135
            new TwigFilter(
136
                'render_view_element',
137
                [$this, 'renderViewElement'],
138
                [
139
                    'is_safe' => ['html'],
140
                    'needs_environment' => true,
141
                ]
142
            ),
143
            new TwigFilter(
144
                'render_view_element_compare',
145
                [$this, 'renderViewElementCompare'],
146
                [
147
                    'is_safe' => ['html'],
148
                    'needs_environment' => true,
149
                ]
150
            ),
151
            new TwigFilter(
152
                'render_relation_element',
153
                [$this, 'renderRelationElement']
154
            ),
155
            new TwigFilter(
156
                'sonata_urlsafeid',
157
                [$this, 'getUrlSafeIdentifier']
158
            ),
159
            new TwigFilter(
160
                'sonata_xeditable_type',
161
                [$this, 'getXEditableType']
162
            ),
163
            new TwigFilter(
164
                'sonata_xeditable_choices',
165
                [$this, 'getXEditableChoices']
166
            ),
167
        ];
168
    }
169
170
    public function getFunctions()
171
    {
172
        return [
173
            new TwigFunction('canonicalize_locale_for_moment', [$this, 'getCanonicalizedLocaleForMoment'], ['needs_context' => true]),
174
            new TwigFunction('canonicalize_locale_for_select2', [$this, 'getCanonicalizedLocaleForSelect2'], ['needs_context' => true]),
175
            new TwigFunction('is_granted_affirmative', [$this, 'isGrantedAffirmative']),
176
        ];
177
    }
178
179
    public function getName()
180
    {
181
        return 'sonata_admin';
182
    }
183
184
    /**
185
     * render a list element from the FieldDescription.
186
     *
187
     * @param object $object
188
     * @param array  $params
189
     *
190
     * @return string
191
     */
192
    public function renderListElement(
193
        Environment $environment,
194
        $object,
195
        FieldDescriptionInterface $fieldDescription,
196
        $params = []
197
    ) {
198
        $template = $this->getTemplate(
199
            $fieldDescription,
200
            // NEXT_MAJOR: Remove this line and use commented line below instead
201
            $fieldDescription->getAdmin()->getTemplate('base_list_field'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
202
            //$this->getTemplateRegistry($fieldDescription->getAdmin()->getCode())->getTemplate('base_list_field'),
203
            $environment
204
        );
205
206
        return $this->render($fieldDescription, $template, array_merge($params, [
207
            'admin' => $fieldDescription->getAdmin(),
208
            'object' => $object,
209
            'value' => $this->getValueFromFieldDescription($object, $fieldDescription),
210
            'field_description' => $fieldDescription,
211
        ]), $environment);
212
    }
213
214
    /**
215
     * @deprecated since sonata-project/admin-bundle 3.33, to be removed in 4.0. Use render instead
216
     *
217
     * @return string
218
     */
219
    public function output(
220
        FieldDescriptionInterface $fieldDescription,
221
        Template $template,
222
        array $parameters,
223
        Environment $environment
224
    ) {
225
        return $this->render(
226
            $fieldDescription,
227
            new TemplateWrapper($environment, $template),
228
            $parameters,
229
            $environment
230
        );
231
    }
232
233
    /**
234
     * return the value related to FieldDescription, if the associated object does no
235
     * exists => a temporary one is created.
236
     *
237
     * @param object $object
238
     *
239
     * @throws \RuntimeException
240
     *
241
     * @return mixed
242
     */
243
    public function getValueFromFieldDescription(
244
        $object,
245
        FieldDescriptionInterface $fieldDescription,
246
        array $params = []
247
    ) {
248
        if (isset($params['loop']) && $object instanceof \ArrayAccess) {
249
            throw new \RuntimeException('remove the loop requirement');
250
        }
251
252
        $value = null;
253
254
        try {
255
            $value = $fieldDescription->getValue($object);
256
        } catch (NoValueException $e) {
257
            if ($fieldDescription->getAssociationAdmin()) {
258
                $value = $fieldDescription->getAssociationAdmin()->getNewInstance();
259
            } else {
260
                // NEXT_MAJOR: throw the NoValueException.
261
                @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
262
                    'Accessing a non existing value is deprecated'
263
                    .' since sonata-project/admin-bundle 3.x and will throw an exception in 4.0.',
264
                    E_USER_DEPRECATED
265
                );
266
            }
267
        }
268
269
        return $value;
270
    }
271
272
    /**
273
     * render a view element.
274
     *
275
     * @param object $object
276
     *
277
     * @return string
278
     */
279
    public function renderViewElement(
280
        Environment $environment,
281
        FieldDescriptionInterface $fieldDescription,
282
        $object
283
    ) {
284
        $template = $this->getTemplate(
285
            $fieldDescription,
286
            '@SonataAdmin/CRUD/base_show_field.html.twig',
287
            $environment
288
        );
289
290
        try {
291
            $value = $fieldDescription->getValue($object);
292
        } catch (NoValueException $e) {
293
            // NEXT_MAJOR: Remove the try catch in order to throw the NoValueException.
294
            @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
295
                'Accessing a non existing value is deprecated'
296
                .' since sonata-project/admin-bundle 3.x and will throw an exception in 4.0.',
297
                E_USER_DEPRECATED
298
            );
299
300
            $value = null;
301
        }
302
303
        return $this->render($fieldDescription, $template, [
304
            'field_description' => $fieldDescription,
305
            'object' => $object,
306
            'value' => $value,
307
            'admin' => $fieldDescription->getAdmin(),
308
        ], $environment);
309
    }
310
311
    /**
312
     * render a compared view element.
313
     *
314
     * @param mixed $baseObject
315
     * @param mixed $compareObject
316
     *
317
     * @return string
318
     */
319
    public function renderViewElementCompare(
320
        Environment $environment,
321
        FieldDescriptionInterface $fieldDescription,
322
        $baseObject,
323
        $compareObject
324
    ) {
325
        $template = $this->getTemplate(
326
            $fieldDescription,
327
            '@SonataAdmin/CRUD/base_show_field.html.twig',
328
            $environment
329
        );
330
331
        try {
332
            $baseValue = $fieldDescription->getValue($baseObject);
333
        } catch (NoValueException $e) {
334
            // NEXT_MAJOR: Remove the try catch in order to throw the NoValueException.
335
            @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
336
                'Accessing a non existing value is deprecated'
337
                .' since sonata-project/admin-bundle 3.x and will throw an exception in 4.0.',
338
                E_USER_DEPRECATED
339
            );
340
341
            $baseValue = null;
342
        }
343
344
        try {
345
            $compareValue = $fieldDescription->getValue($compareObject);
346
        } catch (NoValueException $e) {
347
            // NEXT_MAJOR: Remove the try catch in order to throw the NoValueException.
348
            @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
349
                'Accessing a non existing value is deprecated'
350
                .' since sonata-project/admin-bundle 3.x and will throw an exception in 4.0.',
351
                E_USER_DEPRECATED
352
            );
353
354
            $compareValue = null;
355
        }
356
357
        $baseValueOutput = $template->render([
358
            'admin' => $fieldDescription->getAdmin(),
359
            'field_description' => $fieldDescription,
360
            'value' => $baseValue,
361
        ]);
362
363
        $compareValueOutput = $template->render([
364
            'field_description' => $fieldDescription,
365
            'admin' => $fieldDescription->getAdmin(),
366
            'value' => $compareValue,
367
        ]);
368
369
        // Compare the rendered output of both objects by using the (possibly) overridden field block
370
        $isDiff = $baseValueOutput !== $compareValueOutput;
371
372
        return $this->render($fieldDescription, $template, [
373
            'field_description' => $fieldDescription,
374
            'value' => $baseValue,
375
            'value_compare' => $compareValue,
376
            'is_diff' => $isDiff,
377
            'admin' => $fieldDescription->getAdmin(),
378
        ], $environment);
379
    }
380
381
    /**
382
     * @param mixed $element
383
     *
384
     * @throws \RuntimeException
385
     *
386
     * @return mixed
387
     */
388
    public function renderRelationElement($element, FieldDescriptionInterface $fieldDescription)
389
    {
390
        if (!\is_object($element)) {
391
            return $element;
392
        }
393
394
        $propertyPath = $fieldDescription->getOption('associated_property');
395
396
        if (null === $propertyPath) {
397
            // For BC kept associated_tostring option behavior
398
            $method = $fieldDescription->getOption('associated_tostring');
399
400
            if ($method) {
401
                @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
402
                    'Option "associated_tostring" is deprecated since version 2.3 and will be removed in 4.0. '
403
                    .'Use "associated_property" instead.',
404
                    E_USER_DEPRECATED
405
                );
406
            } else {
407
                $method = '__toString';
408
            }
409
410
            if (!method_exists($element, $method)) {
411
                throw new \RuntimeException(sprintf(
412
                    'You must define an `associated_property` option or '.
413
                    'create a `%s::__toString` method to the field option %s from service %s is ',
414
                    \get_class($element),
415
                    $fieldDescription->getName(),
416
                    $fieldDescription->getAdmin()->getCode()
417
                ));
418
            }
419
420
            return $element->{$method}();
421
        }
422
423
        if (\is_callable($propertyPath)) {
424
            return $propertyPath($element);
425
        }
426
427
        return $this->pool->getPropertyAccessor()->getValue($element, $propertyPath);
428
    }
429
430
    /**
431
     * Get the identifiers as a string that is safe to use in a url.
432
     *
433
     * @param object $model
434
     *
435
     * @return string string representation of the id that is safe to use in a url
436
     */
437
    public function getUrlSafeIdentifier($model, ?AdminInterface $admin = null)
438
    {
439
        if (null === $admin) {
440
            $admin = $this->pool->getAdminByClass(ClassUtils::getClass($model));
441
        }
442
443
        return $admin->getUrlSafeIdentifier($model);
444
    }
445
446
    /**
447
     * @param string[] $xEditableTypeMapping
448
     */
449
    public function setXEditableTypeMapping($xEditableTypeMapping)
450
    {
451
        $this->xEditableTypeMapping = $xEditableTypeMapping;
452
    }
453
454
    /**
455
     * @return string|bool
456
     */
457
    public function getXEditableType($type)
458
    {
459
        return isset($this->xEditableTypeMapping[$type]) ? $this->xEditableTypeMapping[$type] : false;
460
    }
461
462
    /**
463
     * Return xEditable choices based on the field description choices options & catalogue options.
464
     * With the following choice options:
465
     *     ['Status1' => 'Alias1', 'Status2' => 'Alias2']
466
     * The method will return:
467
     *     [['value' => 'Status1', 'text' => 'Alias1'], ['value' => 'Status2', 'text' => 'Alias2']].
468
     *
469
     * @return array
470
     */
471
    public function getXEditableChoices(FieldDescriptionInterface $fieldDescription)
472
    {
473
        $choices = $fieldDescription->getOption('choices', []);
474
        $catalogue = $fieldDescription->getOption('catalogue');
475
        $xEditableChoices = [];
476
        if (!empty($choices)) {
477
            reset($choices);
478
            $first = current($choices);
479
            // the choices are already in the right format
480
            if (\is_array($first) && \array_key_exists('value', $first) && \array_key_exists('text', $first)) {
481
                $xEditableChoices = $choices;
482
            } else {
483
                foreach ($choices as $value => $text) {
484
                    if ($catalogue) {
485
                        if (null !== $this->translator) {
486
                            $text = $this->translator->trans($text, [], $catalogue);
487
                        // NEXT_MAJOR: Remove this check
488
                        } elseif (method_exists($fieldDescription->getAdmin(), 'trans')) {
489
                            $text = $fieldDescription->getAdmin()->trans($text, [], $catalogue);
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin\AdminInterface::trans() has been deprecated with message: since sonata-project/admin-bundle 3.9, to be removed in 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
490
                        }
491
                    }
492
493
                    $xEditableChoices[] = [
494
                        'value' => $value,
495
                        'text' => $text,
496
                    ];
497
                }
498
            }
499
        }
500
501
        if (false === $fieldDescription->getOption('required', true)
502
            && false === $fieldDescription->getOption('multiple', false)
503
        ) {
504
            $xEditableChoices = array_merge([[
505
                'value' => '',
506
                'text' => '',
507
            ]], $xEditableChoices);
508
        }
509
510
        return $xEditableChoices;
511
    }
512
513
    /**
514
     * Returns a canonicalized locale for "moment" NPM library,
515
     * or `null` if the locale's language is "en", which doesn't require localization.
516
     *
517
     * @return string|null
518
     */
519
    final public function getCanonicalizedLocaleForMoment(array $context)
520
    {
521
        $locale = strtolower(str_replace('_', '-', $context['app']->getRequest()->getLocale()));
522
523
        // "en" language doesn't require localization.
524
        if (('en' === $lang = substr($locale, 0, 2)) && !\in_array($locale, ['en-au', 'en-ca', 'en-gb', 'en-ie', 'en-nz'], true)) {
525
            return null;
526
        }
527
528
        foreach (self::MOMENT_UNSUPPORTED_LOCALES as $language => $locales) {
529
            if ($language === $lang && !\in_array($locale, $locales, true)) {
530
                $locale = $language;
531
            }
532
        }
533
534
        return $locale;
535
    }
536
537
    /**
538
     * Returns a canonicalized locale for "select2" NPM library,
539
     * or `null` if the locale's language is "en", which doesn't require localization.
540
     *
541
     * @return string|null
542
     */
543
    final public function getCanonicalizedLocaleForSelect2(array $context)
544
    {
545
        $locale = str_replace('_', '-', $context['app']->getRequest()->getLocale());
546
547
        // "en" language doesn't require localization.
548
        if ('en' === $lang = substr($locale, 0, 2)) {
549
            return null;
550
        }
551
552
        switch ($locale) {
553
            case 'pt':
554
                $locale = 'pt-PT';
555
                break;
556
            case 'ug':
557
                $locale = 'ug-CN';
558
                break;
559
            case 'zh':
560
                $locale = 'zh-CN';
561
                break;
562
            default:
563
                if (!\in_array($locale, ['pt-BR', 'pt-PT', 'ug-CN', 'zh-CN', 'zh-TW'], true)) {
564
                    $locale = $lang;
565
                }
566
        }
567
568
        return $locale;
569
    }
570
571
    /**
572
     * @param string|array $role
573
     * @param object|null  $object
574
     * @param string|null  $field
575
     *
576
     * @return bool
577
     */
578
    public function isGrantedAffirmative($role, $object = null, $field = null)
579
    {
580
        if (null === $this->securityChecker) {
581
            return false;
582
        }
583
584
        if (null !== $field) {
585
            $object = new FieldVote($object, $field);
586
        }
587
588
        if (!\is_array($role)) {
589
            $role = [$role];
590
        }
591
592
        foreach ($role as $oneRole) {
593
            try {
594
                if ($this->securityChecker->isGranted($oneRole, $object)) {
595
                    return true;
596
                }
597
            } catch (AuthenticationCredentialsNotFoundException $e) {
598
                // empty on purpose
599
            }
600
        }
601
602
        return false;
603
    }
604
605
    /**
606
     * Get template.
607
     *
608
     * @param string $defaultTemplate
609
     *
610
     * @return TemplateWrapper
611
     */
612
    protected function getTemplate(
613
        FieldDescriptionInterface $fieldDescription,
614
        $defaultTemplate,
615
        Environment $environment
616
    ) {
617
        $templateName = $fieldDescription->getTemplate() ?: $defaultTemplate;
618
619
        try {
620
            $template = $environment->load($templateName);
621
        } catch (LoaderError $e) {
622
            @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
623
                sprintf(
624
                    'Relying on default template loading on field template loading exception '.
625
                    'is deprecated since 3.1 and will be removed in 4.0. '.
626
                    'A %s exception will be thrown instead',
627
                    LoaderError::class
628
                ),
629
                E_USER_DEPRECATED
630
            );
631
            $template = $environment->load($defaultTemplate);
632
633
            if (null !== $this->logger) {
634
                $this->logger->warning(sprintf(
635
                    'An error occured trying to load the template "%s" for the field "%s", '.
636
                    'the default template "%s" was used instead.',
637
                    $templateName,
638
                    $fieldDescription->getFieldName(),
639
                    $defaultTemplate
640
                ), ['exception' => $e]);
641
            }
642
        }
643
644
        return $template;
645
    }
646
647
    private function render(
648
        FieldDescriptionInterface $fieldDescription,
649
        TemplateWrapper $template,
650
        array $parameters,
651
        Environment $environment
652
    ): ?string {
653
        $content = $template->render($parameters);
654
655
        if ($environment->isDebug()) {
656
            $commentTemplate = <<<'EOT'
657
658
<!-- START
659
    fieldName: %s
660
    template: %s
661
    compiled template: %s
662
    -->
663
    %s
664
<!-- END - fieldName: %s -->
665
EOT;
666
667
            return sprintf(
668
                $commentTemplate,
669
                $fieldDescription->getFieldName(),
670
                $fieldDescription->getTemplate(),
671
                $template->getSourceContext()->getName(),
672
                $content,
673
                $fieldDescription->getFieldName()
674
            );
675
        }
676
677
        return $content;
678
    }
679
680
    /**
681
     * @throws ServiceCircularReferenceException
682
     * @throws ServiceNotFoundException
683
     */
684
    private function getTemplateRegistry(string $adminCode): TemplateRegistryInterface
685
    {
686
        $serviceId = $adminCode.'.template_registry';
687
        $templateRegistry = $this->templateRegistries->get($serviceId);
688
689
        if ($templateRegistry instanceof TemplateRegistryInterface) {
690
            return $templateRegistry;
691
        }
692
693
        throw new ServiceNotFoundException($serviceId);
694
    }
695
}
696