Passed
Push — master ( 1e745f...b938a3 )
by Alexey
05:12
created

Yandex::getAddressUrlParamByAddress()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 12
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace CodeblogPro\GeoCoder\Providers;
6
7
use CodeblogPro\GeoCoordinates\Coordinates;
8
use CodeblogPro\GeoCoordinates\CoordinatesInterface;
9
use CodeblogPro\GeoCoder\Exception\InvalidRequestException;
10
use CodeblogPro\GeoCoder\Http\HttpClientInterface;
11
use CodeblogPro\GeoCoder\Http\Request;
12
use CodeblogPro\GeoCoder\Http\Response;
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
        $addressResult = '';
92
93
        foreach (explode(' ', $address) as $addressPart) {
94
            if (!empty(trim($addressPart))) {
95
                $addressResult .= '+' . trim($addressPart);
96
            }
97
        }
98
99
        $addressResult = '&geocode=' . $addressResult;
100
101
        return $addressResult;
102
    }
103
104
    private function getSearchPrecisionUrlParam(): string
105
    {
106
        // @ToDo: implement the ability to set the search accuracy. For example: return '&kind=locality';
107
        return '';
108
    }
109
110
    private function getMethod(): string
111
    {
112
        return 'GET';
113
    }
114
115
    private function getHeaders(): array
116
    {
117
        return [];
118
    }
119
120
    private function getBody(): string
121
    {
122
        return '';
123
    }
124
125
    private function getLocationByResponse(Response $response): LocationInterface
126
    {
127
        $this->httpStatusCodeValidation((int)$response->getStatusCode());
128
129
        if (!$this->isJson($response->getBody())) {
130
            throw new InvalidRequestException('Invalid body format. JSON format expected.');
131
        }
132
133
        $body = json_decode($response->getBody(), true);
134
135
        $resultsCount = isset($body['response']['GeoObjectCollection']['metaDataProperty']['GeocoderResponseMetaData']['results'])
136
            ? (int)$body['response']['GeoObjectCollection']['metaDataProperty']['GeocoderResponseMetaData']['results']
137
            : 0;
138
139
        if ($resultsCount <= 0) {
140
            return new Location();
141
        }
142
143
        $generalizingLocationArray = [];
144
145
        foreach ($body['response']['GeoObjectCollection']['featureMember'] as $objectItem) {
146
            array_walk_recursive(
147
                $objectItem['GeoObject'],
148
                function ($value, $key) use (&$generalizingLocationArray) {
149
                    $generalizingLocationArray[$key] = $value;
150
                }
151
            );
152
        }
153
154
        if (!empty($body['response']['GeoObjectCollection']['metaDataProperty']['GeocoderResponseMetaData']['Point'])) {
155
            $coordinatesString = $body['response']['GeoObjectCollection']['metaDataProperty']['GeocoderResponseMetaData']['Point']['pos'];
156
        }
157
158
        if (empty($coordinatesString)) {
159
            $coordinatesString = $generalizingLocationArray['pos'];
160
        }
161
162
        $coordinatesArray = explode(' ', $coordinatesString);
163
        $coordinates = null;
164
165
        if (!empty($coordinatesArray[0]) && !empty($coordinatesArray[1])) {
166
            $coordinates = new Coordinates($coordinatesArray[1], $coordinatesArray[0]);
167
        }
168
169
        $country = null;
170
171
        if (!empty($generalizingLocationArray['CountryName'] || !empty($generalizingLocationArray['country_code']))) {
172
            $country = new Country(
173
                $generalizingLocationArray['CountryName'],
174
                $generalizingLocationArray['country_code']
175
            );
176
        }
177
178
        $region = null;
179
180
        if (!empty($generalizingLocationArray['AdministrativeAreaName'])) {
181
            $region = new Region($generalizingLocationArray['AdministrativeAreaName']);
182
        }
183
184
        return new Location(
185
            $coordinates,
186
            $country,
187
            $region,
188
            $generalizingLocationArray['LocalityName'] ?? '',
189
            $generalizingLocationArray['ThoroughfareName'] ?? '',
190
            $generalizingLocationArray['PostalCodeNumber'] ?? '',
191
        );
192
    }
193
}
194