Your might actually take it a step further and create a value object for the data, making it much more type safe. Notice I added PHP 7.4 proeprty typehints. I chosen float for the temperature (maybe it's not appropriate, idk), but i definitely don't know what to choose for the main
, but it sure deserves a typehint, if it's nested object, create another class for it or maybe pick the wanted information directly into the WeatherStatus
class.
final class WeatherStatus
{
private ??? $main;
private float $temperature;
public function __construct(??? $main, float $temperature)
{
$this->main = $main;
$this->temperature = $temperature;
}
public function getMain(): ???
{
return $this->main;
}
public function getTemperature(): float
{
return $this->temperature;
}
}
You can also define an interface for such a method, including a domain specific exception (because just halting the program with die
or exit
is not very nice thing to do, in that case it would be better to not catch the exception at all).
class WeatherProviderException extends \Exception
{
}
interface IWeatherProvider
{
/**
* @throws WeatherProviderException
*/
public function getWeather(): WeatherStatus;
}
In the implementation I would accept the api url rather then hardcoding it. You may add a static named constructor for the version 2.5 api.
The credentials for openweathermap.org
(whatever they are, let me assume a user name and password) might be also promoted to a class or may be passed to the provider constructor as multiple arguments as well.
final class OpenWeatherCredentials
{
private string $user;
private string $password;
// constructor + getters ...
}
class OpenWeatherProvider implements IWeatherProvider
{
private ClientInterface $client;
private string $apiUrl;
private OpenWeatherCredentials $credentials;
public function __construct(ClientInterface $client, string $apiUrl, OpenWeatherCredentials $credentials)
{
$this->client = $client;
$this->apiUrl = $apiUrl;
$this->credentials = $credentials;
}
public static function createV2_5(ClientInterface $client, OpenWeatherCredentials $credentials): self
{
return new self($client, 'https://api.openweathermap.org/data/2.5/weather', $credentials);
}
public function getWeather(): WeatherStatus
{
$options = [
// whatever is needed, $this->credentials->get*
];
try {
// here you called the object $request, but it really is a $response
$response = $this->client->get($this->apiUrl, $options);
} catch (GuzzleException $e) {
throw new WeatherProviderException($e->getMessage(), $e->getCode(), $e);
}
try {
$json = json_decode($response->getBody(), \JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
throw new WeatherProviderException($e->getMessage(), $e->getCode(), $e);
}
return new WeatherStatus(
$json->weather[0]->main,
(float) $json->main->temp,
);
}
}
Also notice how I wrap each statement in separate try-catch to only catch what can be caught. As long as they are handled the same way you try-catch both together but maybe you should catch their common ancestor exception instead to make it even simpler. And I pulled the instantiation of the WeatherStatus
out of the try-catch just because I don't expect it to throw GuzzleException
nor \JsonException
.