ux-map について
UX MapはSymfony向けのインタラクティブな地図コンポーネントです。PHPで地図を定義してTwigで表示でき、LeafletやGoogle Mapsのレンダラー、Stimulusイベント、LiveComponent連携に対応します。
主な機能
- 01PHPの地図オブジェクトとTwig描画
- 02マーカー、図形、情報ウィンドウ
- 03LeafletとGoogle Mapsのレンダラー
- 04Stimulusの地図イベント
- 05LiveComponentのリアクティブ連携
活用シーン
- 01Symfonyページで位置マーカーを表示
- 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