Skip to content

[Twig] allow ExposeInTemplate to be used on public component methods #326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/TwigComponent/src/Attribute/ExposeInTemplate.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*
* @experimental
*/
#[\Attribute(\Attribute::TARGET_PROPERTY)]
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD)]
final class ExposeInTemplate
{
/**
Expand Down
17 changes: 17 additions & 0 deletions src/TwigComponent/src/ComponentRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,5 +109,22 @@ private function exposedVariables(object $component, bool $exposePublicProps): \

yield $attribute->name ?? $property->name => $value;
}

foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
if (!$attribute = $method->getAttributes(ExposeInTemplate::class)[0] ?? null) {
continue;
}

$attribute = $attribute->newInstance();

/** @var ExposeInTemplate $attribute */
$name = $attribute->name ?? (str_starts_with($method->name, 'get') ? lcfirst(substr($method->name, 3)) : $method->name);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be great if we could reuse the logic from Twig or PropertyAccessor or ApiPlatform to determine the right "name" to use... but I'm not aware of any way to do that.


if ($method->getNumberOfRequiredParameters()) {
throw new \LogicException(sprintf('Cannot use %s on methods with required parameters (%s::%s).', ExposeInTemplate::class, $component::class, $method->name));
}

yield $name => $component->{$method->name}();
}
}
}
27 changes: 25 additions & 2 deletions src/TwigComponent/src/Resources/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,16 @@ ExposeInTemplate Attribute

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

.. versionadded:: 2.3

The ``ExposeInTemplate`` attribute now be used on public methods.

All public component properties are available directly in your component
template. You can use the ``ExposeInTemplate`` attribute to expose
private/protected properties directly in a component template (``someProp``
vs ``this.someProp``). These properties must be *accessible* (have a getter).
private/protected properties and public methods directly in a component
template (``someProp`` vs ``this.someProp``, ``someMethod`` vs ``this.someMethod``).
Properties must be *accessible* (have a getter). Methods *cannot have*
required parameters.

.. code-block:: php

Expand Down Expand Up @@ -283,9 +289,26 @@ vs ``this.someProp``). These properties must be *accessible* (have a getter).
return $this->icon;
}

#[ExposeInTemplate]
public function getActions(): array // available as `{{ actions }}` in the template
{
// ...
}

#[ExposeInTemplate('dismissable')]
public function canBeDismissed(): bool // available as `{{ dismissable }}` in the template
{
// ...
}

// ...
}

.. note::

When using ``ExposeInTemplate`` on a method the value is fetched eagerly
before rendering.

PreMount Hook
~~~~~~~~~~~~~

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,22 @@ public function customGetter(): string
{
return $this->prop3;
}

#[ExposeInTemplate]
public function getMethod1(): string
{
return 'method1 value';
}

#[ExposeInTemplate]
public function method2(): string
{
return 'method2 value';
}

#[ExposeInTemplate('customPropMethod')]
public function customMethod(): string
{
return 'customMethod value';
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
Prop1: {{ prop1 }}
Prop2: {{ customProp2 }}
Prop3: {{ customProp3 }}
Method1: {{ method1 }}
Method2: {{ method2 }}
customMethod: {{ customPropMethod }}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ public function testRenderComponentWithExposedVariables(): void
$this->assertStringContainsString('Prop1: prop1 value', $output);
$this->assertStringContainsString('Prop2: prop2 value', $output);
$this->assertStringContainsString('Prop3: prop3 value', $output);
$this->assertStringContainsString('Method1: method1 value', $output);
$this->assertStringContainsString('Method2: method2 value', $output);
$this->assertStringContainsString('customMethod: customMethod value', $output);
}

public function testCanUseComputedMethods(): void
Expand Down