Completed
Pull Request — master (#340)
by Claus
02:05
created

RenderViewHelper::renderStatic()   C

Complexity

Conditions 11
Paths 36

Size

Total Lines 49
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 36
nc 36
nop 3
dl 0
loc 49
rs 5.2653
c 0
b 0
f 0

How to fix   Complexity   

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
namespace TYPO3Fluid\Fluid\ViewHelpers;
3
4
/*
5
 * This file belongs to the package "TYPO3 Fluid".
6
 * See LICENSE.txt that was shipped with this package.
7
 */
8
9
use TYPO3Fluid\Fluid\Core\Parser\ParsedTemplateInterface;
10
use TYPO3Fluid\Fluid\Core\Rendering\RenderableInterface;
11
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
12
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
13
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
14
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
15
16
/**
17
 * A ViewHelper to render a section, a partial, a specified section in a partial
18
 * or a delegate ParsedTemplateInterface implementation.
19
 *
20
 * = Examples =
21
 *
22
 * <code title="Rendering partials">
23
 * <f:render partial="SomePartial" arguments="{foo: someVariable}" />
24
 * </code>
25
 * <output>
26
 * the content of the partial "SomePartial". The content of the variable {someVariable} will be available in the partial as {foo}
27
 * </output>
28
 *
29
 * <code title="Rendering sections">
30
 * <f:section name="someSection">This is a section. {foo}</f:section>
31
 * <f:render section="someSection" arguments="{foo: someVariable}" />
32
 * </code>
33
 * <output>
34
 * the content of the section "someSection". The content of the variable {someVariable} will be available in the partial as {foo}
35
 * </output>
36
 *
37
 * <code title="Rendering recursive sections">
38
 * <f:section name="mySection">
39
 *  <ul>
40
 *    <f:for each="{myMenu}" as="menuItem">
41
 *      <li>
42
 *        {menuItem.text}
43
 *        <f:if condition="{menuItem.subItems}">
44
 *          <f:render section="mySection" arguments="{myMenu: menuItem.subItems}" />
45
 *        </f:if>
46
 *      </li>
47
 *    </f:for>
48
 *  </ul>
49
 * </f:section>
50
 * <f:render section="mySection" arguments="{myMenu: menu}" />
51
 * </code>
52
 * <output>
53
 * <ul>
54
 *   <li>menu1
55
 *     <ul>
56
 *       <li>menu1a</li>
57
 *       <li>menu1b</li>
58
 *     </ul>
59
 *   </li>
60
 * [...]
61
 * (depending on the value of {menu})
62
 * </output>
63
 *
64
 *
65
 * <code title="Passing all variables to a partial">
66
 * <f:render partial="somePartial" arguments="{_all}" />
67
 * </code>
68
 * <output>
69
 * the content of the partial "somePartial".
70
 * Using the reserved keyword "_all", all available variables will be passed along to the partial
71
 * </output>
72
 *
73
 *
74
 * <code title="Rendering via a delegate ParsedTemplateInterface implementation w/ custom arguments">
75
 * <f:render delegate="My\Special\ParsedTemplateImplementation" arguments="{_all}" />
76
 * </code>
77
 * <output>
78
 * Whichever output was generated by calling My\Special\ParsedTemplateImplementation->render()
79
 * with cloned RenderingContextInterface $renderingContext as only argument and content of arguments
80
 * assigned in VariableProvider of cloned context. Supports all other input arguments including
81
 * recursive rendering, contentAs argument, default value etc.
82
 * Note that while ParsedTemplateInterface supports returning a Layout name, this Layout will not
83
 * be respected when rendering using this method. Only the `render()` method will be called!
84
 * </output>
85
 *
86
 * @api
87
 */
88
class RenderViewHelper extends AbstractViewHelper
89
{
90
    use CompileWithRenderStatic;
91
    
92
    /**
93
     * @var boolean
94
     */
95
    protected $escapeOutput = false;
96
97
    /**
98
     * @return void
99
     */
100
    public function initializeArguments()
101
    {
102
        parent::initializeArguments();
103
        $this->registerArgument('section', 'string', 'Section to render - combine with partial to render section in partial');
104
        $this->registerArgument('partial', 'string', 'Partial to render, with or without section');
105
        $this->registerArgument('delegate', 'string', 'Optional PHP class name of a permanent, included-in-app ParsedTemplateInterface implementation to override partial/section');
106
        $this->registerArgument('renderable', RenderableInterface::class, 'Instance of a RenderableInterface implementation to be rendered');
107
        $this->registerArgument('arguments', 'array', 'Array of variables to be transferred. Use {_all} for all variables', false, []);
108
        $this->registerArgument('optional', 'boolean', 'If TRUE, considers the *section* optional. Partial never is.', false, false);
109
        $this->registerArgument('default', 'mixed', 'Value (usually string) to be displayed if the section or partial does not exist');
110
        $this->registerArgument('contentAs', 'string', 'If used, renders the child content and adds it as a template variable with this name for use in the partial/section');
111
    }
112
113
    /**
114
     * @return mixed
115
     */
116
    public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
117
    {
118
        $section = $arguments['section'];
119
        $partial = $arguments['partial'];
120
        $variables = (array) $arguments['arguments'];
121
        $optional = (boolean) $arguments['optional'];
122
        $delegate = $arguments['delegate'];
123
        /** @var RenderableInterface $renderable */
124
        $renderable = $arguments['renderable'];
125
        $tagContent = $renderChildrenClosure();
126
        if ($arguments['contentAs']) {
127
            $variables[$arguments['contentAs']] = $tagContent;
128
        }
129
130
        $view = $renderingContext->getViewHelperVariableContainer()->getView();
131
        if (!$view) {
132
            throw new Exception(
133
                'The f:render ViewHelper was used in a context where the ViewHelperVariableContainer does not contain ' .
134
                'a reference to the View. Normally this is taken care of by the TemplateView, so most likely this ' .
135
                'error is because you overrode AbstractTemplateView->initializeRenderingContext() and did not call ' .
136
                '$renderingContext->getViewHelperVariableContainer()->setView($this) or parent::initializeRenderingContext. ' .
137
                'This is an issue you must fix in your code as f:render is fully unable to render anything without a View.'
138
            );
139
        }
140
        $content = '';
141
        if ($renderable) {
142
            $content = $renderable->render($renderingContext);
143
        } elseif ($delegate !== null) {
144
            if (!is_a($delegate, ParsedTemplateInterface::class, true)) {
145
                throw new \InvalidArgumentException(sprintf('Cannot render %s - must implement ParsedTemplateInterface!', $delegate));
146
            }
147
            $renderingContext = clone $renderingContext;
148
            $renderingContext->getVariableProvider()->setSource($variables);
149
            $content = (new $delegate())->render($renderingContext);
150
        } elseif ($partial !== null) {
151
            $content = $view->renderPartial($partial, $section, $variables, $optional);
152
        } elseif ($section !== null) {
153
            $content = $view->renderSection($section, $variables, $optional);
154
        } elseif (!$optional) {
155
            throw new \InvalidArgumentException('ViewHelper f:render called without either argument section, partial, renderable or delegate and optional flag is false');
156
        }
157
        // Replace empty content with default value. If default is
158
        // not set, NULL is returned and cast to a new, empty string
159
        // outside of this ViewHelper.
160
        if ($content === '') {
161
            $content = $arguments['default'] ?: $tagContent;
162
        }
163
        return $content;
164
    }
165
}
166