├── .env.example ├── .gitignore ├── .travis.yml ├── README.md ├── composer.json ├── phpunit.xml ├── src ├── Avoid.php ├── Contracts │ └── LicenseContract.php ├── DistanceMatrix.php ├── Frameworks │ └── Laravel │ │ ├── DistanceMatrix.php │ │ ├── DistanceMatrixServiceProvider.php │ │ └── google.php ├── Licenses │ ├── PremiumLicense.php │ └── StandardLicense.php ├── Request │ └── DistanceMatrixRequest.php ├── Response │ ├── DistanceMatrixResponse.php │ ├── Element.php │ └── Row.php ├── Stack │ └── LicenseMiddleware.php ├── TrafficModel.php ├── TransitMode.php ├── TransitRouting.php ├── TravelMode.php └── Unit.php └── tests ├── AbstractTestCase.php ├── DistanceMatrixRequestTest.php ├── DistanceMatrixTest.php ├── PremiumLicenseTest.php ├── StandardLicenseTest.php └── responses ├── norwich_to_ipswich.json └── with_duration_in_traffic.json /.env.example: -------------------------------------------------------------------------------- 1 | GOOGLE_MAPS_KEY= 2 | 3 | GOOGLE_MAPS_CLIENT_ID= 4 | GOOGLE_MAPS_ENC_KEY= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | .env 4 | 5 | .idea/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - '7.2' 4 | - '7.3' 5 | - '7.4' 6 | 7 | before_script: composer install 8 | script: ./vendor/bin/phpunit 9 | 10 | notifications: 11 | email: 12 | on_success: never 13 | on_failure: always 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Google Maps Distance Matrix API 2 | 3 | This is a simple package that allows access to the Google Maps Distance Matrix API using a (mostly) fluent API. 4 | There is support for both the Standard License and Premium/Enterprise License types provided by Google. 5 | 6 | ## Installation 7 | 8 | Install the package using composer: 9 | 10 | ``` 11 | $ composer require teampickr/php-google-maps-distance-matrix 12 | ``` 13 | 14 | ## Frameworks 15 | 16 | At the moment we only have framework compatibility for Laravel. However, we welcome PRs to add further framework 17 | specific behavior as long as it doesn't prevent the package working for others, or pull in dependencies that are 18 | not optional (suggested). 19 | 20 | ### Laravel 21 | 22 | If you are using Laravel then you can use our service provider. If you have Laravel >5.5 then the package 23 | will be auto discovered upon install. Else, add the following to your `config/app.php` file: 24 | 25 | ```php 26 | [ 29 | ... 30 | \TeamPickr\DistanceMatrix\Frameworks\Laravel\DistanceMatrixServiceProvider::class, 31 | ] 32 | ``` 33 | 34 | #### Facades 35 | 36 | Personally, I hate facades. However, I know people like them. If you are using Laravel >5.5 then the facade will 37 | be automatically discovered. Else, you can add it in your `config/app.php` file. 38 | 39 | ```php 40 | [ 43 | ... 44 | 'DistanceMatrix' => \TeamPickr\DistanceMatrix\Frameworks\Laravel\DistanceMatrix::class, 45 | ] 46 | ``` 47 | #### Configuration 48 | 49 | First, make sure you have copied the configuration file: 50 | 51 | ``` 52 | $ php artisan vendor:publish --tag=config 53 | ``` 54 | 55 | This will make a `config/google.php` file, this is where your API Key / License information is fetched from. 56 | By default we use the `.env` configuration values to get your API key. 57 | 58 | If you have a standard api key all you need to add to your `.env` is: 59 | 60 | ``` 61 | GOOGLE_MAPS_KEY=MY-API-KEY 62 | ``` 63 | 64 | If you are a Premium / Enterprise Google Maps user, and use a encryption key and client ID then you should add 65 | the following to your `.env`: 66 | 67 | ``` 68 | GOOGLE_LICENSE_TYPE=premium 69 | GOOGLE_MAPS_CLIENT_ID=MY-CLIENT-ID 70 | GOOGLE_MAPS_ENC_KEY=MY-ENCRYPTION-KEY 71 | ``` 72 | 73 | Please, make sure you don't store your keys in version control! 74 | 75 | ## Usage 76 | 77 | #### License / API Key 78 | 79 | Before making requests you need to create your License object. If you are a standard google maps user, then all 80 | you will need is your API key, then you can create your license as follows: 81 | 82 | ```php 83 | $license = new StandardLicense($apiKey); 84 | ``` 85 | 86 | If you are a Premium / Enterprise google maps user, then you need to create your license as follows: 87 | ```php 88 | $license = new PremiumLicense($clientId, $encryptionKey); 89 | ``` 90 | 91 | Then, you can start making your request: 92 | 93 | ```php 94 | $request = new DistanceMatrix($license); 95 | 96 | // or 97 | 98 | $request = DistanceMatrix::license($license); 99 | ``` 100 | 101 | #### Basic usage 102 | 103 | ```php 104 | $response = DistanceMatrix::license($license) 105 | ->addOrigin('norwich,gb') 106 | ->addDestination('52.603669, 1.223785') 107 | ->request(); 108 | 109 | // I want to make the following but of API better, 110 | // as it looks horrible at the moment. 111 | $rows = $response->rows(); 112 | $elements = $rows[0]->elements(); 113 | $element = $element[0]; 114 | 115 | $distance = $element->distance(); 116 | $distanceText = $element->distanceText(); 117 | $duration = $element->duration(); 118 | $durationText = $element->durationText(); 119 | $durationInTraffic = $element->durationInTraffic(); 120 | $durationInTrafficText = $element->durationInTrafficText(); 121 | 122 | // or 123 | 124 | $response->json['destination_addresses'][0]; 125 | $response->json['rows'][0]['elements'][0]['distance']['value']; 126 | $response->json['rows'][0]['elements'][0]['duration_in_traffic']['text']; 127 | ``` 128 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "teampickr/php-google-maps-distance-matrix", 3 | "description": "PHP Implementation of the Google Maps Distance Matrix API", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Jonathan Martin", 9 | "email": "jm@pickr.works" 10 | } 11 | ], 12 | "require": { 13 | "php": ">7.2", 14 | "guzzlehttp/guzzle": "^7.0.1" 15 | }, 16 | "require-dev": { 17 | "phpunit/phpunit": "^8.5.0", 18 | "vlucas/phpdotenv": "^5.2.0", 19 | "illuminate/support": ">5.4" 20 | }, 21 | "suggest": { 22 | "illuminate/support": "Allows you to use this package from within Laravel" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "TeamPickr\\DistanceMatrix\\": "src/", 27 | "TeamPickr\\DistanceMatrix\\Tests\\": "tests/" 28 | } 29 | }, 30 | "extra": { 31 | "laravel": { 32 | "providers": [ 33 | "TeamPickr\\DistanceMatrix\\Frameworks\\Laravel\\DistanceMatrixServiceProvider" 34 | ], 35 | "aliases": { 36 | "DistanceMatrix": "TeamPickr\\DistanceMatrix\\Frameworks\\Laravel\\DistanceMatrix" 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ./tests 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/Avoid.php: -------------------------------------------------------------------------------- 1 | license = $license; 85 | } 86 | 87 | /** 88 | * @param \TeamPickr\DistanceMatrix\Contracts\LicenseContract $license 89 | * 90 | * @return static 91 | */ 92 | public static function license(LicenseContract $license) 93 | { 94 | return new static($license); 95 | } 96 | 97 | /** 98 | * @return LicenseContract 99 | */ 100 | public function getLicense(): LicenseContract 101 | { 102 | return $this->license; 103 | } 104 | 105 | /** 106 | * @param LicenseContract $license 107 | * 108 | * @return DistanceMatrix 109 | */ 110 | public function setLicense(LicenseContract $license) 111 | { 112 | $this->license = $license; 113 | 114 | return $this; 115 | } 116 | 117 | /** 118 | * @param string $origin 119 | * 120 | * @return $this 121 | */ 122 | public function addOrigin(string $origin) 123 | { 124 | $this->origins[] = $origin; 125 | 126 | return $this; 127 | } 128 | 129 | /** 130 | * @return array 131 | */ 132 | public function getOrigins(): array 133 | { 134 | return $this->origins; 135 | } 136 | 137 | /** 138 | * @param string $destination 139 | * 140 | * @return $this 141 | */ 142 | public function addDestination(string $destination) 143 | { 144 | $this->destinations[] = $destination; 145 | 146 | return $this; 147 | } 148 | 149 | /** 150 | * @return array 151 | */ 152 | public function getDestinations(): array 153 | { 154 | return $this->destinations; 155 | } 156 | 157 | /** 158 | * @return string 159 | */ 160 | public function getMode(): string 161 | { 162 | return $this->mode; 163 | } 164 | 165 | /** 166 | * @param string $mode 167 | * 168 | * @return DistanceMatrix 169 | */ 170 | public function setMode(string $mode) 171 | { 172 | $this->mode = $mode; 173 | 174 | return $this; 175 | } 176 | 177 | /** 178 | * @return DistanceMatrixResponse 179 | */ 180 | public function request() 181 | { 182 | return (new DistanceMatrixRequest($this))->request(); 183 | } 184 | 185 | /** 186 | * @return null 187 | */ 188 | public function getLanguage() 189 | { 190 | return $this->language; 191 | } 192 | 193 | /** 194 | * @param null $language 195 | * 196 | * @return DistanceMatrix 197 | */ 198 | public function setLanguage($language) 199 | { 200 | $this->language = $language; 201 | 202 | return $this; 203 | } 204 | 205 | /** 206 | * @return null 207 | */ 208 | public function getArrivalTime() 209 | { 210 | return $this->arrivalTime; 211 | } 212 | 213 | /** 214 | * @param int|\DateTime $arrivalTime 215 | * 216 | * @return DistanceMatrix 217 | */ 218 | public function setArrivalTime($arrivalTime) 219 | { 220 | if ($arrivalTime instanceof DateTime) { 221 | $this->arrivalTime = $arrivalTime->getTimestamp(); 222 | } else { 223 | $this->arrivalTime = $arrivalTime; 224 | } 225 | 226 | return $this; 227 | } 228 | 229 | /** 230 | * @return null 231 | */ 232 | public function getDepartureTime() 233 | { 234 | return $this->departureTime; 235 | } 236 | 237 | /** 238 | * @param null $departureTime 239 | * 240 | * @return DistanceMatrix 241 | */ 242 | public function setDepartureTime($departureTime) 243 | { 244 | if ($departureTime instanceof DateTime) { 245 | $this->departureTime = $departureTime->getTimestamp(); 246 | } else { 247 | $this->departureTime = $departureTime; 248 | } 249 | 250 | return $this; 251 | } 252 | 253 | /** 254 | * @return string|null 255 | */ 256 | public function getTrafficModel() 257 | { 258 | return $this->trafficModel; 259 | } 260 | 261 | /** 262 | * @param string $trafficModel 263 | * 264 | * @return DistanceMatrix 265 | */ 266 | public function setTrafficModel(string $trafficModel) 267 | { 268 | $this->trafficModel = $trafficModel; 269 | 270 | return $this; 271 | } 272 | 273 | /** 274 | * @return array 275 | */ 276 | public function getTransitMode() 277 | { 278 | return $this->transitMode; 279 | } 280 | 281 | /** 282 | * @param string $transitMode 283 | * 284 | * @return DistanceMatrix 285 | */ 286 | public function addTransitMode(string $transitMode) 287 | { 288 | $this->transitMode[] = $transitMode; 289 | 290 | return $this; 291 | } 292 | 293 | /** 294 | * @return string 295 | */ 296 | public function getTransitRoutingPreference() 297 | { 298 | return $this->transitRoutingPreference; 299 | } 300 | 301 | /** 302 | * @param string $transitRoutingPreference 303 | * 304 | * @return DistanceMatrix 305 | */ 306 | public function setTransitRoutingPreference(string $transitRoutingPreference) 307 | { 308 | $this->transitRoutingPreference = $transitRoutingPreference; 309 | 310 | return $this; 311 | } 312 | 313 | /** 314 | * @return string 315 | */ 316 | public function getUnits() 317 | { 318 | return $this->units; 319 | } 320 | 321 | /** 322 | * @param string $units 323 | * 324 | * @return DistanceMatrix 325 | */ 326 | public function setUnits(string $units) 327 | { 328 | $this->units = $units; 329 | 330 | return $this; 331 | } 332 | 333 | /** 334 | * @return DistanceMatrix 335 | */ 336 | public function useMetricUnits() 337 | { 338 | return $this->setUnits(Unit::METRIC); 339 | } 340 | 341 | /** 342 | * @return DistanceMatrix 343 | */ 344 | public function useImperialUnits() 345 | { 346 | return $this->setUnits(Unit::IMPERIAL); 347 | } 348 | 349 | /** 350 | * @return string 351 | */ 352 | public function getRegion() 353 | { 354 | return $this->region; 355 | } 356 | 357 | /** 358 | * @param string $region 359 | * 360 | * @return DistanceMatrix 361 | */ 362 | public function setRegion(string $region) 363 | { 364 | $this->region = $region; 365 | 366 | return $this; 367 | } 368 | 369 | /** 370 | * @return string 371 | */ 372 | public function getAvoid() 373 | { 374 | return $this->avoid; 375 | } 376 | 377 | /** 378 | * @param string $avoid 379 | * 380 | * @return DistanceMatrix 381 | */ 382 | public function setAvoid(string $avoid) 383 | { 384 | $this->avoid = $avoid; 385 | 386 | return $this; 387 | } 388 | 389 | /** 390 | * @return \TeamPickr\DistanceMatrix\DistanceMatrix 391 | */ 392 | public function avoidTolls() 393 | { 394 | return $this->setAvoid(Avoid::TOLLS); 395 | } 396 | 397 | /** 398 | * @return \TeamPickr\DistanceMatrix\DistanceMatrix 399 | */ 400 | public function avoidHighways() 401 | { 402 | return $this->setAvoid(Avoid::HIGHWAYS); 403 | } 404 | 405 | /** 406 | * @return \TeamPickr\DistanceMatrix\DistanceMatrix 407 | */ 408 | public function avoidFerries() 409 | { 410 | return $this->setAvoid(Avoid::FERRIES); 411 | } 412 | 413 | /** 414 | * @return \TeamPickr\DistanceMatrix\DistanceMatrix 415 | */ 416 | public function avoidIndoor() 417 | { 418 | return $this->setAvoid(Avoid::INDOOR); 419 | } 420 | 421 | } -------------------------------------------------------------------------------- /src/Frameworks/Laravel/DistanceMatrix.php: -------------------------------------------------------------------------------- 1 | app->bind(DistanceMatrix::class, function () { 18 | return new DistanceMatrix($this->getLicense()); 19 | }); 20 | } 21 | 22 | /** 23 | * Boot our packages services 24 | */ 25 | public function boot() 26 | { 27 | $this->mergeConfigFrom(__DIR__ . '/google.php', 'google'); 28 | 29 | $this->publishes([ 30 | __DIR__ . '/google.php' => config_path('google.php'), 31 | ], 'config'); 32 | } 33 | 34 | /** 35 | * @return \TeamPickr\DistanceMatrix\Contracts\LicenseContract 36 | */ 37 | protected function getLicense() 38 | { 39 | if (config('google.license_type') === 'premium') { 40 | return new PremiumLicense(config('google.client_id'), config('google.encryption_key')); 41 | } 42 | 43 | return new StandardLicense(config('google.key')); 44 | } 45 | } -------------------------------------------------------------------------------- /src/Frameworks/Laravel/google.php: -------------------------------------------------------------------------------- 1 | env('GOOGLE_LICENSE_TYPE', 'standard'), 5 | 6 | 'key' => env('GOOGLE_KEY', ''), 7 | 8 | 'client_id' => env('GOOGLE_CLIENT_ID', ''), 9 | 'encryption_key' => env('GOOGLE_ENCRYPTION_KEY', ''), 10 | ]; -------------------------------------------------------------------------------- /src/Licenses/PremiumLicense.php: -------------------------------------------------------------------------------- 1 | key = $encryptionKey; 29 | $this->clientId = $clientId; 30 | } 31 | 32 | /** 33 | * @return string 34 | */ 35 | private function getDecodedKey() 36 | { 37 | return base64_decode(strtr($this->key, '-_,', '+/=')); 38 | } 39 | 40 | /** 41 | * @param \GuzzleHttp\Psr7\Request $request 42 | * 43 | * @return string 44 | */ 45 | protected function urlWithClientId(Request $request) 46 | { 47 | $uri = $request->getUri(); 48 | 49 | return $uri->getPath() . "?" . $uri->getQuery() . "&client=" . $this->clientId; 50 | } 51 | 52 | /** 53 | * @param \GuzzleHttp\Psr7\Request $request 54 | * 55 | * @return string 56 | */ 57 | protected function createSignature(Request $request) 58 | { 59 | return strtr( 60 | base64_encode( 61 | hash_hmac('sha1', $this->urlWithClientId($request), $this->getDecodedKey(), true) 62 | ), 63 | '+/=', 64 | '-_,' 65 | ); 66 | } 67 | 68 | /** 69 | * @param \GuzzleHttp\Psr7\Request $request 70 | * 71 | * @return array 72 | */ 73 | public function getQueryStringParameters(Request $request): array 74 | { 75 | return [ 76 | 'client' => $this->clientId, 77 | 'signature' => $this->createSignature($request), 78 | ]; 79 | } 80 | } -------------------------------------------------------------------------------- /src/Licenses/StandardLicense.php: -------------------------------------------------------------------------------- 1 | key = $key; 30 | } 31 | 32 | /** 33 | * @param \GuzzleHttp\Psr7\Request $request 34 | * 35 | * @return array 36 | */ 37 | public function getQueryStringParameters(Request $request): array 38 | { 39 | return [ 40 | 'key' => $this->key, 41 | ]; 42 | } 43 | } -------------------------------------------------------------------------------- /src/Request/DistanceMatrixRequest.php: -------------------------------------------------------------------------------- 1 | settings = $settings; 46 | $this->stack = HandlerStack::create($handler ?? new CurlHandler()); 47 | $this->client = new Client([ 48 | 'base_uri' => static::BASE_URI, 49 | 'handler' => $this->stack, 50 | ]); 51 | 52 | $this->pushMiddleware(new LicenseMiddleware($this->settings->getLicense())); 53 | } 54 | 55 | /** 56 | * @param callable $middleware 57 | * 58 | * @return $this 59 | */ 60 | public function pushMiddleware(callable $middleware) 61 | { 62 | $this->stack->push($middleware); 63 | 64 | return $this; 65 | } 66 | 67 | /** 68 | * @return array 69 | */ 70 | protected function buildQuery() 71 | { 72 | $options = array_merge([ 73 | 'origins' => $this->buildOrigins(), 74 | 'destinations' => $this->buildDestinations(), 75 | 'mode' => $this->settings->getMode(), 76 | 'units' => $this->settings->getUnits(), 77 | 'avoid' => $this->settings->getAvoid(), 78 | 'region' => $this->settings->getRegion(), 79 | 'language' => $this->settings->getLanguage(), 80 | 'arrival_time' => $this->settings->getArrivalTime(), 81 | 'departure_time' => $this->settings->getDepartureTime(), 82 | 'traffic_model' => $this->settings->getTrafficModel(), 83 | 'transit_mode' => $this->buildTransitMode(), 84 | 'transit_routing_preference' => $this->settings->getTransitRoutingPreference(), 85 | ]); 86 | 87 | return array_filter($options, function ($value) { 88 | return $value !== null || $value === ""; 89 | }); 90 | } 91 | 92 | /** 93 | * @return string 94 | */ 95 | protected function buildOrigins() 96 | { 97 | return implode("|", $this->settings->getOrigins()); 98 | } 99 | 100 | /** 101 | * @return string 102 | */ 103 | protected function buildDestinations() 104 | { 105 | return implode("|", $this->settings->getDestinations()); 106 | } 107 | 108 | /** 109 | * @return string 110 | */ 111 | protected function buildTransitMode() 112 | { 113 | return implode("|", $this->settings->getTransitMode()); 114 | } 115 | 116 | /** 117 | * @return DistanceMatrixResponse 118 | */ 119 | public function request() 120 | { 121 | return new DistanceMatrixResponse( 122 | $this->client->get('json', [ 123 | 'query' => $this->buildQuery(), 124 | ]) 125 | ); 126 | } 127 | } -------------------------------------------------------------------------------- /src/Response/DistanceMatrixResponse.php: -------------------------------------------------------------------------------- 1 | response = $response; 34 | $this->json = json_decode($response->getBody()->getContents(), true); 35 | } 36 | 37 | /** 38 | * @return bool 39 | */ 40 | public function successful(): bool 41 | { 42 | return $this->json['status'] === static::RESPONSE_OKAY; 43 | } 44 | 45 | /** 46 | * @return null|string 47 | */ 48 | public function error() 49 | { 50 | switch ($this->json['status']) { 51 | 52 | case static::RESPONSE_INVALID_REQUEST: 53 | return "Request provided was invalid."; 54 | 55 | case static::RESPONSE_MAX_ELEMENTS_EXCEEDED: 56 | return "Too many origins or destinations provided."; 57 | 58 | case static::RESPONSE_OVER_QUERY_LIMIT: 59 | return "You have exceeded the amount of API requests allowed in this time period."; 60 | 61 | case static::RESPONSE_REQUEST_DENIED: 62 | return "Your request was denied. Incorrect authentication?"; 63 | 64 | case static::RESPONSE_UNKNOWN_ERROR: 65 | return "Unknown error occurred."; 66 | 67 | default: 68 | return null; 69 | } 70 | 71 | } 72 | 73 | /** 74 | * @return array 75 | */ 76 | public function origins(): array 77 | { 78 | return $this->json["origin_addresses"]; 79 | } 80 | 81 | /** 82 | * @return array 83 | */ 84 | public function destinations(): array 85 | { 86 | return $this->json["destination_addresses"]; 87 | } 88 | 89 | /** 90 | * @return array 91 | */ 92 | public function rows(): array 93 | { 94 | $rows = $this->json['rows']; 95 | 96 | if (!count($rows)) { 97 | return []; 98 | } 99 | 100 | return array_map(function ($row) { 101 | return new Row($row); 102 | }, $rows); 103 | } 104 | 105 | /** 106 | * @param int $row 107 | * 108 | * @return null|\TeamPickr\DistanceMatrix\Response\Row 109 | */ 110 | public function row(int $row = 0) 111 | { 112 | $rows = $this->rows(); 113 | 114 | if (array_key_exists($row, $rows)) { 115 | return $rows[$row]; 116 | } 117 | 118 | return null; 119 | } 120 | } -------------------------------------------------------------------------------- /src/Response/Element.php: -------------------------------------------------------------------------------- 1 | element = $element; 25 | } 26 | 27 | /** 28 | * @return bool 29 | */ 30 | public function successful(): bool 31 | { 32 | return $this->element['status'] == static::STATUS_OKAY; 33 | } 34 | 35 | /** 36 | * @return null|int 37 | */ 38 | public function distance() 39 | { 40 | return ! empty($this->element['distance']['value']) ? $this->element['distance']['value'] : null; 41 | } 42 | 43 | /** 44 | * @return null|string 45 | */ 46 | public function distanceText() 47 | { 48 | return ! empty($this->element['distance']['text']) ? $this->element['distance']['text'] : null; 49 | } 50 | 51 | /** 52 | * @return null|int 53 | */ 54 | public function duration() 55 | { 56 | return ! empty($this->element['duration']['value']) ? (int) $this->element['duration']['value'] : null; 57 | } 58 | 59 | /** 60 | * @return null|string 61 | */ 62 | public function durationText() 63 | { 64 | return ! empty($this->element['duration']['text']) ? (string) $this->element['duration']['text'] : null; 65 | } 66 | 67 | /** 68 | * @return null|int 69 | */ 70 | public function durationInTraffic() 71 | { 72 | return ! empty($this->element['duration_in_traffic']['value']) ? (int) $this->element['duration_in_traffic']['value'] : null; 73 | } 74 | /** 75 | * @return null|string 76 | */ 77 | public function durationInTrafficText() 78 | { 79 | return ! empty($this->element['duration_in_traffic']['text']) ? (string) $this->element['duration_in_traffic']['text'] : null; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Response/Row.php: -------------------------------------------------------------------------------- 1 | row = $row; 20 | } 21 | 22 | /** 23 | * @return array 24 | */ 25 | public function elements() 26 | { 27 | $elements = $this->row['elements']; 28 | 29 | if(!count($elements)) { 30 | return []; 31 | } 32 | 33 | return array_map(function($element) { 34 | return new Element($element); 35 | }, $elements); 36 | } 37 | 38 | /** 39 | * @param int $element 40 | * 41 | * @return \TeamPickr\DistanceMatrix\Response\Element 42 | */ 43 | public function element(int $element = 0) 44 | { 45 | $elements = $this->elements(); 46 | 47 | if(array_key_exists($element, $elements)) { 48 | return $elements[$element]; 49 | } 50 | 51 | return null; 52 | } 53 | } -------------------------------------------------------------------------------- /src/Stack/LicenseMiddleware.php: -------------------------------------------------------------------------------- 1 | license = $license; 24 | } 25 | 26 | /** 27 | * @param callable $next 28 | * 29 | * @return \Closure 30 | */ 31 | public function __invoke(callable $next) 32 | { 33 | return function (RequestInterface $request, array $options) use ($next) { 34 | return $next($this->addLicense($request), $options); 35 | }; 36 | } 37 | 38 | /** 39 | * @param \Psr\Http\Message\RequestInterface $request 40 | * 41 | * @return \Psr\Http\Message\RequestInterface 42 | */ 43 | protected function addLicense(RequestInterface $request) 44 | { 45 | $query = $this->license->getQueryStringParameters($request); 46 | 47 | if (count($query)) { 48 | foreach ($query as $key => $value) { 49 | $request = $request->withUri(Uri::withQueryValue($request->getUri(), $key, $value)); 50 | } 51 | } 52 | 53 | return $request; 54 | } 55 | } -------------------------------------------------------------------------------- /src/TrafficModel.php: -------------------------------------------------------------------------------- 1 | load(); 34 | } 35 | 36 | $this->key = getenv('GOOGLE_MAPS_KEY'); 37 | } 38 | 39 | /** 40 | * @return DistanceMatrix 41 | */ 42 | public function newInstance() 43 | { 44 | return new DistanceMatrix(new StandardLicense($this->key)); 45 | } 46 | 47 | /** 48 | * @param DistanceMatrix $distanceMatrix 49 | * @param \GuzzleHttp\Handler\MockHandler $mockHandler 50 | * 51 | * @return DistanceMatrixRequest 52 | */ 53 | public function makeTestRequest(DistanceMatrix $distanceMatrix, MockHandler $mockHandler = null) 54 | { 55 | return (new DistanceMatrixRequest($distanceMatrix, $mockHandler)) 56 | ->pushMiddleware(Middleware::history($this->container)); 57 | } 58 | 59 | /** 60 | * @return \GuzzleHttp\Handler\MockHandler 61 | */ 62 | public function makeSuccessfulMockHandler() 63 | { 64 | return new MockHandler([ 65 | new Response(200, [], file_get_contents(__DIR__ . '/responses/norwich_to_ipswich.json')), 66 | ]); 67 | } 68 | 69 | 70 | /** 71 | * @return \GuzzleHttp\Handler\MockHandler 72 | */ 73 | public function makeSuccessfulWithDurationInTrafficMockHandler() 74 | { 75 | return new MockHandler([ 76 | new Response(200, [], file_get_contents(__DIR__ . '/responses/with_duration_in_traffic.json')), 77 | ]); 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /tests/DistanceMatrixRequestTest.php: -------------------------------------------------------------------------------- 1 | newInstance() 18 | ->addOrigin('norwich,gb') 19 | ->addDestination('ipswich,gb') 20 | ->setLanguage('en-GB'); 21 | 22 | $this->makeTestRequest($distanceMatrix, $this->makeSuccessfulMockHandler())->request(); 23 | 24 | $request = $this->container[0]['request']; 25 | 26 | $this->assertStringContainsString("language=en-GB", $request->getUri()->getQuery()); 27 | } 28 | 29 | /** @test */ 30 | public function adds_arrival_time_to_request() 31 | { 32 | $date = new DateTime('2019-01-01 15:00:00'); 33 | 34 | $distanceMatrix = $this->newInstance() 35 | ->addOrigin('norwich,gb') 36 | ->addDestination('ipswich,gb') 37 | ->setArrivalTime($date); 38 | 39 | $this->makeTestRequest($distanceMatrix, $this->makeSuccessfulMockHandler())->request(); 40 | 41 | $request = $this->container[0]['request']; 42 | 43 | $this->assertStringContainsString("arrival_time=" . $date->getTimestamp(), $request->getUri()->getQuery()); 44 | } 45 | 46 | /** @test */ 47 | public function adds_departure_time_to_request() 48 | { 49 | $date = new DateTime('2019-01-02 15:00:00'); 50 | 51 | $distanceMatrix = $this->newInstance() 52 | ->addOrigin('norwich,gb') 53 | ->addDestination('ipswich,gb') 54 | ->setDepartureTime($date); 55 | 56 | $this->makeTestRequest($distanceMatrix, $this->makeSuccessfulMockHandler())->request(); 57 | 58 | $request = $this->container[0]['request']; 59 | 60 | $this->assertStringContainsString("departure_time=" . $date->getTimestamp(), $request->getUri()->getQuery()); 61 | } 62 | 63 | /** @test */ 64 | public function adds_traffic_model_to_request() 65 | { 66 | $distanceMatrix = $this->newInstance() 67 | ->addOrigin('norwich,gb') 68 | ->addDestination('ipswich,gb') 69 | ->setTrafficModel(TrafficModel::OPTIMISTIC); 70 | 71 | $this->makeTestRequest($distanceMatrix, $this->makeSuccessfulMockHandler())->request(); 72 | 73 | $request = $this->container[0]['request']; 74 | 75 | $this->assertStringContainsString("traffic_model=optimistic", $request->getUri()->getQuery()); 76 | } 77 | 78 | /** @test */ 79 | public function adds_avoid_to_request() 80 | { 81 | $distanceMatrix = $this->newInstance() 82 | ->addOrigin('norwich,gb') 83 | ->addDestination('ipswich,gb') 84 | ->avoidHighways(); 85 | 86 | $this->makeTestRequest($distanceMatrix, $this->makeSuccessfulMockHandler())->request(); 87 | 88 | $request = $this->container[0]['request']; 89 | 90 | $this->assertStringContainsString("avoid=highways", $request->getUri()->getQuery()); 91 | } 92 | 93 | /** @test */ 94 | public function adds_transit_mode_to_request() 95 | { 96 | $distanceMatrix = $this->newInstance() 97 | ->addOrigin('norwich,gb') 98 | ->addDestination('ipswich,gb') 99 | ->addTransitMode(TransitMode::RAIL) 100 | ->addTransitMode(TransitMode::BUS); 101 | 102 | $this->makeTestRequest($distanceMatrix, $this->makeSuccessfulMockHandler())->request(); 103 | 104 | $request = $this->container[0]['request']; 105 | 106 | $this->assertStringContainsString("transit_mode=" . urlencode("rail|bus"), $request->getUri()->getQuery()); 107 | } 108 | 109 | /** @test */ 110 | public function adds_transit_routing_preference_request() 111 | { 112 | $distanceMatrix = $this->newInstance() 113 | ->addOrigin('norwich,gb') 114 | ->addDestination('ipswich,gb') 115 | ->addTransitMode(TransitMode::RAIL) 116 | ->setTransitRoutingPreference(TransitRouting::LESS_WALKING); 117 | 118 | $this->makeTestRequest($distanceMatrix, $this->makeSuccessfulMockHandler())->request(); 119 | 120 | $request = $this->container[0]['request']; 121 | 122 | $this->assertStringContainsString("transit_mode=rail", $request->getUri()->getQuery()); 123 | $this->assertStringContainsString("transit_routing_preference=less_walking", $request->getUri()->getQuery()); 124 | } 125 | 126 | /** @test */ 127 | public function adds_units_to_request() 128 | { 129 | $distanceMatrix = $this->newInstance() 130 | ->addOrigin('norwich,gb') 131 | ->addDestination('ipswich,gb') 132 | ->useMetricUnits(); 133 | 134 | $this->makeTestRequest($distanceMatrix, $this->makeSuccessfulMockHandler())->request(); 135 | 136 | $request = $this->container[0]['request']; 137 | 138 | $this->assertStringContainsString("units=metric", $request->getUri()->getQuery()); 139 | } 140 | 141 | /** @test */ 142 | public function adds_region_to_request() 143 | { 144 | $distanceMatrix = $this->newInstance() 145 | ->addOrigin('norwich,gb') 146 | ->addDestination('ipswich,gb') 147 | ->setRegion('GB'); 148 | 149 | $this->makeTestRequest($distanceMatrix, $this->makeSuccessfulMockHandler())->request(); 150 | 151 | $request = $this->container[0]['request']; 152 | 153 | $this->assertStringContainsString("region=GB", $request->getUri()->getQuery()); 154 | } 155 | 156 | /** @test */ 157 | public function can_make_request() 158 | { 159 | $distanceMatrix = $this->newInstance()->addOrigin('norwich,gb') 160 | ->addDestination('ipswich,gb'); 161 | 162 | $response = $this->makeTestRequest($distanceMatrix, $this->makeSuccessfulMockHandler())->request(); 163 | 164 | $element = $response->rows()[0]->elements()[0]; 165 | 166 | $this->assertInstanceOf(DistanceMatrixResponse::class, $response); 167 | $this->assertInstanceOf(Element::class, $element); 168 | $this->assertTrue($response->successful()); 169 | $this->assertNotNull($response->successful()); 170 | } 171 | 172 | 173 | /** @test */ 174 | public function return_duration_in_traffic_request() 175 | { 176 | $date = 'now'; 177 | 178 | $distanceMatrix = $this->newInstance() 179 | ->addOrigin('Campinas, SP, BR') 180 | ->addDestination('Campinas - SP, Brasil') 181 | ->setArrivalTime($date); 182 | 183 | $response = $this->makeTestRequest($distanceMatrix, $this->makeSuccessfulWithDurationInTrafficMockHandler())->request(); 184 | $element = $response->rows()[0]->elements()[0]; 185 | 186 | $request = $this->container[0]['request']; 187 | 188 | $this->assertStringContainsString("arrival_time=" . $date, $request->getUri()->getQuery()); 189 | $this->assertInstanceOf(DistanceMatrixResponse::class, $response); 190 | $this->assertTrue($response->successful()); 191 | $this->assertNotNull($response->successful()); 192 | $this->assertInstanceOf(Element::class, $element); 193 | $this->assertEquals(5496, $element->durationInTraffic()); 194 | $this->assertEquals('1 hora 32 minutos', $element->durationInTrafficText()); 195 | 196 | } 197 | } -------------------------------------------------------------------------------- /tests/DistanceMatrixTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(DistanceMatrix::class, $this->newInstance()); 17 | } 18 | 19 | /** @test */ 20 | public function can_add_origin() 21 | { 22 | $instance = $this->newInstance()->addOrigin("norwich,gb"); 23 | 24 | $this->assertContains("norwich,gb", $instance->getOrigins()); 25 | } 26 | 27 | /** @test */ 28 | public function can_add_destination() 29 | { 30 | $instance = $this->newInstance()->addOrigin("ipswich,gb"); 31 | 32 | $this->assertContains("ipswich,gb", $instance->getOrigins()); 33 | } 34 | 35 | /** @test */ 36 | public function default_mode_is_driving() 37 | { 38 | $this->assertEquals('driving', $this->newInstance()->getMode()); 39 | } 40 | 41 | /** @test */ 42 | public function can_set_mode() 43 | { 44 | $instance = $this->newInstance()->setMode(TravelMode::CYCLING); 45 | 46 | $this->assertEquals('bicycling', $instance->getMode()); 47 | } 48 | } -------------------------------------------------------------------------------- /tests/PremiumLicenseTest.php: -------------------------------------------------------------------------------- 1 | addOrigin('norwich,gb') 19 | ->addDestination('ipswich,gb'); 20 | 21 | $mock = new MockHandler([new Response(200)]); 22 | 23 | $this->makeTestRequest($distanceMatrix, $mock)->request(); 24 | 25 | $request = $this->container[0]['request']; 26 | 27 | $this->assertStringContainsString("signature=", $request->getUri()->getQuery()); 28 | $this->assertStringContainsString("client=" . getenv('GOOGLE_MAPS_CLIENT_ID'), $request->getUri()->getQuery()); 29 | } 30 | } -------------------------------------------------------------------------------- /tests/StandardLicenseTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(['key' => 'key'], 17 | $license->getQueryStringParameters(new Request('GET', 'https://google.com/'))); 18 | } 19 | } -------------------------------------------------------------------------------- /tests/responses/norwich_to_ipswich.json: -------------------------------------------------------------------------------- 1 | { 2 | "destination_addresses" : [ "Ipswich, UK" ], 3 | "origin_addresses" : [ "Norwich, UK" ], 4 | "rows" : [ 5 | { 6 | "elements" : [ 7 | { 8 | "distance" : { 9 | "text" : "71.6 km", 10 | "value" : 71611 11 | }, 12 | "duration" : { 13 | "text" : "1 hour 19 mins", 14 | "value" : 4721 15 | }, 16 | "status" : "OK" 17 | } 18 | ] 19 | } 20 | ], 21 | "status" : "OK" 22 | } -------------------------------------------------------------------------------- /tests/responses/with_duration_in_traffic.json: -------------------------------------------------------------------------------- 1 | { 2 | "destination_addresses" : [ "São Paulo, SP, Brasil" ], 3 | "origin_addresses" : [ "Campinas - SP, Brasil" ], 4 | "rows" : [ 5 | { 6 | "elements" : [ 7 | { 8 | "distance" : { 9 | "text" : "95,8 km", 10 | "value" : 95845 11 | }, 12 | "duration" : { 13 | "text" : "1 hora 14 minutos", 14 | "value" : 4445 15 | }, 16 | "duration_in_traffic" : { 17 | "text" : "1 hora 32 minutos", 18 | "value" : 5496 19 | }, 20 | "status" : "OK" 21 | } 22 | ] 23 | } 24 | ], 25 | "status" : "OK" 26 | } --------------------------------------------------------------------------------