Yandex::getMethod()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 0
cts 3
cp 0
rs 10
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace CodeblogPro\GeoCoder\Providers;
6
7
use CodeblogPro\GeoCoder\Http\ResponseInterface;
8
use CodeblogPro\GeoCoordinates\Coordinates;
9
use CodeblogPro\GeoCoordinates\CoordinatesInterface;
10
use CodeblogPro\GeoCoder\Exceptions\InvalidRequestException;
11
use CodeblogPro\GeoCoder\Http\HttpClientInterface;
12
use CodeblogPro\GeoCoder\Http\Request;
13
use CodeblogPro\GeoLocationAddress\Country;
14
use CodeblogPro\GeoLocationAddress\Region;
15
use CodeblogPro\GeoLocationAddress\Location;
16
use CodeblogPro\GeoLocationAddress\LocationInterface;
17
18
/**
19
 * Class Yandex
20
 * You can find out more about this provider API here:
21
 * https://yandex.ru/dev/maps/geocoder/doc/desc/concepts/input_params.html
22
 *
23
 * @package CodeblogPro\GeoCoder\Providers
24
 */
25
class Yandex extends AbstractProvider implements ProviderInterface
26
{
27
    private const ENDPOINT_URL = 'https://geocode-maps.yandex.ru/1.x/?format=json';
28
    private const HTTP_VERSION = '1.1';
29
30
    private HttpClientInterface $client;
31
    private ?string $apiKey = null;
32
33
    public function __construct(HttpClientInterface $client, string $apiKey = null)
34
    {
35
        $this->client = $client;
36
        $this->apiKey = $apiKey;
37
    }
38
39
    public function getLocationByCoordinates(CoordinatesInterface $coordinates, string $locale = ''): LocationInterface
40
    {
41
        $request = new Request(
42
            $this->getMethod(),
43
            self::ENDPOINT_URL . $this->getKeyUrlParam() . $this->getLocaleUrlParamByLocale($locale)
44
            . $this->getCoordinatesUrlParamByCoordinates($coordinates) . $this->getSearchPrecisionUrlParam(),
45
            $this->getBody(),
46
            $this->getHeaders(),
47
            self::HTTP_VERSION
48
        );
49
50
        $response = $this->client->sendRequest($request);
51
52
        return $this->getLocationByResponse($response);
53
    }
54
55
    public function getLocationByAddress(string $address, string $locale = ''): LocationInterface
56
    {
57
        $request = new Request(
58
            $this->getMethod(),
59
            self::ENDPOINT_URL . $this->getKeyUrlParam() . $this->getLocaleUrlParamByLocale($locale)
60
            . $this->getAddressUrlParamByAddress($address) . $this->getSearchPrecisionUrlParam(),
61
            $this->getBody(),
62
            $this->getHeaders(),
63
            self::HTTP_VERSION
64
        );
65
66
        $response = $this->client->sendRequest($request);
67
68
        return $this->getLocationByResponse($response);
69
    }
70
71
    private function getKeyUrlParam(): string
72
    {
73
        return '&apikey=' . $this->apiKey;
74
    }
75
76
    private function getLocaleUrlParamByLocale(string $locale): string
77
    {
78
        if ($locale === 'en') {
79
            return '&lang=en_US';
80
        }
81
82
        return '&lang=ru_RU';
83
    }
84
85
    private function getCoordinatesUrlParamByCoordinates(CoordinatesInterface $coordinates): string
86
    {
87
        return '&geocode=' . $coordinates->getLongitude() . ',' . $coordinates->getLatitude();
88
    }
89
90
    private function getAddressUrlParamByAddress(string $address): string
91
    {
92
        $addressResult = '';
93
94
        foreach (explode(' ', $address) as $addressPart) {
95
            if (!empty(trim($addressPart))) {
96
                $addressResult .= '+' . trim($addressPart);
97
            }
98
        }
99
100
        $addressResult = '&geocode=' . $addressResult;
101
102
        return $addressResult;
103
    }
104
105
    private function getSearchPrecisionUrlParam(): string
106
    {
107
        // @ToDo: implement the ability to set the search accuracy. For example: return '&kind=locality';
108
        return '';
109
    }
110
111
    private function getMethod(): string
112
    {
113
        return 'GET';
114
    }
115
116
    private function getHeaders(): array
117
    {
118
        return [];
119
    }
120
121
    private function getBody(): string
122
    {
123
        return '';
124
    }
125
126
    private function getLocationByResponse(ResponseInterface $response): LocationInterface
127
    {
128
        $this->httpStatusCodeValidation((int)$response->getStatusCode());
129
130
        if (!$this->isJson($response->getBody())) {
131
            throw new InvalidRequestException('Invalid body format. JSON format expected.');
132
        }
133
134
        $body = json_decode($response->getBody(), true);
135
136
        $resultsCount = isset(
137
            $body['response']['GeoObjectCollection']['metaDataProperty']['GeocoderResponseMetaData']['results']
138
        ) ? (int)$body['response']['GeoObjectCollection']['metaDataProperty']['GeocoderResponseMetaData']['results']
139
            : 0;
140
141
        if ($resultsCount <= 0) {
142
            return new Location();
143
        }
144
145
        $generalizingLocationArray = [];
146
147
        foreach ($body['response']['GeoObjectCollection']['featureMember'] as $objectItem) {
148
            array_walk_recursive(
149
                $objectItem['GeoObject'],
150
                function ($value, $key) use (&$generalizingLocationArray) {
151
                    $generalizingLocationArray[$key] = $value;
152
                }
153
            );
154
        }
155
156
        if (!empty($body['response']['GeoObjectCollection']['metaDataProperty']['GeocoderResponseMetaData']['Point'])) {
157
            $metaDataProperty = $body['response']['GeoObjectCollection']['metaDataProperty'];
158
            $coordinatesString = $metaDataProperty['GeocoderResponseMetaData']['Point']['pos'];
159
        }
160
161
        if (empty($coordinatesString)) {
162
            $coordinatesString = $generalizingLocationArray['pos'];
163
        }
164
165
        $coordinatesArray = explode(' ', $coordinatesString);
166
        $coordinates = null;
167
168
        if (!empty($coordinatesArray[0]) && !empty($coordinatesArray[1])) {
169
            $coordinates = new Coordinates($coordinatesArray[1], $coordinatesArray[0]);
170
        }
171
172
        $country = null;
173
174
        if (!empty($generalizingLocationArray['CountryName'] || !empty($generalizingLocationArray['country_code']))) {
175
            $country = new Country(
176
                $generalizingLocationArray['CountryName'],
177
                $generalizingLocationArray['country_code']
178
            );
179
        }
180
181
        $region = null;
182
183
        if (!empty($generalizingLocationArray['AdministrativeAreaName'])) {
184
            $region = new Region($generalizingLocationArray['AdministrativeAreaName']);
185
        }
186
187
        return new Location(
188
            $coordinates,
189
            $country,
190
            $region,
191
            $generalizingLocationArray['LocalityName'] ?? '',
192
            $generalizingLocationArray['ThoroughfareName'] ?? '',
193
            $generalizingLocationArray['PostalCodeNumber'] ?? '',
194
        );
195
    }
196
}
197