Download the PHP package cheesegrits/filament-google-maps without Composer
On this page you can find all versions of the php package cheesegrits/filament-google-maps. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download cheesegrits/filament-google-maps
More information about cheesegrits/filament-google-maps
Files in cheesegrits/filament-google-maps
Package filament-google-maps
Short Description A Google Maps package for Filament PHP with field, column and widget
License MIT
Homepage https://github.com/cheesegrits/filament-google-maps
Informations about the package filament-google-maps
Filament Google Maps
This package provides a comprehensive set of tools for using Google Maps within the Filament PHP ecosystem (an application builder for Laravel), either as part of an admin panel, or in standalone front end forms, tables and dashboards.
About The Project
Filament v3 release
This is the v3 branch, compatible with the recent Filament v3 release. At some point soon we will replace the main branch (currently the Filament v2 compatible branch) with this v3 branch, and move Filament v2 support to a v2 branch.
Please report any you find either on the GitHub Issues page, or find me (@cheesegrits) on the Filament Discord server.
API Usage
IMPORTANT NOTE - some features of this package could potentially drive up your API bill. If you have large tables that you display static maps on, and you clear your cache frequently. Or if you allow public access to forms that use geocoding, and get hit by bots.
We strongly suggest you set usage quotas in your Google Console. We are not liable if you get a surprise bill!
TL/DR
If you just can't handle reading documentation and want to dive right in ...
... then follow these instructions to add a computed attribute to any model(s) that will use these components (which should already have separate lat and lng fields, even if they are empty, see the Batch Commands section) ...
... then start using the components, like ...
Components
Map Field
The Map field displays (unsurprisingly) a Google map, with a comprehensive set of configuration options. It supports coordinate updating both ways between map and form, forward and revese geocompletion, reverse geocoding and KML layers.
Geocomplete Field
The Geocomplete field turns a text field on your form into a Google geocomplete field, with optional reverse geocoding of address components.
Infolist Field
The MapEntry Infolist field displays a (read only) map showing a single pin. This is currently WIP, features and functionality (like KML layers, GeoJSON drawings, etc) to be added soon.
Map Widget
The MapWidget displays a filterable set of locations from a model, with optional clustering, templatable labels, customizable icons, etc.
Map Table Widget
The MapTableWidget displays a map widget, along with a Filament Table, and reacts to all filtering and searching on the table.
Map Column
The MapColumn displays a customizable static map image, with the images cached locally to reduce API overhead.
Static Map Action
The StaticMapAction is a bulk action that lets you select any number of table rows, and generate a downloadable static map showing those locations.
Radius Filter
The RadiusFilter provides radius filtering against a geocomplete address, in kilometers or miles.
Batch Commands
The Artisan commands allow you to do batch processing on your location tables, either geocoding a combination of address fields into lat lng, or reverse geocoding lat and lng to address fields.
(back to top)
Getting Started
Prerequisites
This package is built on Filament V2, and Laravel 9. It may run on earlier versions of Laravel, but has not been tested.
Installation
You can install this project via composer:
Assets
This package handles asynchronous loading of JS and CSS assets, in both the Filament Admin Panel and standalone pages, with no need to publish anything or modify your project.
Preparing Models
To simplify working with coordinate data, we require a computed property on any model being used for map data, which converts between separate lat and lng fields on your table, and a Google Point style array of 'lat' and 'lng' keys.
To prepare your model, use the Artisan command:
... which will prompt you for:
- model: your model class, such as Places, or Dealerships/Dealership
- lat: your latitude attribute (existing table field)
- lng: your longitude attribute (existing table field)
- location: the computed property name, which should not exist on your table
The 'location' computed attribute is what you will use when you make() your map fields and columns. If you have no religious preference and it doesn't already exist on your table, just use 'location'.
It will then spit out the code for you to copy and paste to your model class.
NOTE - this script also gives you modified $fillable and $appends arrays if required, which will merge any existing content of these arrays, make sure you replace the existing ones if you already have them.
Setting your Google Maps API Key
All use of the Google Maps API requires an API key. If you don't have one, refer to Google's documentation.
Once you have a key, either add it to your .env file as:
... or publish and edit the filament-google-maps.php config file. We recommend using an environment variable. Note that we deliberately use the same key name used by most Google related Laravel packages, just to make life easier. However, if need to use a different key for this package, you may do so - refer to the config file in the next section.
Publish the configuration
You may optionally publish the package configuration. The configuration comes with a set of sane defaults, so we suggest not publishing unless you actually need to change something ... and even then, best to do it with .env variables.
... which can then be found in ./config/filament-google-maps.php
Of particular note are the config settings for your API Keys and the cache store. By default, we will cache all API responses for 30 days, using your default cache driver. For most normal usage this is sufficient, but if you expect heavy usage, we suggest setting up a dedicated Redis store in your cache.php config, and specify this with the FILAMENT_GOOGLE_MAPS_CACHE_STORE environment variable.
(click to expand)
(back to top)
Usage
Form Field
The form field can be used with no options, by simply adding this to your Filament Form schema:
The name used for make() must be the one you set up as your model's computed location property. Note that you can have multiple maps on a form, by adding a second computed property referencing a second pair of lat/lng fields.
Full Options
The full set of options is as follows. All option methods support closures, as well as direct values.
The mapControls without comments are standard Google Maps controls, refer to the API documentation.
Geocompletion
The autocomplete('field_name') option turns the field name you give it into a Google Places geocomplete field, which suggests locations as you type. Selecting a suggestion will move the marker on the map.
If you specify autocompleteReverse(), moving the map marker will update the field specified in autocomplete() with the reverse geocoded address (using the formatted_address component from Google).
There are three additional options you can specify (typically as named params) for the autocomplete() method, see the Geocomplete field section for details.
Reverse Geocoding
The reverseGeocode() option lets you specify a list of field names from your form, with corresponding format strings for decoding the address component response from Google. We use the printf() style formatting defined by Geocoder PHP as follows:
- Street Number: %n
- Street Name: %S
- City (Locality, or Postal Town in Sweden & UK): %L
- City District (Sub-Locality): %D
- Zipcode (Postal Code): %z
- Admin Level Name: %A1, %A2, %A3, %A4, %A5
- Admin Level Code: %a1, %a2, %a3, %a4, %a5
- Country: %C
- Country Code: %c
- Premise: %p
Note that %p is not listed in the Geocoder PHP docs, and represents the "premise" of an address if present, typically a place name like "The Old Farmhouse".
To help you figure out the format strings you need, you can set debug() on the map field, which will console.log() the response from each reverse geocode event (e.g. whenever you move the marker).
Layers / GeoJSON
There are two ways to add layers to the map. The layers() method accepts an array of KML or GeoRSS file URLs, which will be added to the map using the Maps API KmlLayer() method. Note that these URLs must be publicly accessible, as the KmlLayer() method requires Google servers to read and process the files, see the KML & GeoRSS Layers documentation for details and limitations.
The second method allows for a single GeoJSON file to be specified using the geoJson() method, which accepts a closure or string that can be a local file path, raw GeoJSON, or a URL to a GeoJSON file. If specifying a local path, the optional second argument can be the name of the Storage disk to use. The GeoJSON is rendered on the map using the Maps API Data Layer.
When using GeoJSON, we provide a convenience method for storing a reference to any polygon features which contain the map marker coordinates, using the geoJsonContainsField() method. The first argument to this method is the field name on your form (which can be a Hidden field type) in which to store the data. The second is an optional argument specifying a property name from your GeoJSON features to store. If not specified, the entire GeoJSON feature will be stored.
With the above example, if the user dropped the map pin inside the rectangle, the 'geojson_contains' field would be updated as ["value0"]. If the second argument was omitted, the field would be updated with a GeoJSON FeatureCollection containing the JSON for the rectangle. If you have overlapping features, and multiple polygons contain the marker, all features containing the marker will be included in the array / FeatureCollection.
Also note the optional use of the geoJsonVisible(false) method, which hides the layer (creates a separate Data layer and does not attach it to the map), so you can track which polygons contain the marker without showing the polygons.
Reactive Form Fields
If you want the map marker to react to changes to lat or lng fields on your form:
If you wish to update your lat and lng fields on the form when the map marker is moved:
Reverse Geocode & Place Changed Callbacks
To use the features in this section, you must add the InteractsWithMaps trait to your Livewire component. If you are using it in a Filament panel, this will typically be on the EditFoo page of your resource (or ManageFoo for a simple resource):
In a standalone form context, this would be on your own component.
If the built-in reverse geocode symbol mapping doesn't do what you need, you can provide a closure which will get called via Livewire whenever a reverse geocode occurs on the Map. You will be passed an array with the geocode results, and can then process those how you want, and use a $set callable to set fields on your form accordingly.
NOTE that reverseGeocodeUsing() can be used in combination with reverseGeocode(), so you can fill some fields with the simpler reverseGeocode() method, and others with reverseGeocodeUsing(). This is useful if, for example, you have counties and/or states tables and use those with Select fields with relationships, so need to handle counties / states differently (by looking up the corresponding address components in your tables and setting your form fields to the appropriate keys).
Likewise, if you want to do custom processing whenever a Place is resolved on the Map, usually from a Geocomplete or by clicking on a place pin on the map, you can use the
NOTE that when you provide a placeUpdatedUsing() callback, we automatically add 'photos' to the list of Place fields to fetch from the API, which are then available to you in the $place array.
ALSO NOTE that placeUpdatedUsing() can add extra API calls when the map is clicked, so just be aware if you are trying to keep your API usage to a minimum.
Geocomplete Field
The Geocomplete field turns a field on your form into a Google Geocomplete field. You would usually use this instead of a Map field (if you want a geocomplete field together with a map, you would typically use the autocomplete() feature on the Map field).
The Geocomplete field can operate in one of two modes. Either independently, where you simply use it with a normal text field on your form, e.g. 'full_address', and this component will simply fill the field in with the formatted address returned when the user selects one from the dropdown.
The second mode is isLocation() mode, where you use it with the 'location' computed attribute field from your model. In this usage, when the form is saved, the currently selected address will be geocoded to your lat and lng fields. When the form loads, if geocodeOnLoad() is specified, the current lat and lng will be reverse geocoded to a full address (using the formatted_address field from Google).
NOTE - the geocodeOnLoad() feature requires API access from your server. If you are using an API key which is restricted to HTTP Referrers, this will not work. You will need to add another key using the FILAMENT_GOOGLE_MAPS_SERVER_API_KEY (see Config section), which is restricted by IP address.
In isLocation mode the field on the form will be empty on load (as it's not a text field an address can be stored in). If you want this filled in, you can use geocodeOnLoad() which will do a server side API call to resolve the lat/lng to an address. See the note in the config section about server side API keys.
In both modes, you can specify the type(s) of place to show, and the Places response field to use to fill the field. Refer to the Google Places API documentation for the Place Types and Place Data Fields. Pay particular to the limitations on the number and mix of types - either 1 from Table 3 (like 'address' or 'establishment'), or up to 5 from tables 1 or 2 (like 'airport', 'subway_station', etc).
In both modes, you may optionally specify fields to reverse geocode the selected address component data to, using the same method as the Map component, documented above.
Not shown in thew following example, but you can also use the reverseGeocodeUsing() method to provide your own closure for handling reverse geocode data, as described in the Map component above.
This field is cost optimized, so it will only start searching for places after 300ms of inactivity, and will not search while you are typing. This is to prevent excessive API calls, as Google charges for each one. If you set the minChars to 0, it will start searching immediately. I suggest you set it to a minimum of 3.
The Geocomplete field also offers many of the same features as Filament's TextInput, like prefixes, suffixes, placeholders, etc.
Infolist Field
The Infolist field displays a read-only map with a single field showing the field's location.
Form WidgetMap Field
If you need to display multiple markers in a map on a form, you can use the WidgetMap field. This is a cut down version of the main MapWidget (see below), providing a read-only display of multiple markers. You cannot move or update the markers, only display them.
The markers() method must return an array of location arrays (same as the main Map Widget) of the form:
There are also center() and zoom() methods you can use to customize the initial display of the map.
Table Column
The table column displays a static Google map image. The images are created on the server side through calls to the Maps API, and cached locally on the server (using Laravel's default cache driver) for a default of 30 days, to prevent excessive API usage. See the warning at the top of this page about API usage.
NOTE that options marked as 'API Setting' are used as part of the cache key, so changing any of these will force a cache refresh for all images in the table (as they are displayed).
Radius Filtering
The radius filter allows you to specify an address (using a geocomplete dropdown), a numeric distance and an optional unit selection, and the table will be filtered to records within the specified distance of that address.
If your locations are in a related table, for example if you want to put a RadiusFilter on an 'events' table, and your locations are in a 'places' table, and you have a 'place' BelongsTo relationship on your Event model.
You may also override the color and icon.
When using Radius filtering, there is also a RadiusAction you can use, which allows you to click a button on a row in the table to set the address being used for the current Radius Filter.
NOTE - you must name the RadiusAction the same as your RadiusFilter. The default is 'radius'.
If your locations are in related data, you may add a relationship() method to the RadiusAction. You may also override the color and icon:
Map Is Filter
See the Map Table Widget section below for details on how to use a map as a filter for a table.
Static Map Bulk Action
The Static Map bulk action allows you to select any number of rows in the table, then generate a downloadable static map of those locations, with a dialog to specify the map size, type and scale.
Map Widget
The map widget can be used either in the Filament Admin panel (see Filament docs), or standalone as a normal Livewire component.
To generate the code for a widget, run this Artisan command:
If you omit the Resource, the widget will be created in the main widget folder at /Filament/Widgets, and the command will tell you what to do if you want to use it on the front end:
The created code will look something like this:
Optionally you can render your labels with Blade templates (see the Google API docs for restrictions on what HTML markup and styling you can use), and provide an icon (svg or png) ...
To add a clickable popup action to your markers, for example to display an Infolist with record details, you can add a markerAction() method, which can use the 'model_id' from $arguments in the actions's record() method to locate the record for the clicked marker, for example:
You can add options to the map config (the 'opts' object passed to the Google map creation in Javascript) by overriding the getConfig() method, and adding a ['mapConfig'] entry to the $config. Anything you add to this will be passed verbatim to the map creation. For example, to hide POI (points of interest) markers:
See the parent component code for further methods and variables you can override, like changing or removing the icon or making the map section collapsible.
Map Table Widget
The map table widget has all the features of the vanilla widget, but with the addition of a Filament table underneath it. The map responds to all filtering and searching on the table, which is done with standard Filament Table methods and schemas.
To generate a Dealership table map, you would run the same Artisan command, but choose the Map & Table option. The generated code will look similar to the Map option, but with the addition of the familiar Filament methods to define the table columns, filters, actions, etc.
Anything you can do in normal Filament tables, you can do in this table.
Also note the use of the MapIsFilter table filter. With this optionally included in the table filters, your map acts as a filter for the attached table, so zooming and panning to change the visible map pins will filter the table accordingly.
There is also an additional action, the GoToAction, available for this widget, which will zoom and pan the map to the selected location when clicked.
(back to top)
Artisan Commands
The following commands can also be referenced as fgm: instead of filament-google-maps:, as yes, we get tired typing that as well.
Helper commands
It is often useful to be able to test a single geocode lookup. We provide two commands ...
... where the switches are optional and control what format(s) the lat/lng are given, useful for (say) getting the array to use for setting a default location on a Map field. Or, as we are doing here, finding the coordinates of an address to use in the reverse lookup command, so we can check the address components formats ...
Batch Commands
When dealing with location data, it is common to have tables which have lat and lng date but no address data, or vice versa. This package provides a convenient way to process tables to either geocode or reverse geocode them to fill in the blanks.
Batch Geocoding
To add lat and lng coordinates to a table with address data, run this command:
... which will prompt you for the following
- model - your model name, like Location or Dealerships/Location
- fields - an ordered, comma separated list of the fields that make up an address, like 'street,city,state,zip'
- lat - your lat field
- lng - your lng field
- processed - optional field name that will get set to 1 when geocoded, and excluded if it is set to 1
- rate-limit - max number of lookups per minute (max is 300, which is Google's hard limit, suggested max is 150)
Or you can skip the hand holding and issue it as ...
If any of your address data is a join relationship, like say you have a 'states' table and the 'state' field is a foreign key, you can specify that in dotted notation, like 'states.state_full_name', where the first part (states) is the name of the relationship on your model.
The command will select all records from your table where either the lat or lng fields are empty (0, null or empty string).
Batch Reverse Geocoding
Reverse geocoding from the command line is a little trickier, as we have to decompose and map the rather complicated address format Google returns. For this, we use a standard printf style formatting from Gecocoder PHP.
Rather than explain it all, here as an example terminal session ...
(click to expand)
(back to top)
Example / Test Repo
There is an example app you can use for testing, which provides examples of most of the features provided by this package.
(back to top)
Roadmap
- [x] Add caching for all API usage
- [x] Add option for which cache store to use for static maps
- [x] Add Geocomplete field
- [ ] Improve Geocomplete field Places Data Field handling (allow more than one to be combined)
- [x] Add Artisan commands for geocoding / reverse geocoding tables, useful when source tables have addreeses but no coords, or vice versa
- [ ] Add optional request signing of API calls
- [x] Add locale to all API calls
- [x] Add make-widget artisan command
- [x] Add KML layers to field and widgets
- [x] Add more geocoding options for form fields, for individual address components (street, city, zip, etc)
- [ ] Improve reverse geocoding format grammar, like alternates ... %A3|%A2 (is %A3 empty, try %A2), etc
- [ ] Write test suite
(back to top)
Issues
If (when) you find bugs, please report them on the issues page and we'll fix them ASAP.
(back to top)
Contributing
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
(back to top)
License
Distributed under the MIT License. See LICENSE.txt
for more information.
(back to top)
Contact
Hugh Messenger - @cheesegrits - [email protected]
Project Link: https://github.com/cheesegrits/filament-google-maps
(back to top)
Acknowledgments
(back to top)
All versions of filament-google-maps with dependencies
spatie/laravel-package-tools Version ^1.9
guzzlehttp/guzzle Version ^7.5
geocoder-php/google-maps-provider Version ^4.7
php-http/guzzle7-adapter Version ^1.0
spatie/guzzle-rate-limiter-middleware Version ^2.0
php-http/message Version ^1.13
ryangjchandler/blade-capture-directive Version ^0.3|^1.0
mastani/laravel-google-static-map Version ^2.2
laravel/prompts Version ^0.1.4|^0.2.0|^0.3.0