PHPackages                             kikwik/gmap-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. kikwik/gmap-bundle

ActiveSymfony-bundle[API Development](/categories/api)

kikwik/gmap-bundle
==================

Google Map and Geocoder support for Symfony 5

v1.0.23(2y ago)061MITPHPPHP &gt;=7.2.5

Since Apr 16Pushed 2y ago1 watchersCompare

[ Source](https://github.com/kikwik/gmap-bundle)[ Packagist](https://packagist.org/packages/kikwik/gmap-bundle)[ RSS](/packages/kikwik-gmap-bundle/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (1)Dependencies (10)Versions (23)Used By (0)

KikwikGmapBundle
================

[](#kikwikgmapbundle)

Google Map and Geocoder support for Symfony 5

Installation
------------

[](#installation)

Open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle:

```
$ composer require kikwik/gmap-bundle
```

Configuration
-------------

[](#configuration)

Add your Api keys to .env file:

- `GMAP_API_KEY` for server geocoding
- `GMAP_API_KEY_JS` for javascript maps:

```
###> geocoder ###
# https://console.cloud.google.com/apis/credentials?hl=it&project=my-project
# credential: "My server api key"
# allowed IP address: xxx.xxx.xxx.xxx | yyy.yyy.yyy.yyy
GMAP_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# https://console.cloud.google.com/apis/credentials?hl=it&project=my-project
# credential: "My javascript api key"
# allowed domains: https://*.my-domain.ltd/*  |  https://my-domain.ltd/*
GMAP_API_KEY_JS=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
###< geocoder ###
```

Autowiring
----------

[](#autowiring)

The bundle will configure an autowired provider with signature `Provider $googleMapsGeocoder` that you can use as follow:

```
namespace App\Service;

use Geocoder\Provider\Provider;
use Geocoder\Provider\GoogleMaps\Model\GoogleAddress;

class MyService
{
    private $googleMapsGeocoder;

    public function __construct(Provider $googleMapsGeocoder)
    {
        $this->googleMapsGeocoder = $googleMapsGeocoder;
    }

    public function doGeocode(string $address)
    {
        // create the GeocodeQuery with the address to geocode
        $geocodeQuery = GeocodeQuery::create('piazza Duomo 1, Milano')
            ->withLocale('it');

        // ask geocode to the $googleMapsGeocoder provider,
        // the result is an array of GoogleAddress objects
        /** @var GoogleAddress $geocodeResults[] */
        $geocodeResults = $googleMapsGeocoder->geocodeQuery($geocodeQuery);

        // first result should be the best match
        return $geocodeResults[0] ?? null;
    }
}
```

Geocodable Entity
-----------------

[](#geocodable-entity)

Make your entities (and repository) geocodable by implementing `GeocodableEntityInterface` (and `GeocodableRepositoryInterface`)

Use the provided traits to be quick:

```
namespace App\Entity;

use Kikwik\GmapBundle\Geocodable\GeocodableEntityInterface;
use Kikwik\GmapBundle\Geocodable\GeocodableEntityTrait;

class Place implements GeocodableEntityInterface
{
    use GeocodableEntityTrait;

    // ...
}
```

```
namespace App\Repository;

use Kikwik\GmapBundle\Geocodable\GeocodableRepositoryInterface;
use Kikwik\GmapBundle\Geocodable\GeocodableRepositoryTrait;

class PlaceRepository extends ServiceEntityRepository implements GeocodableRepositoryInterface
{
    use GeocodableRepositoryTrait;

    // ...
}
```

Then you can set address to the entity and ask to geocode her self (passing the provider):

```
namespace App\Controller;

use Geocoder\Provider\Provider;

class GmapController extends AbstractController
{
    /**
     * @Route("/gmap/new", name="app_gmap_new")
     */
    public function createNewPlace(Provider $googleMapsGeocoder, EntityManagerInterface $entityManager)
    {
        // create an object that implement GeocodableEntityInterface
        /** @var Kikwik\GmapBundle\Geocodable\GeocodableEntityInterface $place */
        $place = new Place();

        // fill the address fields
        $place->setStreet('Piazza Duomo');
        $place->setStreetNumber('1');
        $place->setZipCode('20100');
        $place->setCity('Milano');
        $place->setProvince('MI');
        $place->setCountry('Italia');

        // Ask geocode by passing the provider
        $place->doGeocode($googleMapsGeocoder);

        $entityManager->persist($place);
        $entityManager->flush();

        return $this->redirect($place->getGmapsUrl());
    }
}
```

Geocode Command
---------------

[](#geocode-command)

With the `kikwik:gmap:geocode` command you can batch geocode all the entities that need to be geocoded (never geocoded or with address changed after the last geocode)

```
$ php bin/console kikwik:gmap:geocode --limit=5
```

Use the `--failed` option to try to geocode again the failed ones

```
$ php bin/console kikwik:gmap:geocode --limit=5 --failed
```

Display Maps
------------

[](#display-maps)

- Call the `kw_gmap_script_tags` twig function inside the javascripts block to initialize the GMap library, eventually pass the optional nonce value
- then create a new `kwMap` object
- and call its `init` function that return a promise that is resolved when the map is loaded

```
{% block javascripts %}
    {{ parent() }}

    {{ kw_gmap_script_tags(csp_nonce('script')) }}

        document.addEventListener("DOMContentLoaded", function() {
            let mapElements = document.querySelectorAll('.kw-map');
            mapElements.forEach(function (mapElement){
                let map = new kwMap();
                map.init(mapElement)
                        .then(function (){

                            map.getGMap().addListener('bounds_changed', function() {
                                const searchText = document.getElementById('map-search-txt');
                                const searchResults = document.getElementById('search-results');
                                const searchResultList = searchResults.querySelector('.js-location-list');
                                const searchResultCount = searchResults.querySelector('.js-location-count');
                                if(searchText.value)
                                {
                                    // get visible markers
                                    let visibleMarkers = map.getVisibleMarkers();
                                    // update counter
                                    searchResultCount.textContent = '('+visibleMarkers.length+')';
                                    // empty result list
                                    searchResultList.innerHTML = '';
                                    // add results to list
                                    for(let visibleMarker of visibleMarkers)
                                    {
                                        let node = document.createElement('li');
                                        node.id = 'result-'+visibleMarker.id;
                                        node.innerHTML = visibleMarker.info;
                                        searchResultList.appendChild(node);
                                    }
                                    // show results
                                    searchResults.classList.remove('d-none');
                                }
                                else
                                {
                                    // hide results
                                    searchResults.classList.add('d-none');
                                }
                            });

                            const mapSourceRadios = document.querySelectorAll('.js-map-source');
                            mapSourceRadios.forEach(function (mapSourceRadio){
                                mapSourceRadio.addEventListener('click', function() {
                                    map.clearMarkers();
                                    let url = this.value;
                                    mapElement.dataset.mapRemoteMarkers = url;
                                    map.loadMarkers();
                                })
                            })
                        })
            });
        });

{% endblock %}
```

Then place a div on the page for each map, and use the twig helpers:

- `{{ kw_map_data_center(-31.56391, 147.154312) }}` - set map center, parameters are a couple of float
- `{{ kw_map_data_center(place) }}` - set map center, parameter is a GeocodableEntityInterface object
- `{{ kw_map_data_zoom(3) }}` - set map zoom, parameter is an integer
- `{{ kw_map_data_markers(places) }}` - load markers, parameter is an array of GeocodableEntityInterface objects
- `{{ kw_map_data_cluster({ maxZoom: 10, minPoints: 5 }, 'darkgreen') }}` - activate cluster feature, parameters are an array of SuperCluster options, (see ) and an optional color (this activate the SingleColorRenderer)
- `{{ kw_map_data_remote_markers(asset('path/to/file.json')) }}` - load remote markers, parameter is the remote url
- `{{ kw_map_data_search_address('#map-address','#map-address-submit', { findNearestMarker: true }) }}` - bound a search form, parameters are the css selector of the input text, the css selector of the submit button and an array of options
- `{{ kw_map_data_street_view('#street-view',place) }}` - enable street view, parameters are the css selector of the container and a GeocodableEntityInterface object
- `{{ kw_map_data_street_view('#street-view',41.9027835, 12.4963655) }}` - enable street view, parameters are the css selector of the container and a couple of float

Here all the data-attribute supported:

- `data-map-center` a json string that represent a LatLngLiteral
- `data-map-zoom` an integer value
- `data-map-markers` a json string that represent an array of marker descriptor, each marker descriptor must have the following fields
    - `lat` latitude value (float)
    - `lng` longitude value (float)
    - `info` the google.maps.InfoWindow content (optional)
    - `icon` the icon file (optional)
    - `identifier` a sting that identify the marker (optional)
- `data-map-cluster` a json string that represent the SuperCluster options (see )
- `data-map-cluster-color` a color string for the cluster's SingleColorRenderer
- `data-map-remote-markers` an url from which load markers in json format
- `data-map-search-address` the css selector of the input text used to center the map
- `data-map-search-submit` the css selector of the submit button used to center the map
- `data-map-search-find-nearest-marker` set to "1" for a zoom out after a successful search, until a marker is visible in the map
- `data-map-street-view` the css selector of the element that will contain the street view
- `data-map-street-view-position` a json string that represent a LatLngLiteral

some examples:

```

    Cerca ›

Map with clustered external data and search box:

Empty map centered in australia:

Empty map centered in place (an GeocodableEntityInterface object):

Map with marker in all places (an array of GeocodableEntityInterface object):

Map with zoom=3, marker in all places (an array of GeocodableEntityInterface object) centered in the first one, street view on the third one

```

Address Autocomplete
--------------------

[](#address-autocomplete)

Use the `AddressAutocompleteType` in your forms to geocode adresses in separate components

```
use Kikwik\GmapBundle\Form\Type\AddressAutocompleteType;

class GmapController extends AbstractController
{
    /**
     * @Route("/gmap/autocomplete", name="app_gmap_autocomplete")
     */
    public function addressAutocomplete(Request $request)
    {
        $submittedData = null;
        $form = $this->createFormBuilder()
            ->add('indirizzo1',AddressAutocompleteType::class, [
            ])
            ->add('indirizzo2',AddressAutocompleteType::class, [
                'autocomplete_fields' => ['latitude','longitude']
            ])
            ->getForm();
        $form->handleRequest($request);
        if($form->isSubmitted() && $form->isValid())
        {
            $submittedData = $form->getData();
            dump($submittedData['indirizzo1']['autocomplete']);
            dump($submittedData['indirizzo1']['street']);
            dump($submittedData['indirizzo1']['streetNumber']);
            dump($submittedData['indirizzo1']['zipCode']);
            dump($submittedData['indirizzo1']['locality']);
            dump($submittedData['indirizzo1']['city']);
            dump($submittedData['indirizzo1']['province']);
            dump($submittedData['indirizzo1']['region']);
            dump($submittedData['indirizzo1']['country']);
            dump($submittedData['indirizzo1']['latitude']);
            dump($submittedData['indirizzo1']['longitude']);
        }

        return $this->render('gmap/addressAutocomplete.html.twig',[
            'form'=>$form->createView(),
            'submittedData' => $submittedData,
        ]);
    }
}
```

remember to load gmap scripts in your template:

```
{% block javascripts %}
    {{ parent() }}

    {{ kw_gmap_script_tags() }}

{% endblock %}
```

###  Health Score

23

—

LowBetter than 27% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity8

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~13 days

Recently: every ~29 days

Total

22

Last Release

839d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/4bdd98919c8ee6645e854e72f8c6b76c503e12fd10078fb34ae1668cb2bd6d1a?d=identicon)[kikwik](/maintainers/kikwik)

---

Top Contributors

[![kikwik](https://avatars.githubusercontent.com/u/58590255?v=4)](https://github.com/kikwik "kikwik (45 commits)")

### Embed Badge

![Health badge](/badges/kikwik-gmap-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/kikwik-gmap-bundle/health.svg)](https://phpackages.com/packages/kikwik-gmap-bundle)
```

###  Alternatives

[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.4k5.6M651](/packages/sylius-sylius)[prestashop/prestashop

PrestaShop is an Open Source e-commerce platform, committed to providing the best shopping cart experience for both merchants and customers.

9.0k15.4k](/packages/prestashop-prestashop)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.3M152](/packages/sulu-sulu)[kimai/kimai

Kimai - Time Tracking

4.6k7.4k1](/packages/kimai-kimai)[contao/core-bundle

Contao Open Source CMS

1231.6M2.4k](/packages/contao-core-bundle)[ec-cube/ec-cube

EC-CUBE EC open platform.

78527.0k1](/packages/ec-cube-ec-cube)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
