11
11
12
12
namespace Symfony \UX \TwigComponent \Twig ;
13
13
14
+ use Symfony \UX \TwigComponent \BlockStack ;
14
15
use Twig \Compiler ;
15
- use Twig \Node \EmbedNode ;
16
16
use Twig \Node \Expression \AbstractExpression ;
17
+ use Twig \Node \Node ;
17
18
18
19
/**
19
20
* @author Fabien Potencier <fabien@symfony.com>
20
21
* @author Kevin Bond <kevinbond@gmail.com>
21
22
*
22
23
* @internal
23
24
*/
24
- final class ComponentNode extends EmbedNode
25
+ final class ComponentNode extends Node
25
26
{
26
- public function __construct (string $ component , string $ template , int $ index , AbstractExpression $ variables , bool $ only , int $ lineno , string $ tag )
27
+ public function __construct (string $ component , string $ embeddedTemplateName , int $ embeddedTemplateIndex , ? AbstractExpression $ props , bool $ only , int $ lineno , string $ tag )
27
28
{
28
- parent ::__construct ($ template , $ index , $ variables , $ only , false , $ lineno , $ tag );
29
+ $ nodes = [];
30
+ if (null !== $ props ) {
31
+ $ nodes ['props ' ] = $ props ;
32
+ }
29
33
34
+ parent ::__construct ($ nodes , [], $ lineno , $ tag );
35
+
36
+ $ this ->setAttribute ('only ' , $ only );
37
+ $ this ->setAttribute ('embedded_template ' , $ embeddedTemplateName );
38
+ $ this ->setAttribute ('embedded_index ' , $ embeddedTemplateIndex );
30
39
$ this ->setAttribute ('component ' , $ component );
31
40
}
32
41
33
42
public function compile (Compiler $ compiler ): void
34
43
{
35
44
$ compiler ->addDebugInfo ($ this );
36
45
46
+ /*
47
+ * Block 1) PreCreateForRender handling
48
+ *
49
+ * We call code to trigger the PreCreateForRender event. If the event returns
50
+ * a string, we return that string and skip the rest of the rendering process.
51
+ */
37
52
$ compiler
38
53
->write ('$preRendered = $this->extensions[ ' )
39
54
->string (ComponentExtension::class)
40
- ->raw (']->preRender ( ' )
55
+ ->raw (']->extensionPreCreateForRender ( ' )
41
56
->string ($ this ->getAttribute ('component ' ))
42
57
->raw (', ' )
43
58
->raw ('twig_to_array( ' )
44
- ->subcompile ($ this ->getNode ('variables ' ))
59
+ ;
60
+ $ this ->writeProps ($ compiler )
45
61
->raw (') ' )
46
62
->raw ("); \n" )
47
63
;
@@ -58,32 +74,85 @@ public function compile(Compiler $compiler): void
58
74
->indent ()
59
75
;
60
76
77
+ /*
78
+ * Block 2) Create the component & return render info
79
+ *
80
+ * We call code that creates the component and dispatches the
81
+ * PreRender event. The result $preRenderEvent variable holds
82
+ * the final template, template index & variables.
83
+ */
61
84
$ compiler
62
- ->write ('$embeddedContext = $this->extensions[ ' )
85
+ ->write ('$preRenderEvent = $this->extensions[ ' )
63
86
->string (ComponentExtension::class)
64
- ->raw (']->embeddedContext ( ' )
87
+ ->raw (']->startEmbeddedComponentRender ( ' )
65
88
->string ($ this ->getAttribute ('component ' ))
66
89
->raw (', twig_to_array( ' )
67
- ->subcompile ($ this ->getNode ('variables ' ))
90
+ ;
91
+ $ this ->writeProps ($ compiler )
68
92
->raw ('), ' )
69
93
->raw ($ this ->getAttribute ('only ' ) ? '[] ' : '$context ' )
70
94
->raw (', ' )
71
- ->string (TemplateNameParser::parse ($ this ->getAttribute ('name ' )))
95
+ ->string (TemplateNameParser::parse ($ this ->getAttribute ('embedded_template ' )))
72
96
->raw (', ' )
73
- ->raw ($ this ->getAttribute ('index ' ))
97
+ ->raw ($ this ->getAttribute ('embedded_index ' ))
74
98
->raw ("); \n" )
75
99
;
100
+ $ compiler
101
+ ->write ('$embeddedContext = $preRenderEvent->getVariables(); ' )
102
+ ->raw ("\n" )
103
+ // Add __parent__ to the embedded context: this is used in its extends
104
+ // Note: PreRenderEvent::getTemplateIndex() is not used here. This is
105
+ // only used during "normal" {{ component() }} rendering, which allows
106
+ // you to target rendering a specific "embedded template" that originally
107
+ // came from a {% component %} tag. This is used by LiveComponents to
108
+ // allow an "embedded component" syntax live component to be re-rendered.
109
+ // In this case, we are obviously rendering an entire template, which
110
+ // happens to contain a {% component %} tag. So we don't need to worry
111
+ // about trying to allow a specific embedded template to be targeted.
112
+ ->write ('$embeddedContext["__parent__"] = $preRenderEvent->getTemplate(); ' )
113
+ ->raw ("\n" )
114
+ ;
115
+
116
+ /*
117
+ * Block 3) Add & update the block stack
118
+ *
119
+ * We add the outerBlock to the context if it doesn't exist yet.
120
+ * Then add them to the block stack and get the converted embedded blocks.
121
+ */
122
+ $ compiler ->write ('if (!isset($embeddedContext["outerBlocks"])) { ' )
123
+ ->raw ("\n" )
124
+ ->indent ()
125
+ ->write (sprintf ('$embeddedContext["outerBlocks"] = new \%s(); ' , BlockStack::class))
126
+ ->raw ("\n" )
127
+ ->outdent ()
128
+ ->write ('} ' )
129
+ ->raw ("\n" );
76
130
77
131
$ compiler ->write ('$embeddedBlocks = $embeddedContext[ ' )
78
132
->string ('outerBlocks ' )
79
133
->raw (']->convert($blocks, ' )
80
- ->raw ($ this ->getAttribute ('index ' ))
134
+ ->raw ($ this ->getAttribute ('embedded_index ' ))
81
135
->raw ("); \n" )
82
136
;
83
137
84
- $ this ->addGetTemplate ($ compiler );
85
- $ compiler ->raw ('->display($embeddedContext, $embeddedBlocks); ' );
86
- $ compiler ->raw ("\n" );
138
+ /*
139
+ * Block 4) Render the component template
140
+ *
141
+ * This will actually render the child component template.
142
+ */
143
+ $ compiler
144
+ ->write ('$this->loadTemplate( ' )
145
+ ->string ($ this ->getAttribute ('embedded_template ' ))
146
+ ->raw (', ' )
147
+ ->repr ($ this ->getTemplateName ())
148
+ ->raw (', ' )
149
+ ->repr ($ this ->getTemplateLine ())
150
+ ->raw (', ' )
151
+ ->string ($ this ->getAttribute ('embedded_index ' ))
152
+ ->raw (') ' )
153
+ ->raw ('->display($embeddedContext, $embeddedBlocks); ' )
154
+ ->raw ("\n" )
155
+ ;
87
156
88
157
$ compiler ->write ('$this->extensions[ ' )
89
158
->string (ComponentExtension::class)
@@ -97,4 +166,13 @@ public function compile(Compiler $compiler): void
97
166
->raw ("\n" )
98
167
;
99
168
}
169
+
170
+ private function writeProps (Compiler $ compiler ): Compiler
171
+ {
172
+ if ($ this ->hasNode ('props ' )) {
173
+ return $ compiler ->subcompile ($ this ->getNode ('props ' ));
174
+ }
175
+
176
+ return $ compiler ->raw ('[] ' );
177
+ }
100
178
}
0 commit comments