Skip to content

Commit c05aff1

Browse files
committed
feature #2117 [Map] Render map from Twig with ux_map() and <twig:ux:map /> (smnandre)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- [Map] Render map from Twig with `ux_map()` and `<twig:ux:map />` | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Issues | Fix #... | License | MIT * ~~add an MapFactory (internal)~~ * allow `ux_map` Twig function to render map from named arguments * add basic TwigComponent `<twig:ux:map ... />` ### Create Map from Twig ```twig {{ ux_map( center: [51.5074, 0.1278], zoom: 3, markers: [ { position: [51.5074, 0.1278], title: 'London' }, { position: [48.8566, 2.3522], title: 'Paris' }, { position: [40.7128, -74.0060], title: 'New York', infoWindow: { content: 'Welcom to <b>New York</b>' } }, ], attributes: { class: 'foo', style: 'height: 800px; width: 100%; border: 4px solid red; margin-block: 10vh;', } ) }} ``` ![ux-map](https://github.com/user-attachments/assets/2d127863-c159-4024-9266-c12609460534) Commits ------- 92f89d3 [Map] Render map from Twig with `ux_map()` and `<twig:ux:map />`
2 parents 2b6c5d1 + 92f89d3 commit c05aff1

17 files changed

+632
-9
lines changed

src/Map/CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
## 2.20
44

5-
- Rename `render_map` Twig function `ux_map`
6-
- Deprecate `render_map` Twig function
5+
- Deprecate `render_map` Twig function (will be removed in 2.21). Use
6+
`ux_map` or the `<twig:ux:map />` Twig component instead.
7+
- Add `ux_map` Twig function (replaces `render_map` with a more flexible
8+
interface)
9+
- Add `<twig:ux:map />` Twig component
710

811
## 2.19
912

src/Map/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
"symfony/asset-mapper": "^6.4|^7.0",
4040
"symfony/framework-bundle": "^6.4|^7.0",
4141
"symfony/phpunit-bridge": "^6.4|^7.0",
42-
"symfony/twig-bundle": "^6.4|^7.0"
42+
"symfony/twig-bundle": "^6.4|^7.0",
43+
"symfony/ux-twig-component": "^2.18"
4344
},
4445
"extra": {
4546
"thanks": {

src/Map/config/services.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\UX\Map\Renderer\Renderer;
1616
use Symfony\UX\Map\Renderer\Renderers;
1717
use Symfony\UX\Map\Twig\MapExtension;
18+
use Symfony\UX\Map\Twig\MapRuntime;
1819

1920
/*
2021
* @author Hugo Alliaume <[email protected]>
@@ -26,7 +27,6 @@
2627
->args([
2728
abstract_arg('renderers configuration'),
2829
])
29-
->tag('twig.runtime')
3030

3131
->set('ux_map.renderer_factory.abstract', AbstractRendererFactory::class)
3232
->abstract()
@@ -41,5 +41,11 @@
4141

4242
->set('ux_map.twig_extension', MapExtension::class)
4343
->tag('twig.extension')
44+
45+
->set('ux_map.twig_runtime', MapRuntime::class)
46+
->args([
47+
service('ux_map.renderers'),
48+
])
49+
->tag('twig.runtime')
4450
;
4551
};

src/Map/config/twig_component.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
13+
14+
use Symfony\UX\Map\Twig\UXMapComponent;
15+
use Symfony\UX\Map\Twig\UXMapComponentListener;
16+
use Symfony\UX\TwigComponent\Event\PreCreateForRenderEvent;
17+
18+
return static function (ContainerConfigurator $container): void {
19+
$container->services()
20+
->set('.ux_map.twig_component_listener', UXMapComponentListener::class)
21+
->args([
22+
service('ux_map.twig_runtime'),
23+
])
24+
->tag('kernel.event_listener', [
25+
'event' => PreCreateForRenderEvent::class,
26+
'method' => 'onPreCreateForRender',
27+
])
28+
29+
->set('.ux_map.twig_component.map', UXMapComponent::class)
30+
->tag('twig.component', ['key' => 'UX:Map'])
31+
;
32+
};

src/Map/src/InfoWindow.php

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
final readonly class InfoWindow
2020
{
2121
/**
22-
* @param array<string, mixed> $extra Extra data, can be used by the developer to store additional information and use them later JavaScript side
22+
* @param array<string, mixed> $extra Extra data, can be used by the developer to store additional information and
23+
* use them later JavaScript side
2324
*/
2425
public function __construct(
2526
private ?string $headerContent = null,
@@ -31,6 +32,16 @@ public function __construct(
3132
) {
3233
}
3334

35+
/**
36+
* @return array{
37+
* headerContent: string|null,
38+
* content: string|null,
39+
* position: array{lat: float, lng: float}|null,
40+
* opened: bool,
41+
* autoClose: bool,
42+
* extra: object,
43+
* }
44+
*/
3445
public function toArray(): array
3546
{
3647
return [
@@ -42,4 +53,25 @@ public function toArray(): array
4253
'extra' => (object) $this->extra,
4354
];
4455
}
56+
57+
/**
58+
* @param array{
59+
* headerContent: string|null,
60+
* content: string|null,
61+
* position: array{lat: float, lng: float}|null,
62+
* opened: bool,
63+
* autoClose: bool,
64+
* extra: object,
65+
* } $data
66+
*
67+
* @internal
68+
*/
69+
public static function fromArray(array $data): self
70+
{
71+
if (isset($data['position'])) {
72+
$data['position'] = Point::fromArray($data['position']);
73+
}
74+
75+
return new self(...$data);
76+
}
4577
}

src/Map/src/Map.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,36 @@ public function toArray(): array
103103
'markers' => array_map(static fn (Marker $marker) => $marker->toArray(), $this->markers),
104104
];
105105
}
106+
107+
/**
108+
* @param array{
109+
* center?: array{lat: float, lng: float},
110+
* zoom?: float,
111+
* markers?: list<array>,
112+
* fitBoundsToMarkers?: bool,
113+
* options?: object,
114+
* } $map
115+
*
116+
* @internal
117+
*/
118+
public static function fromArray(array $map): self
119+
{
120+
$map['fitBoundsToMarkers'] = true;
121+
122+
if (isset($map['center'])) {
123+
$map['center'] = Point::fromArray($map['center']);
124+
}
125+
126+
if (isset($map['zoom']) || isset($map['center'])) {
127+
$map['fitBoundsToMarkers'] = false;
128+
}
129+
130+
$map['markers'] ??= [];
131+
if (!\is_array($map['markers'])) {
132+
throw new InvalidArgumentException('The "markers" parameter must be an array.');
133+
}
134+
$map['markers'] = array_map(Marker::fromArray(...), $map['markers']);
135+
136+
return new self(...$map);
137+
}
106138
}

src/Map/src/Marker.php

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\UX\Map;
1313

14+
use Symfony\UX\Map\Exception\InvalidArgumentException;
15+
1416
/**
1517
* Represents a marker on a map.
1618
*
@@ -19,7 +21,8 @@
1921
final readonly class Marker
2022
{
2123
/**
22-
* @param array<string, mixed> $extra Extra data, can be used by the developer to store additional information and use them later JavaScript side
24+
* @param array<string, mixed> $extra Extra data, can be used by the developer to store additional information and
25+
* use them later JavaScript side
2326
*/
2427
public function __construct(
2528
private Point $position,
@@ -29,6 +32,14 @@ public function __construct(
2932
) {
3033
}
3134

35+
/**
36+
* @return array{
37+
* position: array{lat: float, lng: float},
38+
* title: string|null,
39+
* infoWindow: array<string, mixed>|null,
40+
* extra: object,
41+
* }
42+
*/
3243
public function toArray(): array
3344
{
3445
return [
@@ -38,4 +49,28 @@ public function toArray(): array
3849
'extra' => (object) $this->extra,
3950
];
4051
}
52+
53+
/**
54+
* @param array{
55+
* position: array{lat: float, lng: float},
56+
* title: string|null,
57+
* infoWindow: array<string, mixed>|null,
58+
* extra: object,
59+
* } $marker
60+
*
61+
* @internal
62+
*/
63+
public static function fromArray(array $marker): self
64+
{
65+
if (!isset($marker['position'])) {
66+
throw new InvalidArgumentException('The "position" parameter is required.');
67+
}
68+
$marker['position'] = Point::fromArray($marker['position']);
69+
70+
if (isset($marker['infoWindow'])) {
71+
$marker['infoWindow'] = InfoWindow::fromArray($marker['infoWindow']);
72+
}
73+
74+
return new self(...$marker);
75+
}
4176
}

src/Map/src/Point.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,16 @@ public function toArray(): array
4343
'lng' => $this->longitude,
4444
];
4545
}
46+
47+
/**
48+
* @param array{lat: float, lng: float}|array{0: float, 1: float} $point
49+
*/
50+
public static function fromArray(array $point): self
51+
{
52+
if (isset($point['lat'], $point['lng'])) {
53+
return new self($point['lat'], $point['lng']);
54+
}
55+
56+
return new self(...$point);
57+
}
4658
}

src/Map/src/Twig/MapExtension.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace Symfony\UX\Map\Twig;
1313

14-
use Symfony\UX\Map\Renderer\Renderers;
1514
use Twig\DeprecatedCallableInfo;
1615
use Twig\Extension\AbstractExtension;
1716
use Twig\TwigFunction;
@@ -26,13 +25,13 @@ final class MapExtension extends AbstractExtension
2625
public function getFunctions(): array
2726
{
2827
return [
29-
new TwigFunction('render_map', [Renderers::class, 'renderMap'], [
28+
new TwigFunction('render_map', [MapRuntime::class, 'renderMap'], [
3029
'is_safe' => ['html'],
3130
...(class_exists(DeprecatedCallableInfo::class)
3231
? ['deprecation_info' => new DeprecatedCallableInfo('symfony/ux-map', '2.20', 'ux_map')]
3332
: ['deprecated' => '2.20', 'deprecating_package' => 'symfony/ux-map', 'alternative' => 'ux_map']),
3433
]),
35-
new TwigFunction('ux_map', [Renderers::class, 'renderMap'], ['is_safe' => ['html']]),
34+
new TwigFunction('ux_map', [MapRuntime::class, 'renderMap'], ['is_safe' => ['html']]),
3635
];
3736
}
3837
}

src/Map/src/Twig/MapRuntime.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\Map\Twig;
13+
14+
use Symfony\UX\Map\Map;
15+
use Symfony\UX\Map\Marker;
16+
use Symfony\UX\Map\Point;
17+
use Symfony\UX\Map\Renderer\RendererInterface;
18+
use Twig\Extension\RuntimeExtensionInterface;
19+
20+
/**
21+
* @author Simon André <[email protected]>
22+
*
23+
* @internal
24+
*/
25+
final class MapRuntime implements RuntimeExtensionInterface
26+
{
27+
public function __construct(
28+
private readonly RendererInterface $renderer,
29+
) {
30+
}
31+
32+
/**
33+
* @param array<string, mixed> $attributes
34+
* @param array<string, mixed> $markers
35+
*/
36+
public function renderMap(
37+
?Map $map = null,
38+
array $attributes = [],
39+
?array $markers = null,
40+
?array $center = null,
41+
?float $zoom = null,
42+
): string {
43+
if ($map instanceof Map) {
44+
if (null !== $center || null !== $zoom || $markers) {
45+
throw new \InvalidArgumentException('You cannot set "center", "markers" or "zoom" on an existing Map.');
46+
}
47+
48+
return $this->renderer->renderMap($map, $attributes);
49+
}
50+
51+
$map = new Map();
52+
foreach ($markers ?? [] as $marker) {
53+
$map->addMarker(Marker::fromArray($marker));
54+
}
55+
if (null !== $center) {
56+
$map->center(Point::fromArray($center));
57+
}
58+
if (null !== $zoom) {
59+
$map->zoom($zoom);
60+
}
61+
62+
return $this->renderer->renderMap($map, $attributes);
63+
}
64+
}

src/Map/src/Twig/UXMapComponent.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\Map\Twig;
13+
14+
use Symfony\UX\Map\Marker;
15+
use Symfony\UX\Map\Point;
16+
17+
/**
18+
* @author Simon André <[email protected]>
19+
*
20+
* @internal
21+
*/
22+
final class UXMapComponent
23+
{
24+
public ?float $zoom;
25+
26+
public ?Point $center;
27+
28+
/**
29+
* @var Marker[]
30+
*/
31+
public array $markers;
32+
}

0 commit comments

Comments
 (0)