geocoder-php / Geocoder

The most featured Geocoder library written in PHP.
https://geocoder-php.org
MIT License
3.94k stars 517 forks source link

Making latitude and longitude coordinates nullable #1143

Closed ActuallyConnor closed 2 years ago

ActuallyConnor commented 2 years ago

This proposed change will allow you to pass nullable arguments to the Coordinates class for the values of $latitude and $longitude.

Through using this library I have found myself every time creating a separate, custom Coordinates class that allows for nullable $latitude and $longitude values. There are many cases where you may wish to still instantiate a Coordinates object with one or both values being null, and then determining later on if the Coordinates class is usable by calling the hasLatitude() or hasLongitude() methods.

I am welcome to any and all feedback. This is after all a suggested change, and I won't take offence if it is not accepted.

Cheers! 😄

ActuallyConnor commented 2 years ago

@norkunas any chance you could review/run checks again, please?

norkunas commented 2 years ago

I'm not a maintainer of this repo, I maintain only geocoder-php/BazingaGeocoderBundle :)

From the logic pov I'm not sure - because without this PR getLatitude/getLongitude will return floats and doesn't throw, but after this PR it could throw, so I'd need to update code for these cases.

ActuallyConnor commented 2 years ago

Understood, I guess I will wait and see what is said then.

jbelien commented 2 years ago

Hello @ActuallyConnor ,

Thank you for your contribution.

I must admit I'm a bit confused by this request. Why would you create a location with only a longitude or only a latitude ? I can't really make sense of having coordinates with the longitude or latitude missing. The definition of coordinates is a longitude and a latitude.

There are many cases where you may wish to still instantiate a Coordinates object with one or both values being null, and then determining later on if the Coordinates class is usable by calling the hasLatitude() or hasLongitude() methods.

Could you give us some example of such use cases ?

ActuallyConnor commented 2 years ago

@jbelien so the use case we commonly have is reading and writing from nullable fields for latitude and longitude in the database. In the off chance that one is empty we would like the Coordinates object to still be created when reading from the database. And then when you wish to use the Coordinates you can check to see that they are set before accessing. Then on the access is will throw an exception if it is null. Hence the hasLatitude() and hasLongitude() methods.

Another use case is wanting to still return a Coordinates object in a function. See below for example.

public function geoCode(string $address, ?string $region = null) : Coordinates
{
    try {
        $coded = $this->geoCoder->geocodeQuery(GeocodeQuery::create($address)->withData('region', $region));
        $coordinates = $coded->first()->getCoordinates();

        return new Coordinates($coordinates->getLatitude(), $coordinates->getLongitude());
    } catch (Exception | GeocoderException $e) {
        return new Coordinates(null, null);
    }
}

In this case we created our own Coordinates class that allows for nullable properties. This ensure that the method itself does not return null.

If you don't see these use cases as good enough examples for as to why this PR would add value, then I understand. I just didn't like having to recreate the Coordinates class in order to meet these needs every time.

norkunas commented 2 years ago

In case geocoding cannot result coordinates, isn't it better to return null instead of a object with null values?

jbelien commented 2 years ago

In case geocoding cannot result coordinates, isn't it better to return null instead of a object with null values?

I fully agree with @norkunas !

public function geoCode(string $address, ?string $region = null) : Coordinates
{
    try {
        $coded = $this->geoCoder->geocodeQuery(GeocodeQuery::create($address)->withData('region', $region));
        return $coded->first()->getCoordinates();
    } catch (Exception | GeocoderException $e) {
        return  null;
    }
}

It makes no sense to have coordinates with "half" the parts. Coordinates must have both a longitude and a latitude, otherwise it's no coordinates.

What do you think @willdurand @Nyholm @atymic ?

mtmail commented 2 years ago

The try/catch seems a good enough, and my preferred, solution. I understand the specific usecase of nullable coordinates objects but I worry the average new user to the geocoder library would be too confused.

ActuallyConnor commented 2 years ago

I understand, no worries then. Thanks for at least taking the time to consider the PR 👍