Skip to content

Commit 24ad943

Browse files
committed
[Twig] allow ExposeInTemplate to be used on public component methods
1 parent 09c9eff commit 24ad943

File tree

6 files changed

+67
-3
lines changed

6 files changed

+67
-3
lines changed

src/TwigComponent/src/Attribute/ExposeInTemplate.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*
2121
* @experimental
2222
*/
23-
#[\Attribute(\Attribute::TARGET_PROPERTY)]
23+
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD)]
2424
final class ExposeInTemplate
2525
{
2626
/**

src/TwigComponent/src/ComponentRenderer.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,5 +109,22 @@ private function exposedVariables(object $component, bool $exposePublicProps): \
109109

110110
yield $attribute->name ?? $property->name => $value;
111111
}
112+
113+
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
114+
if (!$attribute = $method->getAttributes(ExposeInTemplate::class)[0] ?? null) {
115+
continue;
116+
}
117+
118+
$attribute = $attribute->newInstance();
119+
120+
/** @var ExposeInTemplate $attribute */
121+
$name = $attribute->name ?? (str_starts_with($method->name, 'get') ? lcfirst(substr($method->name, 3)) : $method->name);
122+
123+
if ($method->getNumberOfRequiredParameters()) {
124+
throw new \LogicException(sprintf('Cannot use %s on methods with required parameters (%s::%s).', ExposeInTemplate::class, $component::class, $method->name));
125+
}
126+
127+
yield $name => $component->{$method->name}();
128+
}
112129
}
113130
}

src/TwigComponent/src/Resources/doc/index.rst

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,16 @@ ExposeInTemplate Attribute
237237

238238
The ``ExposeInTemplate`` attribute was added in TwigComponents 2.1.
239239

240+
.. versionadded:: 2.3
241+
242+
The ``ExposeInTemplate`` attribute now be used on public methods.
243+
240244
All public component properties are available directly in your component
241245
template. You can use the ``ExposeInTemplate`` attribute to expose
242-
private/protected properties directly in a component template (``someProp``
243-
vs ``this.someProp``). These properties must be *accessible* (have a getter).
246+
private/protected properties and public methods directly in a component
247+
template (``someProp`` vs ``this.someProp``, ``someMethod`` vs ``this.someMethod``).
248+
Properties must be *accessible* (have a getter). Methods *cannot have*
249+
required parameters.
244250

245251
.. code-block:: php
246252
@@ -283,9 +289,26 @@ vs ``this.someProp``). These properties must be *accessible* (have a getter).
283289
return $this->icon;
284290
}
285291
292+
#[ExposeInTemplate]
293+
public function getActions(): array // available as `{{ actions }}` in the template
294+
{
295+
// ...
296+
}
297+
298+
#[ExposeInTemplate('dismissable')]
299+
public function canBeDismissed(): bool // available as `{{ dismissable }}` in the template
300+
{
301+
// ...
302+
}
303+
286304
// ...
287305
}
288306
307+
.. note::
308+
309+
When using ``ExposeInTemplate`` on a method the value is fetched eagerly
310+
before rendering.
311+
289312
PreMount Hook
290313
~~~~~~~~~~~~~
291314

src/TwigComponent/tests/Fixtures/Component/WithExposedVariables.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,22 @@ public function customGetter(): string
3434
{
3535
return $this->prop3;
3636
}
37+
38+
#[ExposeInTemplate]
39+
public function getMethod1(): string
40+
{
41+
return 'method1 value';
42+
}
43+
44+
#[ExposeInTemplate]
45+
public function method2(): string
46+
{
47+
return 'method2 value';
48+
}
49+
50+
#[ExposeInTemplate('customPropMethod')]
51+
public function customMethod(): string
52+
{
53+
return 'customMethod value';
54+
}
3755
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
Prop1: {{ prop1 }}
22
Prop2: {{ customProp2 }}
33
Prop3: {{ customProp3 }}
4+
Method1: {{ method1 }}
5+
Method2: {{ method2 }}
6+
customMethod: {{ customPropMethod }}

src/TwigComponent/tests/Integration/ComponentExtensionTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ public function testRenderComponentWithExposedVariables(): void
9696
$this->assertStringContainsString('Prop1: prop1 value', $output);
9797
$this->assertStringContainsString('Prop2: prop2 value', $output);
9898
$this->assertStringContainsString('Prop3: prop3 value', $output);
99+
$this->assertStringContainsString('Method1: method1 value', $output);
100+
$this->assertStringContainsString('Method2: method2 value', $output);
101+
$this->assertStringContainsString('customMethod: customMethod value', $output);
99102
}
100103

101104
public function testCanUseComputedMethods(): void

0 commit comments

Comments
 (0)