yandex/geo
yandex/geo is a PHP client for the Yandex Geocoder API that converts addresses to coordinates and coordinates to addresses. It supports configurable language, precision, and result limits, making geocoding and reverse geocoding straightforward in Laravel/PHP applications.
yandex/geo is a framework-agnostic HTTP client around the Yandex Geocoder XML/REST API. There is no ServiceProvider, no facade, and no config file — you instantiate Yandex\Geo\Api directly.
composer require yandex/geo
First smoke test in php artisan tinker:
$api = new \Yandex\Geo\Api();
$api->setQuery('Москва, Красная площадь')
->setLimit(5)
->setLang(\Yandex\Geo\Api::LANG_RU)
->load();
foreach ($api->getResponse()->getList() as $item) {
dump($item->getAddress(), $item->getLatitude(), $item->getLongitude());
}
Where to look first:
Yandex\Geo\Api — all set* setters (setQuery, setLatitude/setLongitude for reverse geocoding, setLimit, setOffset, setLang, setArea/setBoundingBox).Yandex\Geo\Response — getList(), getFound(), getRawResponse().Yandex\Geo\GeoObject — getAddress(), getLatitude(), getLongitude(), getKind(), getPrecision(), getData() (raw DOM node for anything not exposed).Your first practical use case: geocode a user-entered shipping address into (lat, lng) so you can pass it to a map (Leaflet/Yandex Maps JS/Google Maps) or store it on an Address model next to the free-text address.
Wrap it in a single service, not scattered new Api() calls. You will want to inject config (default lang, limits) and centralize caching and error mapping.
namespace App\Services;
use Yandex\Geo\Api;
class Geocoder
{
public function forward(string $query, int $limit = 5): array
{
return \Cache::remember("geo:{$query}:{$limit}", 86400, function () use ($query, $limit) {
$api = (new Api())->setQuery($query)->setLimit($limit)->setLang(Api::LANG_RU)->load();
return array_map(fn($o) => [
'address' => $o->getAddress(),
'lat' => (float) $o->getLatitude(),
'lng' => (float) $o->getLongitude(),
'kind' => $o->getKind(),
'raw' => $o->getData(),
], $api->getResponse()->getList());
});
}
public function reverse(float $lat, float $lng): ?array
{
$api = (new Api())->setLatitude($lat)->setLongitude($lng)->setLang(Api::LANG_RU)->load();
$list = $api->getResponse()->getList();
return $list ? ['address' => $list[0]->getAddress()] : null;
}
}
Never call the geocoder inside a request that also renders a form. Yandex is slow and rate-limited. Two patterns work well:
GeocodeAddress job, update lat/lng when done, and broadcast/poll for completion./api/geocode?q= that hits the service (cached) and returns a JSON array for an autocomplete box. Debounce on the client (setLimit(5) → small payloads).Config in config/services.php:
'yandex_geo' => [
'lang' => env('YANDEX_GEO_LANG', 'ru_RU'),
'limit' => env('YANDEX_GEO_LIMIT', 10),
'cache_ttl' => 86400,
],
Storing addresses: persist the raw getAddress() string and the components (country, province, locality, street, house) parsed out of getData(), plus the coordinates. Address strings change; components are what you query.
Reverse geocoding for map pins: when a user drops a pin, reverse-geocode to fill the "nearest known address" field. Fall back to showing the raw lat/lng if the API returns empty.
apikey parameter is not exposed by Api. If Yandex rejects your requests due to auth, you'll need to fork the class (it's ~200 lines) or skip the package and call https://geocode-maps.yandex.ru/1.x/?apikey=...&geocode=... with Laravel's Http client. Prefer the latter for new code.apikey setter, no timeouts, no injectable HTTP client. If you must use it, wrap calls in rescue() / try-catch and set a timeout at the cURL level via setCurlOptions() in the underlying curl — otherwise a hung request will block your worker.Yandex\Geo\Exception (API error envelope), Yandex\Geo\Exception\CurlError (network), Yandex\Geo\Exception\ServerError (5xx). Catch broadly in the service layer and return a typed result; never let them bubble to the UI.setLatitude()->setLongitude() is explicit — but getLatitude() returns a string, so cast to float before storing or comparing.getAddress() generally decodes fine, but if you see %D0%9C..., urldecode() before storing.md5($query . $lang . $limit). Consider negative caching (null result) for a short TTL so typo'd addresses don't hammer the API.getList() returns an ArrayObject of GeoObject. Calling getList()[0] is fine, but count with count($list) or via $response->getFound() (the API's reported total, which can exceed what was returned).getData() is a DOMElement. Use textContent/getElementsByTagName to pull fields not exposed (e.g. metaDataProperty, AdministrativeAreaName, ThoroughfareName). This is your escape hatch when the wrapper doesn't surface a field."street", "house", "locality", "country". Compare with === and handle unknown values defensively because Yandex has expanded these over time.Yandex\Geo\Api for a tiny Http::get(...) wrapper in your own service class, keeping the same forward()/reverse() interface. That gets you API-key support, timeouts, retries, and Http::fake() in tests for free.How can I help you explore Laravel packages today?