1111
1212namespace Symfony \UX \TwigComponent \Twig ;
1313
14+ use Symfony \UX \TwigComponent \BlockStack ;
1415use Twig \Compiler ;
15- use Twig \Node \EmbedNode ;
1616use Twig \Node \Expression \AbstractExpression ;
17+ use Twig \Node \Node ;
1718
1819/**
1920 * @author Fabien Potencier <fabien@symfony.com>
2021 * @author Kevin Bond <kevinbond@gmail.com>
2122 *
2223 * @internal
2324 */
24- final class ComponentNode extends EmbedNode
25+ final class ComponentNode extends Node
2526{
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 )
2728 {
28- parent ::__construct ($ template , $ index , $ variables , $ only , false , $ lineno , $ tag );
29+ $ nodes = [];
30+ if (null !== $ props ) {
31+ $ nodes ['props ' ] = $ props ;
32+ }
2933
34+ parent ::__construct ($ nodes , [], $ lineno , $ tag );
35+
36+ $ this ->setAttribute ('only ' , $ only );
37+ $ this ->setAttribute ('embedded_template ' , $ embeddedTemplateName );
38+ $ this ->setAttribute ('embedded_index ' , $ embeddedTemplateIndex );
3039 $ this ->setAttribute ('component ' , $ component );
3140 }
3241
3342 public function compile (Compiler $ compiler ): void
3443 {
3544 $ compiler ->addDebugInfo ($ this );
3645
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+ */
3752 $ compiler
3853 ->write ('$preRendered = $this->extensions[ ' )
3954 ->string (ComponentExtension::class)
40- ->raw (']->preRender ( ' )
55+ ->raw (']->extensionPreCreateForRender ( ' )
4156 ->string ($ this ->getAttribute ('component ' ))
4257 ->raw (', ' )
4358 ->raw ('twig_to_array( ' )
44- ->subcompile ($ this ->getNode ('variables ' ))
59+ ;
60+ $ this ->writeProps ($ compiler )
4561 ->raw (') ' )
4662 ->raw ("); \n" )
4763 ;
@@ -58,32 +74,85 @@ public function compile(Compiler $compiler): void
5874 ->indent ()
5975 ;
6076
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+ */
6184 $ compiler
62- ->write ('$embeddedContext = $this->extensions[ ' )
85+ ->write ('$preRenderEvent = $this->extensions[ ' )
6386 ->string (ComponentExtension::class)
64- ->raw (']->embeddedContext ( ' )
87+ ->raw (']->startEmbeddedComponentRender ( ' )
6588 ->string ($ this ->getAttribute ('component ' ))
6689 ->raw (', twig_to_array( ' )
67- ->subcompile ($ this ->getNode ('variables ' ))
90+ ;
91+ $ this ->writeProps ($ compiler )
6892 ->raw ('), ' )
6993 ->raw ($ this ->getAttribute ('only ' ) ? '[] ' : '$context ' )
7094 ->raw (', ' )
71- ->string (TemplateNameParser::parse ($ this ->getAttribute ('name ' )))
95+ ->string (TemplateNameParser::parse ($ this ->getAttribute ('embedded_template ' )))
7296 ->raw (', ' )
73- ->raw ($ this ->getAttribute ('index ' ))
97+ ->raw ($ this ->getAttribute ('embedded_index ' ))
7498 ->raw ("); \n" )
7599 ;
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" );
76130
77131 $ compiler ->write ('$embeddedBlocks = $embeddedContext[ ' )
78132 ->string ('outerBlocks ' )
79133 ->raw (']->convert($blocks, ' )
80- ->raw ($ this ->getAttribute ('index ' ))
134+ ->raw ($ this ->getAttribute ('embedded_index ' ))
81135 ->raw ("); \n" )
82136 ;
83137
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+ ;
87156
88157 $ compiler ->write ('$this->extensions[ ' )
89158 ->string (ComponentExtension::class)
@@ -97,4 +166,13 @@ public function compile(Compiler $compiler): void
97166 ->raw ("\n" )
98167 ;
99168 }
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+ }
100178}
0 commit comments