Completed
Pull Request — master (#304)
by Marc
02:07
created

RenderViewHelper::render()   D

Complexity

Conditions 9
Paths 28

Size

Total Lines 37
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

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