关于 ux-map
UX Map 是用于 Symfony 的交互式地图组件,可通过 PHP 定义地图并在 Twig 中渲染。它支持 Leaflet 与 Google Maps 渲染器,并可结合 Stimulus 和 LiveComponent 扩展。
主要功能
- 01PHP 地图对象与 Twig 渲染
- 02支持标记、形状与信息窗口
- 03Leaflet 和 Google Maps 渲染器
- 04Stimulus 地图事件
- 05LiveComponent 响应式集成
适用场景
- 01在 Symfony 页面中展示位置标记
- 02绘制多边形、线条和圆形区域
- 03监听地图元素创建事件
- 04构建可响应更新的地图组件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ smnandre/ symfony-ux-skills --skill ux-map安装后在 Claude Code 中输入/ux-map确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/api.mdgotchas.mdpatterns.md详细介绍
UX Map
在 Symfony 中使用 Leaflet 或 Google Maps 创建交互式地图。使用 PHP 构建地图,在 Twig 中渲染,并通过 Stimulus 控制器扩展功能。支持标记、多边形、折线、圆形、信息窗口和 LiveComponent 集成。
安装
# Install the base package
composer require symfony/ux-map
# Then install ONE renderer:
composer require symfony/ux-leaflet-map # Leaflet (free, open source)
# OR
composer require symfony/ux-google-map # Google Maps (requires API key)
快速参考
Map PHP object, holds markers/shapes/options
Point latitude + longitude
Marker pin on the map, optional InfoWindow
Polygon closed shape (array of Points)
Polyline open line (array of Points)
Circle center Point + radius in meters
InfoWindow popup attached to a Marker, Polygon, or Circle
ux_map(map, attrs) Twig function to render a Map
<twig:ux:map /> Twig component (requires ux-twig-component)
在 PHP 中构建地图
use Symfony\UX\Map\Map;
use Symfony\UX\Map\Point;
use Symfony\UX\Map\Marker;
use Symfony\UX\Map\InfoWindow;
use Symfony\UX\Map\Polygon;
use Symfony\UX\Map\Polyline;
use Symfony\UX\Map\Circle;
$map = (new Map())
->center(new Point(48.8566, 2.3522))
->zoom(12)
->minZoom(3)
->maxZoom(18);
// Marker with info window
$map->addMarker(new Marker(
position: new Point(48.8566, 2.3522),
title: 'Paris',
infoWindow: new InfoWindow(
headerContent: '<b>Paris</b>',
content: 'The capital of France',
),
));
// Marker with custom icon (UX Icons integration)
$map->addMarker(new Marker(
position: new Point(48.8738, 2.2950),
title: 'Eiffel Tower',
icon: Icon::ux('mdi:tower-eiffel')->width(32)->height(32),
extra: ['category' => 'landmark'],
));
// Polygon (closed area)
$map->addPolygon(new Polygon(
points: [
new Point(48.8566, 2.3522),
new Point(48.8606, 2.3376),
new Point(48.8530, 2.3499),
],
infoWindow: new InfoWindow(content: 'Central Paris area'),
));
// Polyline (open line)
$map->addPolyline(new Polyline(
points: [
new Point(48.8566, 2.3522),
new Point(48.8738, 2.2950),
],
));
// Circle (center + radius in meters)
$map->addCircle(new Circle(
center: new Point(48.8566, 2.3522),
radius: 500,
infoWindow: new InfoWindow(content: '500m radius'),
));
// Auto-fit bounds to show all markers
$map->fitBoundsToMarkers();
在 Twig 中渲染
Twig 函数
{# Basic rendering #}
{{ ux_map(map, {style: 'height: 400px; width: 100%;'}) }}
{# With custom attributes and Stimulus controller #}
{{ ux_map(map, {
'data-controller': 'custom-map',
style: 'height: 400px;',
class: 'rounded shadow'
}) }}
Twig 组件(HTML 语法)
需要 symfony/ux-twig-component。无需 PHP 即可直接定义地图:
<twig:ux:map
:center="[48.8566, 2.3522]"
zoom="12"
:markers='[
{"position": [48.8566, 2.3522], "title": "Paris"},
{"position": [48.8738, 2.2950], "title": "Eiffel Tower",
"infoWindow": {"content": "324m tall"}}
]'
:fitBoundsToMarkers="true"
style="height: 400px; width: 100%;"
class="rounded shadow"
/>
配置
# config/packages/ux_map.yaml
ux_map:
renderer: '%env(resolve:default::UX_MAP_DSN)%'
# Google Maps specific
google_maps:
default_map_id: null # Optional: default Map ID for all maps
渲染器 DSN
在 .env 中设置:
# Leaflet (free)
UX_MAP_DSN=leaflet://default
# Google Maps (requires API key)
UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default
渲染器选项
Leaflet 选项
use Symfony\UX\Map\Bridge\Leaflet\LeafletOptions;
use Symfony\UX\Map\Bridge\Leaflet\Option\TileLayer;
use Symfony\UX\Map\Bridge\Leaflet\Option\ZoomControlOptions;
use Symfony\UX\Map\Bridge\Leaflet\Option\AttributionControlOptions;
use Symfony\UX\Map\Bridge\Leaflet\Option\ControlPosition;
$leafletOptions = (new LeafletOptions())
->tileLayer(new TileLayer(
url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
attribution: '© OpenStreetMap contributors',
options: ['minZoom' => 5, 'maxZoom' => 18],
))
->zoomControl(true)
->zoomControlOptions(new ZoomControlOptions(ControlPosition::TOP_LEFT))
->attributionControl(true)
->attributionControlOptions(new AttributionControlOptions(ControlPosition::BOTTOM_LEFT));
$map->options($leafletOptions);
Google Maps 选项
use Symfony\UX\Map\Bridge\Google\GoogleOptions;
use Symfony\UX\Map\Bridge\Google\Option\ControlPosition;
use Symfony\UX\Map\Bridge\Google\Option\GestureHandling;
use Symfony\UX\Map\Bridge\Google\Option\MapTypeControlOptions;
use Symfony\UX\Map\Bridge\Google\Option\MapTypeControlStyle;
use Symfony\UX\Map\Bridge\Google\Option\ZoomControlOptions;
use Symfony\UX\Map\Bridge\Google\Option\StreetViewControlOptions;
use Symfony\UX\Map\Bridge\Google\Option\FullscreenControlOptions;
$googleOptions = (new GoogleOptions())
->mapId('YOUR_MAP_ID')
->gestureHandling(GestureHandling::GREEDY)
->backgroundColor('#f00')
->doubleClickZoom(true)
->zoomControlOptions(new ZoomControlOptions(
position: ControlPosition::BLOCK_START_INLINE_END,
))
->mapTypeControlOptions(new MapTypeControlOptions(
mapTypeIds: ['roadmap'],
position: ControlPosition::INLINE_END_BLOCK_START,
style: MapTypeControlStyle::DROPDOWN_MENU,
))
->streetViewControlOptions(new StreetViewControlOptions(
position: ControlPosition::BLOCK_END_INLINE_START,
))
->fullscreenControlOptions(new FullscreenControlOptions(
position: ControlPosition::INLINE_START_BLOCK_END,
));
$map->options($googleOptions);
禁用控件:
$googleOptions = (new GoogleOptions())
->mapId('YOUR_MAP_ID')
->zoomControl(false)
->mapTypeControl(false)
->streetViewControl(false)
->fullscreenControl(false);
地图事件(Stimulus)
附加自定义 Stimulus 控制器,与底层地图交互:
// assets/controllers/custom-map_controller.js
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
connect() {
this.element.addEventListener('ux:map:connect', this._onConnect.bind(this));
this.element.addEventListener('ux:map:marker:after-create', this._onMarkerCreated.bind(this));
}
_onConnect(event) {
const { map, markers } = event.detail;
console.log('Map ready:', map);
}
_onMarkerCreated(event) {
const { marker, definition } = event.detail;
if (definition.extra?.category === 'landmark') {
console.log('Landmark marker created');
}
}
}
{{ ux_map(map, {'data-controller': 'custom-map', style: 'height: 400px;'}) }}
可用事件
| 事件 | 详情 | 描述 |
|---|---|---|
ux:map:connect | {map, markers} | 地图已初始化并就绪 |
ux:map:marker:before-create | {definition} | 添加标记之前 |
ux:map:marker:after-create | {marker, definition} | 添加标记之后 |
ux:map:polygon:before-create | {definition} | 添加多边形之前 |
ux:map:polygon:after-create | {polygon, definition} | 添加多边形之后 |
ux:map:polyline:before-create | {definition} | 添加折线之前 |
ux:map:polyline:after-create | {polyline, definition} | 添加折线之后 |
ux:map:circle:before-create | {definition} | 添加圆形之前 |
ux:map:circle:after-create | {circle, definition} | 添加圆形之后 |
ux:map:info-window:before-create | {definition} | 创建信息窗口之前 |
ux:map:info-window:after-create | {infoWindow, definition} | 创建信息窗口之后 |
LiveComponent 集成
使用 ComponentWithMapTrait 让地图具备响应性:
namespace App\Twig\Components;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;
use Symfony\UX\Map\InfoWindow;
use Symfony\UX\Map\Live\ComponentWithMapTrait;
use Symfony\UX\Map\Map;
use Symfony\UX\Map\Marker;
use Symfony\UX\Map\Point;
#[AsLiveComponent]
final class MapSearch
{
use DefaultActionTrait;
use ComponentWithMapTrait;
#[LiveProp(writable: true)]
public string $query = '';
protected function instantiateMap(): Map
{
$map = (new Map())
->center(new Point(48.8566, 2.3522))
->zoom(12)
->fitBoundsToMarkers();
foreach ($this->getFilteredLocations() as $location) {
$map->addMarker(new Marker(
position: new Point($location->lat, $location->lng),
title: $location->name,
infoWindow: new InfoWindow($location->name),
));
}
return $map;
}
private function getFilteredLocations(): array
{
// Filter based on $this->query
return [];
}
}
{# templates/components/MapSearch.html.twig #}
<div {{ attributes }}>
<input type="search" data-model="debounce(300)|query" placeholder="Search locations...">
{{ ux_map(this.map, {style: 'height: 400px;'}) }}
</div>
核心原则
在 PHP 中构建,在 Twig 中渲染。 Map 对象是你的数据模型。在 PHP 中添加标记、多边形和圆形(此时你可以访问数据库和服务),然后在 Twig 中通过一次函数调用进行渲染。
渲染器可以互换。 Leaflet 和 Google Maps 可以互换。无论使用哪个渲染器,你的 PHP 代码都使用相同的 Map、Marker 和 Point 类。不同之处仅在于渲染器特有的选项。
使用 extra 添加自定义数据。 标记、多边形和圆形上的 extra 参数可让你附加任意数据,这些数据可在 JavaScript 事件中使用。
安装渲染器。 基础 symfony/ux-map 包提供 PHP API,但你还必须安装 symfony/ux-leaflet-map(免费)或 symfony/ux-google-map(需要 API 密钥)。如果没有渲染器,地图将无法渲染。
Point(lat, lng) —— 纬度在前。 这与 Google Maps 的约定一致,但与 GeoJSON 的 [lng, lat] 顺序相反。顺序弄错会导致标记位置错误,甚至落入海中。
善用 fitBoundsToMarkers()。 无需手动计算中心点和缩放级别,让地图自动调整范围以显示所有标记。如果没有标记,地图会回退到你设置的 center 和 zoom——请务必为两者提供默认值。
参考资料
- API(所有类、方法和选项):references/api.md
- 模式(常见用法、控制器和动态地图):references/patterns.md
- 易错点(常见错误和调试):references/gotchas.md