В версии модуля sale 17.0.13 появилось автоматическое определение местоположения покупателя при оформлении заказа. Но это можно использовать и в своих целях, соответствующие классы и методы доступны для использования разработчиками.

В sale 17.0.17 замечен баг с геолокацией (Ошибка "Call to undefined method Bitrix\Main\Service\GeoIp\Manager::getData()"). Из-за этого не работает оформление заказа (sale.order.ajax). Для получения костыля нужно написать в техподдержку или обновиться до sale 17.0.18.

Определение местоположения реализовано в классах пространства имён \Bitrix\Main\Service\GeoIp. Для работы примеров будет использоваться сокращение:

use \Bitrix\Main\Service\GeoIp;

Обработчики геолокации

Доступные обработчики можно найти и настроить в настройках Настройки > Настройки продукта > Геолокация. На момент написания статьи доступны обработчики:

  • MaxMind;
  • Sypex Geo;
  • Расширение GeoIP.

Для первых двух нужны ключи для доступа к соответствующим сервисам. Для тестирования проще и дешевле зарегистрироваться на Sypex Geo, там дают 30000 бесплатных запросов в месяц. Ключ для сервиса будет указан в личном кабинете, его нужно будет указать в настройках обработчика.

Обработчики геолокации - классы-наследники GeoIp\Base. Из коробки идут 3 обработчика, как говорилось выше:

$buildInHandlers = array(
    '\Bitrix\Main\Service\GeoIp\MaxMind' => 'lib/service/geoip/maxmind.php',
    '\Bitrix\Main\Service\GeoIp\Extension' => 'lib/service/geoip/extension.php',
    '\Bitrix\Main\Service\GeoIp\SypexGeo' => 'lib/service/geoip/sypexgeo.php'
);

Добавить свои можно с помощью события onMainGeoIpHandlersBuildList модуля main:

$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->addEventHandler(
    'main',
    'onMainGeoIpHandlersBuildList',
    'myGeoIpHandlers'
);

function myGeoIpHandlers()
{
    return new \Bitrix\Main\EventResult(
        \Bitrix\Main\EventResult::SUCCESS,
        array(
            '\Acme\YourClass' => '/path/to/your/class.php',
        )
    );
}

Примеры обработчиков можно найти в ядре, обязательно требуется определить три метода: getTitle, getDescription и getData.

Использование

Для определения местоположения требуется IP пользователя. Его можно получить с помощью метода GeoIp\Manager::getRealIp:

$ipAddress = GeoIp\Manager::getRealIp();

Для получения геоинформации по этому IP нужно вызвать метод GeoIp\Manager::getDataResult:

$result = GeoIp\Manager::getDataResult($ipAddress, "ru");

Метод возвращает объект класса GeoIp\Result (до версии 17.0.18 – GeoIp\DataResult). Поля объекта:

object(Bitrix\Main\Service\GeoIp\Result)#101 (4) {
  ["geoData":protected]=>
  object(Bitrix\Main\Service\GeoIp\Data)#75 (16) {
    ["ip"]=>
    string(12) "XXX.XXX.XXX.XXX"
    ["lang"]=>
    string(2) "ru"
    ["countryName"]=>
    string(12) "Россия"
    ["regionName"]=>
    string(31) "Липецкая область"
    ["subRegionName"]=>
    NULL
    ["cityName"]=>
    string(12) "Липецк"
    ["countryCode"]=>
    string(2) "RU"
    ["regionCode"]=>
    string(6) "RU-LIP"
    ["zipCode"]=>
    NULL
    ["latitude"]=>
    float(52.60311)
    ["longitude"]=>
    float(39.57076)
    ["timezone"]=>
    string(13) "Europe/Moscow"
    ["asn"]=>
    NULL
    ["ispName"]=>
    NULL
    ["organizationName"]=>
    NULL
    ["handlerClass"]=>
    string(34) "Bitrix\Main\Service\GeoIp\SypexGeo"
  }
  ["isSuccess":protected]=>
  bool(true)
  ["errors":protected]=>
  object(Bitrix\Main\ErrorCollection)#79 (1) {
    ["values":protected]=>
    array(0) {
    }
  }
  ["data":protected]=>
  array(0) {
  }
}

Из поля 'handlerClass' видно, что для определения использовался обработчик Sypex Geo. Вообще менеджер перебирает все доступные настроенные обработчики, которые могут вернуть результат на нужном языке (второй аргумент в методе getDataResult) и с нужными полями. Если найден подходящий обработчик и он вернул результат, то запросы к другим доступным обработчикам не делаются.

Нужные поля можно указать третьим аргументом в методе getDataResult, например:

GeoIp\Manager::getDataResult($ip, $lang, array('cityName', 'zipCode'));

В таком случае Sypex Geo не будет использован, т.к. в списке возвращаемых полей нет zipCode.

Перед запросом можно включить сохранение геоинформации в cookies, для этого перед getDataResult следует вызвать:

GeoIp\Manager::useCookieToStoreInfo(true);

В таком случае, в случае успешного получения геоинформации она будет сохранена в cookies BX_MAIN_GEO_IP_DATA_XXX_XXX_XXX_XXX, привязанной к определенному IP. Пока cookie будет жива и IP будет оставаться прежним, запросов в сервис геолокации по данному пользователю не будет.

Стоит учесть, что максимальная длина cookie - 4096 байт, а в IE вроде как еще меньше. Поэтому неизвестно, насколько надежно держать в cookie сериализованный объект с геоинформацией. Уверен, что в Битриксе об этом не задумывались.

Геолокация в модуле sale

Как говорилось в начале, геолокация была анонсирована в обновлении модуля sale, поэтому геолокацию можно использовать для определения местоположения пользователя для оформления заказа. Для этого существует класс Bitrix\Sale\Location\GeoIp, который использует вышеописанный функционал для поиска местоположения в базе Битрикса.

// Возвращает ID местоположения
\Bitrix\Sale\Location\GeoIp::getLocationId($ip, $lang);
// Возвращает код местоположения
\Bitrix\Sale\Location\GeoIp::getLocationCode($ip, $lang);
// Возвращает индекс
\Bitrix\Sale\Location\GeoIp::getZipCode($ip, $lang);

Во всех этих методах используется сохранение результата геолокации в cookies. Местоположение в базе находится по названию независимо от регистра.