-
Notifications
You must be signed in to change notification settings - Fork 523
Implement a Query object #583
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
src/Model/Query/GeocodeQuery.php
Outdated
| */ | ||
| public function withBounds(Bounds $bounds) | ||
| { | ||
| $this->bounds = $bounds; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to make query object immutable, i.e.
$query = clone $this;
$query->bounds = $bounds;GeocodeQuery is a value object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about making them immutable. I will make the query objects easier to reused and you will not get any unexpected behavior. Though, they are "builder". I compare the GeocodeQuery to Doctrines QueryBuilder. It would not make much sense to have the QueryBuilder immutable, would it? Or maybe Im missing something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would not make much sense to have the QueryBuilder immutable, would it?
I'm not that familiar with Doctrine's QB, but as I can see:
- query builder is not perfect match for
GeocoderQuery, query builder is not a query, it is a service; query, on other hand, is a message, queries should be compared by their values -> equality is not based on identity; - query builder probably was designed in the mutable way because of performance reasons, because cloning that object is more expensive than small object like
GeocoderQuery; and number of method calls is much more higher than in your case: for complex query you may triggerclone20-30 times very easy.
However, even for SQL query builder it probably makes sense to keep it immutable too. Sometimes you need to execute very similar queries, but with little difference (e.g. simple query + the same with COUNT(*)). AFAIU, Doctrine suggests to clone QB explicitly when you need it, but it looks like a trade-off.
I'd rather compare your query with something like Request from PSR-7 if you are in doubt. Request and Query are very similar concepts. Whenever your modify your request/query/command/event, you create different message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okey, Thanks =)
Thanks for the review.
src/Model/Query/Query.php
Outdated
| /** | ||
| * @author Tobias Nyholm <tobias.nyholm@gmail.com> | ||
| */ | ||
| interface Query |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this interface is necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct.
src/Provider/BingMaps.php
Outdated
| * @return \Geocoder\Collection | ||
| */ | ||
| private function executeQuery($query) | ||
| private function executeQuery(Query $query, $url) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd pass locale and limit variables or even copy-paste URL generation for both queries.
src/Model/Query/GeocodeQuery.php
Outdated
|
|
||
| public function __clone() | ||
| { | ||
| $this->bounds = clone $this->bounds; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bounds is an immutable structure: sharing same instance of Bounds is safe.
|
Thank you @unkind. I've update the PR. |
|
Today Ive done nothing to this PR but adding tests and making sure all providers implements the new interfaces. |
src/GeocoderTrait.php
Outdated
| { | ||
| return $this->reverseQuery(ReverseQuery::fromCoordinates($latitude, $longitude)); | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing newline
| <?php | ||
|
|
||
| /** | ||
| * This file is part of the Geocoder package. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this header mandatory? If so, it's missing from a few places.
src/Model/Query/GeocodeQuery.php
Outdated
| private $data; | ||
|
|
||
| /** | ||
| * @param Coordinates $coordinates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not seem to be correct
src/Model/Query/GeocodeQuery.php
Outdated
| } | ||
|
|
||
| /** | ||
| * @param $text |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type?
| { | ||
| return new self($coordinates); | ||
| } | ||
| /** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing newline
src/Model/Query/ReverseQuery.php
Outdated
| /** | ||
| * @param int $limit | ||
| * | ||
| * @return $this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be the class name
src/Provider/FreeGeoIp.php
Outdated
| */ | ||
| const ENDPOINT_URL = 'https://freegeoip.net/json/%s'; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indent
src/Provider/GoogleMaps.php
Outdated
| * | ||
| * @return GoogleMaps | ||
| */ | ||
| public function setRegion($region) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the set prefix preserved for BC reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it is a typo
| * @param string $locale A locale (optional). | ||
| */ | ||
| public function __construct(HttpClient $client, $apiKey, $locale = null) | ||
| public function __construct(HttpClient $client, $apiKey) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC this was $httpClient a few thousand lines above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not understand. We always refer to the HttpClient as $client
| /** | ||
| * @var Bounds | ||
| */ | ||
| private $bounds; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently this cannot be set at all
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a bunch of setter.
|
I like the idea of immutability in Providers. As for the queries: in case of the reverse query it seems to be a bit of overkill: basically coordinates are packed into an object, which gets unpacked in the provider as is. If it's a matter of consistency and/or probable future extension then it's probably fine. Also, it feels weird that the limit can be passed in the query as well. One could say limit is a Provider configuration. I don't have too much context, so I might be wrong about it. |
|
Thank you for the review. Really appreciated.
Yes, it is a matter of consistency and about being able to pass arbitrary data to the provider.
Hm, I've never considered that.. I compare it to Doctrine. The |
|
Good job! |
This is an implementation of a query object suggested in #574. Ping @unkind
Changes:
LocaleTrait)GeocoderextendsProviderLocaleAwareProviderLocaleAwareGeocoder(no methods)StatefulGeocoder(this will make migrating easy)GeocodeQuery,ReverseQueryProvider::geocodeQuery,Provider::reverseQueryOnly GoogleMaps and Bing are using the new Provider interface.
Why we need this
The Query objects keeps providers stateless. The query object is easily extendable with extra data like bounds, locale, limit and also arbitrary data. Allowing arbitrary data will solve #546.
Using an query object over setters on the providers keeps the public interface of the providers more clean.
This PR will also solve #501.
TODO: