Skip to content

Commit

Permalink
Allow co living components with discriminator attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
squrious committed Nov 9, 2023
1 parent aff83f2 commit 23d4b7c
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 19 deletions.
4 changes: 2 additions & 2 deletions src/LiveComponent/assets/dist/live_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2734,7 +2734,7 @@ function fromQueryString(search) {
const insertDotNotatedValueIntoData = (key, value, data) => {
const [first, second, ...rest] = key.split('.');
if (!second)
return data[key] = value;
return (data[key] = value);
if (data[first] === undefined) {
data[first] = Number.isNaN(Number.parseInt(second)) ? {} : [];
}
Expand Down Expand Up @@ -2819,7 +2819,7 @@ class QueryStringPlugin {
if (typeof value !== 'object') {
return false;
}
for (let key of Object.keys(value)) {
for (const key of Object.keys(value)) {
if (!this.isEmpty(value[key])) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default class implements PluginInterface {
return false;
}

for (let key of Object.keys(value)) {
for (const key of Object.keys(value)) {
if (!this.isEmpty(value[key])) {
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/LiveComponent/assets/src/url_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function fromQueryString(search: string) {
const [first, second, ...rest] = key.split('.');

// We're at a leaf node, let's make the assigment...
if (!second) return data[key] = value;
if (!second) return (data[key] = value);

// This is where we fill in empty arrays/objects along the way to the assigment...
if (data[first] === undefined) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\UX\LiveComponent\Metadata\LiveComponentMetadata;
use Symfony\UX\LiveComponent\Metadata\LiveComponentMetadataFactory;
use Symfony\UX\LiveComponent\Util\LiveControllerAttributesCreator;
use Symfony\UX\LiveComponent\Util\QueryStringPropsExtractor;
use Symfony\UX\TwigComponent\Event\PreMountEvent;

Expand Down Expand Up @@ -58,7 +59,11 @@ public function onPreMount(PreMountEvent $event): void

$request = $this->requestStack->getMainRequest();

$queryStringData = $this->queryStringPropsExtractor->extract($request, $metadata, $component);
$prefix = $data[LiveControllerAttributesCreator::URL_PREFIX_PROP_NAME]
?? $data[LiveControllerAttributesCreator::KEY_PROP_NAME]
?? '';

$queryStringData = $this->queryStringPropsExtractor->extract($request, $metadata, $component, $prefix);

$event->setData(array_merge($data, $queryStringData));
}
Expand Down
2 changes: 1 addition & 1 deletion src/LiveComponent/src/Metadata/LivePropMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public function allowsNull(): bool
}

/**
* @return array{'parameters': array<string,array{'property': string}>}
* @return array{'name': string}
*/
public function getQueryStringMapping(): array
{
Expand Down
35 changes: 24 additions & 11 deletions src/LiveComponent/src/Util/LiveControllerAttributesCreator.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class LiveControllerAttributesCreator
*/
public const KEY_PROP_NAME = 'key';

public const URL_PREFIX_PROP_NAME = 'query-param-prefix';

public function __construct(
private LiveComponentMetadataFactory $metadataFactory,
private LiveComponentHydrator $hydrator,
Expand Down Expand Up @@ -108,6 +110,28 @@ public function attributesForRendering(MountedComponent $mounted, ComponentMetad
}
}

$liveMetadata = $this->metadataFactory->getMetadata($mounted->getName());

if ($liveMetadata->hasQueryStringBindings()) {
$urlPrefix = $mountedAttributes->all()[self::URL_PREFIX_PROP_NAME]
?? $mounted->getInputProps()[self::KEY_PROP_NAME]
?? '';

$queryMapping = [];
foreach ($liveMetadata->getAllLivePropsMetadata() as $livePropMetadata) {
if ($mapping = $livePropMetadata->getQueryStringMapping()) {
$mapping['name'] = $urlPrefix.$mapping['name'];
$queryMapping[$livePropMetadata->getName()] = $mapping;
}
}
$attributesCollection->setQueryUrlMapping($queryMapping);

if ('' !== $urlPrefix) {
// So the prefix is also used when the component is re-rendered in live
$mountedAttributes = $mountedAttributes->defaults([self::URL_PREFIX_PROP_NAME => $urlPrefix]);
}
}

$dehydratedProps = $this->dehydrateComponent(
$mounted->getName(),
$mounted->getComponent(),
Expand All @@ -121,17 +145,6 @@ public function attributesForRendering(MountedComponent $mounted, ComponentMetad
);
}

$liveMetadata = $this->metadataFactory->getMetadata($mounted->getName());
if ($liveMetadata->hasQueryStringBindings()) {
$queryMapping = [];
foreach ($liveMetadata->getAllLivePropsMetadata() as $livePropMetadata) {
if ($mapping = $livePropMetadata->getQueryStringMapping()) {
$queryMapping[$livePropMetadata->getName()] = $mapping;
}
}
$attributesCollection->setQueryUrlMapping($queryMapping);
}

return $attributesCollection;
}

Expand Down
4 changes: 2 additions & 2 deletions src/LiveComponent/src/Util/QueryStringPropsExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function __construct(private readonly LiveComponentHydrator $hydrator)
{
}

public function extract(Request $request, LiveComponentMetadata $metadata, object $component): array
public function extract(Request $request, LiveComponentMetadata $metadata, object $component, string $prefix = ''): array
{
$query = $request->query->all();

Expand All @@ -39,7 +39,7 @@ public function extract(Request $request, LiveComponentMetadata $metadata, objec

foreach ($metadata->getAllLivePropsMetadata() as $livePropMetadata) {
if ($queryStringMapping = $livePropMetadata->getQueryStringMapping()) {
if (null !== ($value = $query[$queryStringMapping['name']] ?? null)) {
if (null !== ($value = $query[$prefix.$queryStringMapping['name']] ?? null)) {
if (\is_array($value) && $this->isNumericIndexedArray($value)) {
ksort($value);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{{ component('component_with_url_bound_props', {'id': 'component1', 'query-param-prefix': 'c1_'}) }}
{{ component('component_with_url_bound_props', {'id': 'component2', 'key': 'c2_'}) }}
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,35 @@ public function testQueryStringMappingAttribute()

$this->assertEquals($expected, $queryMapping);
}

public function testQueryStringMappingAttributeWithMultipleComponents()
{
$crawler = $this->browser()
->visit('/render-template/render_multiple_components_with_url_bound_props')
->assertSuccessful()
->crawler()
;
$component1 = $crawler->filter('div[id=component1]');
$this->assertEquals('c1_', $component1->attr('query-param-prefix'));
$queryMapping = json_decode($component1->attr('data-live-query-mapping-value'), true);
$expected = [
'prop1' => ['name' => 'c1_prop1'],
'prop2' => ['name' => 'c1_prop2'],
'prop3' => ['name' => 'c1_prop3'],
'prop5' => ['name' => 'c1_prop5'],
];

$this->assertEquals($expected, $queryMapping);

$component2 = $crawler->filter('div[id=component2]');
$this->assertNull($component2->attr('query-param-prefix'));
$queryMapping = json_decode($component2->attr('data-live-query-mapping-value'), true);
$expected = [
'prop1' => ['name' => 'c2_prop1'],
'prop2' => ['name' => 'c2_prop2'],
'prop3' => ['name' => 'c2_prop3'],
'prop5' => ['name' => 'c2_prop5'],
];
$this->assertEquals($expected, $queryMapping);
}
}

0 comments on commit 23d4b7c

Please sign in to comment.