GetDistance.php 3.96 KB
<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
declare(strict_types=1);

namespace Magento\InventoryDistanceBasedSourceSelection\Model\DistanceProvider\GoogleMap;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\HTTP\ClientInterface;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\InventoryDistanceBasedSourceSelection\Model\Convert\LatLngToQueryString;
use Magento\InventoryDistanceBasedSourceSelectionApi\Api\Data\LatLngInterface;
use Magento\InventoryDistanceBasedSourceSelectionApi\Api\GetDistanceInterface;

/**
 * @inheritdoc
 */
class GetDistance implements GetDistanceInterface
{
    private const GOOGLE_ENDPOINT = 'https://maps.googleapis.com/maps/api/distancematrix/json';
    private const XML_PATH_MODE = 'cataloginventory/source_selection_distance_based_google/mode';
    private const XML_PATH_VALUE = 'cataloginventory/source_selection_distance_based_google/value';
    private const ZERO_RESULT_RESPONSE = 'ZERO_RESULTS';

    /**
     * @var array
     */
    private $distanceCache = [];

    /**
     * @var ClientInterface
     */
    private $client;

    /**
     * @var ScopeConfigInterface
     */
    private $scopeConfig;

    /**
     * @var Json
     */
    private $json;

    /**
     * @var GetApiKey
     */
    private $getApiKey;

    /**
     * @var LatLngToQueryString
     */
    private $latLngToQueryString;

    /**
     * GetLatLngFromAddress constructor.
     *
     * @param ClientInterface $client
     * @param ScopeConfigInterface $scopeConfig
     * @param Json $json
     * @param GetApiKey $getApiKey
     * @param LatLngToQueryString $latLngToQueryString
     */
    public function __construct(
        ClientInterface $client,
        ScopeConfigInterface $scopeConfig,
        Json $json,
        GetApiKey $getApiKey,
        LatLngToQueryString $latLngToQueryString
    ) {
        $this->client = $client;
        $this->json = $json;
        $this->getApiKey = $getApiKey;
        $this->scopeConfig = $scopeConfig;
        $this->latLngToQueryString = $latLngToQueryString;
    }

    /**
     * @inheritdoc
     * @throws LocalizedException
     */
    public function execute(LatLngInterface $source, LatLngInterface $destination): float
    {
        $sourceString = $this->latLngToQueryString->execute($source);
        $destinationString =  $this->latLngToQueryString->execute($destination);

        $key = $sourceString . '|' . $destinationString;

        if (!isset($this->distanceCache[$key])) {
            $queryString = http_build_query([
                'key' => $this->getApiKey->execute(),
                'origins' => $sourceString,
                'destinations' => $destinationString,
                'mode' => $this->scopeConfig->getValue(self::XML_PATH_MODE),
            ]);

            $this->client->get(self::GOOGLE_ENDPOINT . '?' . $queryString);
            if ($this->client->getStatus() !== 200) {
                throw new LocalizedException(__('Unable to connect google API for distance matrix'));
            }

            $res = $this->json->unserialize($this->client->getBody());

            if ($res['status'] !== 'OK'
                || $res['rows'][0]['elements'][0]['status'] === self::ZERO_RESULT_RESPONSE
            ) {
                throw new LocalizedException(
                    __(
                        'Unable to get distance between %1 and %2',
                        $sourceString,
                        $destinationString
                    )
                );
            }

            $element = $res['rows'][0]['elements'][0];

            if ($this->scopeConfig->getValue(self::XML_PATH_VALUE) === 'time') {
                $this->distanceCache[$key] = (float)$element['duration']['value'];
            } else {
                $this->distanceCache[$key] = (float)$element['distance']['value'];
            }
        }

        return $this->distanceCache[$key];
    }
}