naif/map_address
A Laravel/PHP package for working with map addresses, providing a simple, configurable way to integrate address lookup and location mapping into your application. It offers a clean API for handling address data and adding location-aware features to PHP projects.
naif/map_address is a Nova form field that renders a Google Map with a draggable marker, reverse-geocodes the pin position, and returns a formatted street address as the field value.
Viability check first. Last release was January 2019 (Nova 1.x era, Vue 2 assets, Laravel Mix build). Nova 4/5 moved to Vue 3 and a different asset pipeline, so expect the field's JavaScript not to load on a modern Nova install. Before writing any code, open the repo and check: (a) the composer.json Nova constraint, (b) src/MapAddress.php for the actual fluent option names, (c) resources/js/field.js for which Google APIs it calls. If you're on Nova 3+, plan from the start on vendoring/forking it rather than composer require.
Minimal setup
composer require naif/map_address
php artisan vendor:publish --provider="Naif\MapAddress\FieldServiceProvider"
Schema::table('stores', function (Blueprint $table) {
$table->string('address')->nullable();
$table->decimal('latitude', 10, 7)->nullable();
$table->decimal('longitude', 10, 7)->nullable();
});
use Naif\MapAddress\MapAddress;
public function fields(Request $request)
{
return [
MapAddress::make('Address', 'address')
->rules('required', 'string', 'max:255'),
];
}
First use case: an admin picks a store/venue/property location by clicking the map instead of typing coordinates. Treat latitude/longitude as the source of truth and address as a derived, human-readable cache — reverse geocoding is lossy and locale-dependent, and you'll want the numbers for distance math, map embeds, and geo queries.
Split coordinates out of the field value. The field's rendered component is one input; the coordinates live in the field's Vue/withMeta payload and are not saved automatically. Bridge them in the field definition or on the model:
MapAddress::make('Address')
->fillUsing(function ($request, $model, $attribute) {
$model->{$attribute} = $request->input($attribute);
$model->latitude = $request->input('latitude');
$model->longitude = $request->input('longitude');
})
If the package exposes ->latField('latitude')->lngField('longitude')-style options (check src/MapAddress.php), prefer those — but verify they actually hit the request payload, because Nova only posts registered fields by default; hidden lat/lng attributes need ->onlyOnForms() companions or a fillUsing hook as above.
Form vs. detail. Use ->onlyOnForms() for the picker and render a read-only map or a static embed on detail:
MapAddress::make('Address')->onlyOnForms(),
Text::make('Coordinates', fn () => "{$this->latitude}, {$this->longitude}")
->onlyOnDetail(),
Backfilling existing rows. The field is useless for imports and seeders because reverse geocoding happens in the browser. Write a small server-side service that hits the Google Geocoding API and cache the result — one class, reused by an artisan command and by an observer:
class Geocoder
{
public function reverse(float $lat, float $lng): ?string
{
$res = Http::get('https://maps.googleapis.com/maps/api/geocode/json', [
'latlng' => "{$lat},{$lng}",
'key' => config('services.google.maps_key'),
]);
return data_get($res->json(), 'results.0.formatted_address');
}
}
Keep the address fresh. Addresses drift (renames, renumbering). Schedule a nightly job that re-geocodes rows where updated_at is older than N days, or where address is null — never re-geocode on every save.
Downstream consumers. Once coordinates are stored, the normal Laravel/Nova follow-ons apply: ->sortable() on lat/lng, Haversine selectRaw for "nearest to" filters, and MapAddress-independent map widgets for detail views.
Geocoder in config/services.php. A missing/disabled API produces a grey map with no PHP error — check the browser console for ApiNotActivatedMapError.saving when address is blank but coordinates are present..js won't register. Debug path: php artisan nova:publish, confirm files land in public/vendor/naif/map-address, check the network tab for a 404 on js/field.js, and check whether Vue.component(...) in the bundle matches Nova's Vue version. If it doesn't, the pragmatic fix is a thin fork: keep the PHP field class, replace resources/js/field.js with a Vue 3 component using @googlemaps/js-api-loader, and register it via Nova::script yourself.decimal(10,7), not float or string. Cast them to float on the model, otherwise comparisons and radius queries behave oddly.address_components and store them separately.dragend), cache by rounded lat,lng key, and set a billing quota alert — a 13-star package with no rate limiting will happily fire a request per pixel of drag.nova-components directory, pin the fork, and add a feature test that posts the form and asserts lat/lng/address all persist. That test is also your fastest regression signal when you upgrade Nova.fillUsing() for custom persistence, resolveUsing() for formatting what the detail view shows, and withMeta() for injecting the API key, default center, and zoom from config rather than hardcoding them in the field call.How can I help you explore Laravel packages today?