diff --git a/src/LiveComponent/CHANGELOG.md b/src/LiveComponent/CHANGELOG.md index 15450cf54a9..0f21bfead6d 100644 --- a/src/LiveComponent/CHANGELOG.md +++ b/src/LiveComponent/CHANGELOG.md @@ -6,6 +6,8 @@ - Allow multiple `LiveListener` attributes on a single method. - Requests to LiveComponent are sent as POST by default - Add method prop to AsLiveComponent to still allow GET requests, usage: `#[AsLiveComponent(method: 'get')]` +- Add a new `urlReferenceType` parameter to `AsLiveComponent`, which allows to + generate different type URL (e.g. absolute) for the component Ajax calls. ## 2.13.2 diff --git a/src/LiveComponent/doc/index.rst b/src/LiveComponent/doc/index.rst index 4900cf23bb2..f8874af5b8e 100644 --- a/src/LiveComponent/doc/index.rst +++ b/src/LiveComponent/doc/index.rst @@ -3279,6 +3279,26 @@ Then specify this new route on your component: use DefaultActionTrait; } +.. versionadded:: 2.14 + + The ``urlReferenceType`` option was added in LiveComponents 2.14. + +You can also control the type of the generated URL: + +.. code-block:: diff + + // src/Components/RandomNumber.php + + use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + use Symfony\UX\LiveComponent\Attribute\AsLiveComponent; + use Symfony\UX\LiveComponent\DefaultActionTrait; + + - #[AsLiveComponent] + + #[AsLiveComponent(urlReferenceType: UrlGeneratorInterface::ABSOLUTE_URL)] + class RandomNumber + { + use DefaultActionTrait; + } + Add a Hook on LiveProp Update ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/LiveComponent/src/Attribute/AsLiveComponent.php b/src/LiveComponent/src/Attribute/AsLiveComponent.php index bde5f9fb807..991b60459b7 100644 --- a/src/LiveComponent/src/Attribute/AsLiveComponent.php +++ b/src/LiveComponent/src/Attribute/AsLiveComponent.php @@ -11,6 +11,7 @@ namespace Symfony\UX\LiveComponent\Attribute; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; /** @@ -33,6 +34,7 @@ final class AsLiveComponent extends AsTwigComponent * @param string $attributesVar The name of the special "attributes" variable in the template * @param bool $csrf Whether to enable CSRF protection (default: true) * @param string $route The route used to render the component & handle actions (default: ux_live_component) + * @param int $urlReferenceType Which type of URL should be generated for the given route. Use the constants from UrlGeneratorInterface (default: absolute path, e.g. "/dir/file"). */ public function __construct( string $name = null, @@ -43,6 +45,7 @@ public function __construct( public bool $csrf = true, public string $route = 'ux_live_component', public string $method = 'post', + public int $urlReferenceType = UrlGeneratorInterface::ABSOLUTE_PATH, ) { parent::__construct($name, $template, $exposePublicProps, $attributesVar); @@ -64,6 +67,7 @@ public function serviceConfig(): array 'csrf' => $this->csrf, 'route' => $this->route, 'method' => $this->method, + 'url_reference_type' => $this->urlReferenceType, ]); } diff --git a/src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php b/src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php index 1f34352e306..97c48d75d5e 100644 --- a/src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php +++ b/src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php @@ -89,7 +89,7 @@ public function load(array $configs, ContainerBuilder $container): void AsLiveComponent::class, function (ChildDefinition $definition, AsLiveComponent $attribute) { $definition - ->addTag('twig.component', array_filter($attribute->serviceConfig())) + ->addTag('twig.component', array_filter($attribute->serviceConfig(), static fn ($v) => null !== $v && '' !== $v)) ->addTag('controller.service_arguments') ; } diff --git a/src/LiveComponent/src/Twig/LiveComponentRuntime.php b/src/LiveComponent/src/Twig/LiveComponentRuntime.php index 69f608711c0..11ffc40d814 100644 --- a/src/LiveComponent/src/Twig/LiveComponentRuntime.php +++ b/src/LiveComponent/src/Twig/LiveComponentRuntime.php @@ -45,6 +45,6 @@ public function getComponentUrl(string $name, array $props = []): string $metadata = $this->factory->metadataFor($mounted->getName()); - return $this->urlGenerator->generate($metadata->get('route'), $params); + return $this->urlGenerator->generate($metadata->get('route'), $params, $metadata->get('url_reference_type')); } } diff --git a/src/LiveComponent/src/Util/LiveControllerAttributesCreator.php b/src/LiveComponent/src/Util/LiveControllerAttributesCreator.php index 5ea730d3ec8..29f25dd35e9 100644 --- a/src/LiveComponent/src/Util/LiveControllerAttributesCreator.php +++ b/src/LiveComponent/src/Util/LiveControllerAttributesCreator.php @@ -61,7 +61,7 @@ public function attributesForRendering(MountedComponent $mounted, ComponentMetad $attributesCollection = $this->attributeHelper->create(); $attributesCollection->setLiveController($mounted->getName()); - $url = $this->urlGenerator->generate($metadata->get('route'), ['_live_component' => $mounted->getName()]); + $url = $this->urlGenerator->generate($metadata->get('route'), ['_live_component' => $mounted->getName()], $metadata->get('url_reference_type')); $attributesCollection->setUrl($url); $liveListeners = AsLiveComponent::liveListeners($mounted->getComponent()); diff --git a/src/LiveComponent/tests/Fixtures/Component/WithAbsoluteUrl.php b/src/LiveComponent/tests/Fixtures/Component/WithAbsoluteUrl.php new file mode 100644 index 00000000000..2068dc44521 --- /dev/null +++ b/src/LiveComponent/tests/Fixtures/Component/WithAbsoluteUrl.php @@ -0,0 +1,27 @@ + + */ +#[AsLiveComponent('with_absolute_url', urlReferenceType: UrlGeneratorInterface::ABSOLUTE_URL)] +final class WithAbsoluteUrl +{ + use DefaultActionTrait; + + #[LiveProp(writable: true, url: true)] + public int $count = 0; + + #[LiveAction] + public function increase(): void + { + ++$this->count; + } +} diff --git a/src/LiveComponent/tests/Fixtures/templates/component_url.html.twig b/src/LiveComponent/tests/Fixtures/templates/component_url.html.twig index 50d382f8691..fa4ea11cd7e 100644 --- a/src/LiveComponent/tests/Fixtures/templates/component_url.html.twig +++ b/src/LiveComponent/tests/Fixtures/templates/component_url.html.twig @@ -1,2 +1,3 @@ {{ component_url('component1', {prop1: null, prop2: date}) }} {{ component_url('alternate_route') }} +{{ component_url('with_absolute_url') }} diff --git a/src/LiveComponent/tests/Fixtures/templates/components/with_absolute_url.html.twig b/src/LiveComponent/tests/Fixtures/templates/components/with_absolute_url.html.twig new file mode 100644 index 00000000000..64d5c816c1a --- /dev/null +++ b/src/LiveComponent/tests/Fixtures/templates/components/with_absolute_url.html.twig @@ -0,0 +1,3 @@ + + From absolute url. Count: {{ count }} + diff --git a/src/LiveComponent/tests/Fixtures/templates/render_with_absolute_url.html.twig b/src/LiveComponent/tests/Fixtures/templates/render_with_absolute_url.html.twig new file mode 100644 index 00000000000..3cd678078c6 --- /dev/null +++ b/src/LiveComponent/tests/Fixtures/templates/render_with_absolute_url.html.twig @@ -0,0 +1 @@ +{{ component('with_absolute_url') }} diff --git a/src/LiveComponent/tests/Functional/EventListener/AddLiveAttributesSubscriberTest.php b/src/LiveComponent/tests/Functional/EventListener/AddLiveAttributesSubscriberTest.php index b6019f1f9ce..7162f7fc7c4 100644 --- a/src/LiveComponent/tests/Functional/EventListener/AddLiveAttributesSubscriberTest.php +++ b/src/LiveComponent/tests/Functional/EventListener/AddLiveAttributesSubscriberTest.php @@ -12,6 +12,7 @@ namespace Symfony\UX\LiveComponent\Tests\Functional\EventListener; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Symfony\Component\DomCrawler\Crawler; use Symfony\UX\LiveComponent\Tests\LiveComponentTestHelper; use Zenstruck\Browser\Test\HasBrowser; @@ -154,4 +155,62 @@ public function testQueryStringMappingAttribute() $this->assertEquals($expected, $queryMapping); } + + public function testAbsoluteUrl(): void + { + $div = $this->browser() + ->visit('/render-template/render_with_absolute_url') + ->assertSuccessful() + ->assertContains('Count: 0') + ->crawler() + ->filter('div') + ; + + $props = json_decode($div->attr('data-live-props-value'), true); + + $this->assertSame('live', $div->attr('data-controller')); + $this->assertSame('http://localhost/_components/with_absolute_url', $div->attr('data-live-url-value')); + $this->assertNotNull($div->attr('data-live-csrf-value')); + $this->assertCount(3, $props); + $this->assertArrayHasKey('@checksum', $props); + $this->assertArrayHasKey('@attributes', $props); + $this->assertArrayHasKey('data-live-id', $props['@attributes']); + $this->assertArrayHasKey('count', $props); + $this->assertSame($props['count'], 0); + } + + public function testAbsoluteUrlWithLiveQueryProp() + { + $token = null; + $props = []; + $div = $this->browser() + ->get('/render-template/render_with_absolute_url?count=1') + ->assertSuccessful() + ->assertContains('Count: 1') + ->use(function (Crawler $crawler) use (&$token, &$props) { + $div = $crawler->filter('div')->first(); + $token = $div->attr('data-live-csrf-value'); + $props = json_decode($div->attr('data-live-props-value'), true); + }) + ->post('http://localhost/_components/with_absolute_url/increase', [ + 'headers' => ['X-CSRF-TOKEN' => $token], + 'body' => ['data' => json_encode(['props' => $props])], + ]) + ->assertContains('Count: 2') + ->crawler() + ->filter('div') + ; + + $props = json_decode($div->attr('data-live-props-value'), true); + + $this->assertSame('live', $div->attr('data-controller')); + $this->assertSame('http://localhost/_components/with_absolute_url', $div->attr('data-live-url-value')); + $this->assertNotNull($div->attr('data-live-csrf-value')); + $this->assertCount(3, $props); + $this->assertArrayHasKey('@checksum', $props); + $this->assertArrayHasKey('@attributes', $props); + $this->assertArrayHasKey('data-live-id', $props['@attributes']); + $this->assertArrayHasKey('count', $props); + $this->assertSame($props['count'], 2); + } } diff --git a/src/LiveComponent/tests/Integration/Twig/LiveComponentExtensionTest.php b/src/LiveComponent/tests/Integration/Twig/LiveComponentExtensionTest.php index 5d34a3f0eef..57d253e60a4 100644 --- a/src/LiveComponent/tests/Integration/Twig/LiveComponentExtensionTest.php +++ b/src/LiveComponent/tests/Integration/Twig/LiveComponentExtensionTest.php @@ -26,5 +26,6 @@ public function testGetComponentUrl(): void $this->assertStringContainsString('/_components/component1?props=%7B%22prop1%22:null,%22prop2%22:%222022-10-06T00:00:00%2B00:00%22,%22prop3%22:null,', $rendered); $this->assertStringContainsString('/alt/alternate_route?', $rendered); + $this->assertStringContainsString('http://localhost/_components/with_absolute_url?', $rendered); } }