mcpField
U
ux-map

ux-map

github.com/smnandre开发18054 次安装19 周前更新
代码仓库

关于 ux-map

UX Map 是用于 Symfony 的交互式地图组件,可通过 PHP 定义地图并在 Twig 中渲染。它支持 Leaflet 与 Google Maps 渲染器,并可结合 Stimulus 和 LiveComponent 扩展。

主要功能

  1. 01PHP 地图对象与 Twig 渲染
  2. 02支持标记、形状与信息窗口
  3. 03Leaflet 和 Google Maps 渲染器
  4. 04Stimulus 地图事件
  5. 05LiveComponent 响应式集成

适用场景

  1. 01在 Symfony 页面中展示位置标记
  2. 02绘制多边形、线条和圆形区域
  3. 03监听地图元素创建事件
  4. 04构建可响应更新的地图组件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/smnandre/symfony-ux-skills --skill ux-map

安装后在 Claude Code 中输入/ux-map确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api.md
gotchas.md
patterns.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: '&copy; 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——请务必为两者提供默认值。

参考资料

评论

支持 Markdown