diff --git a/BACKLOG.md b/BACKLOG.md index cde6ef2..f029aeb 100644 --- a/BACKLOG.md +++ b/BACKLOG.md @@ -1,3 +1,2 @@ * Add functionality for find places what included in specified radius -* Add functionality for find border places -* Add measuring method for calculate civil twilight for specific coordinate +* Add filtering Places by type diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ade5c0..e83f694 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,17 @@ The change log describes what is "Added", "Removed", "Changed" or "Fixed" between each release. +## 2.12 + ++ Add functionality for find all child places of specific Admin level collection ++ Implement type property for Place entity + ## 2.1 + Add to coordinates altitude + Add measuring distance between two coordinates ++ Fix potential issue in findPlaceByCoordinates method ++ Implement functionality for find neighbor places ## 2.0 diff --git a/src/Common/CHANGELOG.md b/src/Common/CHANGELOG.md index 62d8d78..e83f694 100644 --- a/src/Common/CHANGELOG.md +++ b/src/Common/CHANGELOG.md @@ -2,6 +2,11 @@ The change log describes what is "Added", "Removed", "Changed" or "Fixed" between each release. +## 2.12 + ++ Add functionality for find all child places of specific Admin level collection ++ Implement type property for Place entity + ## 2.1 + Add to coordinates altitude diff --git a/src/Common/Database/AbstractDatabase.php b/src/Common/Database/AbstractDatabase.php index cefcb48..c8279ae 100644 --- a/src/Common/Database/AbstractDatabase.php +++ b/src/Common/Database/AbstractDatabase.php @@ -20,7 +20,7 @@ /** * @author Borys Yermokhin */ -abstract class AbstractDatabase +abstract class AbstractDatabase implements DataBaseInterface { /** * @var bool[] @@ -106,8 +106,6 @@ public function compileKey( * Levels compiler for forming identifier for Address entity in @see compileKey * * @return string[] - * - * @throws \Psr\Cache\InvalidArgumentException */ protected function compileLevelsForKey(Address $address): array { @@ -190,22 +188,35 @@ public function getAdminLevels(): array * @param int $maxResults * @param string $locale * @param int $filterAdminLevel + * @param string $filterType * * @return string[] */ - protected function makeSearch(string $phrase, int $page, int $maxResults, string $locale, int $filterAdminLevel = -1): array - { + protected function makeSearch( + string $phrase, + int $page, + int $maxResults, + string $locale, + int $filterAdminLevel = -1, + string $filterType = '' + ): array { $result = []; - foreach ($this->actualKeys[$locale] as $adminLevel => $actualKeys) { - if ($filterAdminLevel > -1 && $filterAdminLevel !== $adminLevel) { + foreach ($this->actualKeys[$locale] as $type => $adminLevels) { + if (strlen($type) > 0 && $type !== $filterType) { continue; } - foreach ($actualKeys as $actualKey => $objectHash) { - $grade = $this->evaluateHitPhrase($phrase, $actualKey); - if ($grade > 0) { - $result[$actualKey] = $grade; + foreach ($adminLevels as $adminLevel => $actualKeys) { + if ($filterAdminLevel > -1 && $filterAdminLevel !== $adminLevel) { + continue; + } + + foreach ($actualKeys as $actualKey => $objectHash) { + $grade = $this->evaluateHitPhrase($phrase, $actualKey); + if ($grade > 0) { + $result[$actualKey] = $grade; + } } } } @@ -256,13 +267,15 @@ protected function evaluateHitPhrase(string $phrase, string $original): int * @param string $locale * @param string $key * - * @return int + * @return array */ - protected function findAdminLevelForKey(string $locale, string $key): int + protected function findAdminLevelAndTypeForKey(string $locale, string $key): array { foreach ($this->existAdminLevels as $existAdminLevel => $value) { - if (isset($this->actualKeys[$locale][$existAdminLevel][$key])) { - return $existAdminLevel; + foreach (array_keys($this->actualKeys[$locale]) as $existType) { + if (isset($this->actualKeys[$locale][$existType][$existAdminLevel][$key])) { + return [$existAdminLevel, $existType]; + } } } diff --git a/src/Common/Database/DataBaseInterface.php b/src/Common/Database/DataBaseInterface.php index bbee639..02bbc7a 100644 --- a/src/Common/Database/DataBaseInterface.php +++ b/src/Common/Database/DataBaseInterface.php @@ -15,6 +15,7 @@ use ApacheBorys\Location\Model\Address; use ApacheBorys\Location\Model\DBConfig; use ApacheBorys\Location\Model\Place; +use ApacheBorys\Location\Model\PlaceCollection; /** * @author Borys Yermokhin @@ -61,9 +62,9 @@ public function delete(Place $place): bool; * @param int $offset * @param int $limit * - * @return Place[] + * @return PlaceCollection */ - public function getAllPlaces(int $offset = 0, int $limit = 50): array; + public function getAllPlaces(int $offset = 0, int $limit = 50): PlaceCollection; /** * All admin levels what contain database @@ -113,7 +114,19 @@ public function compileKey( bool $useAddress = true ): string; + /** + * Service method for update all existing admin levels + * + * @return bool + */ public function updateExistAdminLevels(): bool; + /** + * Service method for normalize string for key name what should stored in database + * + * @param string $rawString + * + * @return mixed + */ public function normalizeStringForKeyName(string $rawString); } diff --git a/src/Common/Database/PdoDatabase.php b/src/Common/Database/PdoDatabase.php index b1d0a75..9fb57cc 100644 --- a/src/Common/Database/PdoDatabase.php +++ b/src/Common/Database/PdoDatabase.php @@ -23,6 +23,7 @@ use ApacheBorys\Location\Database\PdoDatabase\HelperLocator; use ApacheBorys\Location\Model\DBConfig; use ApacheBorys\Location\Model\Place; +use ApacheBorys\Location\Model\PlaceCollection; use ApacheBorys\Location\Model\Polygon; use Psr\Log\InvalidArgumentException; @@ -44,11 +45,12 @@ class PdoDatabase extends AbstractDatabase implements DataBaseInterface /** * By that keys we will store hashes (references) to fetch real object * first key - locale + * second key - type of Place * second key - admin level * third key - compiled key from Address of current locale * value - hash of object * - * @var string[][][] + * @var string[][][][] */ protected $actualKeys = []; @@ -114,9 +116,9 @@ public function get(string $searchKey, int $page = 0, int $maxResults = 30, stri $result = []; foreach ($this->makeSearch($searchKey, $page, $maxResults, $locale, $filterAdminLevel) as $key) { - $adminLevel = $this->findAdminLevelForKey($locale, $key); + list($adminLevel, $placeType) = $this->findAdminLevelAndTypeForKey($locale, $key); - $result[] = $this->getPlace($this->actualKeys[$locale][$adminLevel][$key]); + $result[] = $this->getPlace($this->actualKeys[$locale][$placeType][$adminLevel][$key]); } return $result; @@ -126,9 +128,9 @@ public function delete(Place $place): bool { if ($this->deletePlace($place->getObjectHash())) { foreach ($this->actualKeys as $locale => $localeKeys) { - $search = array_search($place->getObjectHash(), $localeKeys[$place->getMaxAdminLevel()]); + $search = array_search($place->getObjectHash(), $localeKeys[$place->getType()][$place->getMaxAdminLevel()]); if (is_string($search)) { - unset($this->actualKeys[$locale][$place->getMaxAdminLevel()][$search]); + unset($this->actualKeys[$locale][$place->getType()][$place->getMaxAdminLevel()][$search]); } } unset($this->objectsHashes[$place->getObjectHash()]); @@ -139,7 +141,7 @@ public function delete(Place $place): bool return false; } - public function getAllPlaces(int $offset = 0, int $limit = 50): array + public function getAllPlaces(int $offset = 0, int $limit = 50): PlaceCollection { $result = []; @@ -153,7 +155,7 @@ public function getAllPlaces(int $offset = 0, int $limit = 50): array $result[] = $this->getPlace($rawPlace[Constants::OBJECT_HASH]); } - return $result; + return new PlaceCollection($result); } public function updateExistAdminLevels(): bool @@ -285,7 +287,7 @@ private function prepareSearchKeysForInsert(Place $place): array foreach ($place->getAvailableAddresses() as $locale => $address) { $stmtSearchKeyForAddress[$locale] = $this->prepareSearchKeyForInsert($place, $address, $locale); - $this->actualKeys[$locale][$place->getMaxAdminLevel()][$this->compileKey($address, true, false)] = $place->getObjectHash(); + $this->actualKeys[$locale][$place->getType()][$place->getMaxAdminLevel()][$this->compileKey($address, true, false)] = $place->getObjectHash(); } return $stmtSearchKeyForAddress; @@ -607,7 +609,7 @@ private function getActualKeys(): bool $rawActKeys = $stmt->fetchAll(); foreach ($rawActKeys as $rawActKey) { - $this->actualKeys[$rawActKey[Constants::LOCALE]][$rawActKey[Constants::LEVEL]][$rawActKey[Constants::SEARCH_TEXT]] = $rawActKey[Constants::OBJECT_HASH]; + $this->actualKeys[$rawActKey[Constants::LOCALE]][$rawActKey[Constants::TYPE]][$rawActKey[Constants::LEVEL]][$rawActKey[Constants::SEARCH_TEXT]] = $rawActKey[Constants::OBJECT_HASH]; } ++$page; diff --git a/src/Common/Database/PdoDatabase/Constants.php b/src/Common/Database/PdoDatabase/Constants.php index e155369..c4b4301 100644 --- a/src/Common/Database/PdoDatabase/Constants.php +++ b/src/Common/Database/PdoDatabase/Constants.php @@ -30,6 +30,8 @@ final class Constants const LOCALE = 'locale'; + const TYPE = 'type'; + const PROVIDED_BY = 'provided_by'; const BOUNDS_WEST = 'bounds_west'; @@ -77,6 +79,7 @@ final class Constants self::COMPRESSED_DATA => '', self::PROVIDED_BY => Place::class.'::providedBy', self::LOCALE => Place::class.'::locale', + self::TYPE => Place::class.'::type', self::BOUNDS_WEST => Bounds::class.'::west', self::BOUNDS_SOUTH => Bounds::class.'::south', self::BOUNDS_NORTH => Bounds::class.'::north', diff --git a/src/Common/Database/PdoDatabase/MysqlHelper.php b/src/Common/Database/PdoDatabase/MysqlHelper.php index 973a62d..44c0116 100644 --- a/src/Common/Database/PdoDatabase/MysqlHelper.php +++ b/src/Common/Database/PdoDatabase/MysqlHelper.php @@ -32,6 +32,7 @@ public function queryForCreateTables(): array "'.Constants::COMPRESSED_DATA.'" BLOB, "'.Constants::PROVIDED_BY.'" TEXT, "'.Constants::LOCALE.'" TEXT, + "'.Constants::TYPE.'" TEXT, "'.Constants::BOUNDS_SOUTH.'" REAL, "'.Constants::BOUNDS_WEST.'" REAL, "'.Constants::BOUNDS_NORTH.'" REAL, diff --git a/src/Common/Database/PdoDatabase/PostgresqlHelper.php b/src/Common/Database/PdoDatabase/PostgresqlHelper.php index ec740d5..2eab7fc 100644 --- a/src/Common/Database/PdoDatabase/PostgresqlHelper.php +++ b/src/Common/Database/PdoDatabase/PostgresqlHelper.php @@ -32,6 +32,7 @@ public function queryForCreateTables(): array "'.Constants::COMPRESSED_DATA.'" BYTEA, "'.Constants::PROVIDED_BY.'" TEXT, "'.Constants::LOCALE.'" TEXT, + "'.Constants::TYPE.'" TEXT, "'.Constants::BOUNDS_SOUTH.'" REAL, "'.Constants::BOUNDS_WEST.'" REAL, "'.Constants::BOUNDS_NORTH.'" REAL, diff --git a/src/Common/Database/PdoDatabase/SqliteHelper.php b/src/Common/Database/PdoDatabase/SqliteHelper.php index a8ece5f..fa1fc04 100644 --- a/src/Common/Database/PdoDatabase/SqliteHelper.php +++ b/src/Common/Database/PdoDatabase/SqliteHelper.php @@ -32,6 +32,7 @@ public function queryForCreateTables(): array "'.Constants::COMPRESSED_DATA.'" BLOB, "'.Constants::PROVIDED_BY.'" TEXT, "'.Constants::LOCALE.'" TEXT, + "'.Constants::TYPE.'" TEXT, "'.Constants::BOUNDS_SOUTH.'" REAL, "'.Constants::BOUNDS_WEST.'" REAL, "'.Constants::BOUNDS_NORTH.'" REAL, diff --git a/src/Common/Database/Psr6Database.php b/src/Common/Database/Psr6Database.php index aee02a9..43b5b13 100644 --- a/src/Common/Database/Psr6Database.php +++ b/src/Common/Database/Psr6Database.php @@ -14,6 +14,7 @@ use ApacheBorys\Location\Model\DBConfig; use ApacheBorys\Location\Model\Place; +use ApacheBorys\Location\Model\PlaceCollection; use Psr\Cache\CacheItemPoolInterface; use Psr\Log\InvalidArgumentException; @@ -25,11 +26,12 @@ class Psr6Database extends AbstractDatabase implements DataBaseInterface /** * By that keys we will store hashes (references) to fetch real object * first key - locale + * second key - type of Place * second key - admin level * third key - compiled key from Address of current locale * value - hash of object * - * @var string[][][] + * @var string[][][][] */ protected $actualKeys = []; @@ -123,9 +125,9 @@ public function get(string $searchKey, int $page = 0, int $maxResults = 30, stri $result = []; foreach ($this->makeSearch($searchKey, $page, $maxResults, $locale, $filterAdminLevel) as $key) { - $adminLevel = $this->findAdminLevelForKey($locale, $key); + list($adminLevel, $placeType) = $this->findAdminLevelAndTypeForKey($locale, $key); - $item = $this->databaseProvider->getItem($this->actualKeys[$locale][$adminLevel][$key]); + $item = $this->databaseProvider->getItem($this->actualKeys[$locale][$placeType][$adminLevel][$key]); if ($item->isHit()) { $this->dbConfig->isUseCompression() ? $rawData = json_decode(gzuncompress($item->get()), true) : @@ -143,10 +145,10 @@ public function get(string $searchKey, int $page = 0, int $maxResults = 30, stri * * @throws \Psr\Cache\InvalidArgumentException */ - public function getAllPlaces(int $offset = 0, int $limit = 50): array + public function getAllPlaces(int $offset = 0, int $limit = 50): PlaceCollection { if ($offset > count($this->objectsHashes)) { - return []; + return new PlaceCollection(); } if ($limit > $this->dbConfig->getMaxPlacesInOneResponse()) { @@ -177,7 +179,7 @@ public function getAllPlaces(int $offset = 0, int $limit = 50): array } } - return $result; + return new PlaceCollection($result); } /** @@ -198,8 +200,8 @@ public function delete(Place $place): bool foreach ($this->actualKeys as $locale => $keys) { $place->selectLocale($locale); $keyForDelete = $this->compileKey($place->getSelectedAddress()); - if (isset($keys[$place->getMaxAdminLevel()][$keyForDelete])) { - unset($this->actualKeys[$locale][$place->getMaxAdminLevel()][$keyForDelete]); + if (isset($keys[$place->getType()][$place->getMaxAdminLevel()][$keyForDelete])) { + unset($this->actualKeys[$locale][$place->getType()][$place->getMaxAdminLevel()][$keyForDelete]); $this->updateActualKeys(); } } @@ -356,7 +358,7 @@ private function savePlace(Place $place): bool $this->updateHashKeys(); foreach ($this->compileKeys($place) as $locale => $key) { - $this->actualKeys[$locale][$place->getMaxAdminLevel()][$key] = $place->getObjectHash(); + $this->actualKeys[$locale][$place->getType()][$place->getMaxAdminLevel()][$key] = $place->getObjectHash(); } $this->updateActualKeys(); diff --git a/src/Common/Location.php b/src/Common/Location.php index 563880a..0e29a53 100644 --- a/src/Common/Location.php +++ b/src/Common/Location.php @@ -40,11 +40,25 @@ public function __construct(DataBaseInterface $dataBase) $this->dataBase = $dataBase; } + /** + * Add new place to database + * + * @param Place $place + * + * @return bool + */ public function addPlace(Place $place): bool { return $this->dataBase->add($place); } + /** + * Delete existent place from database + * + * @param Place $place + * + * @return bool + */ public function deletePlace(Place $place): bool { return $this->dataBase->delete($place); @@ -54,13 +68,57 @@ public function deletePlace(Place $place): bool * @param int $offset * @param int $limit * - * @return Place[] + * @return PlaceCollection */ - public function getAllPlaces(int $offset = 0, int $limit = 50): array + public function getAllPlaces(int $offset = 0, int $limit = 50): PlaceCollection { return $this->dataBase->getAllPlaces($offset, $limit); } + /** + * Find all Places what has less admin level and same level what contains passed AdminLevelCollection + * + * @param AdminLevelCollection $adminLevelCollection + * @param string $locale + * + * @return PlaceCollection + */ + public function findChildPlaces(AdminLevelCollection $adminLevelCollection, string $locale = ''): PlaceCollection + { + $result = new PlaceCollection(); + $tempPlace = new Address($this->getName(), $adminLevelCollection); + + for ($searchAdminLevel = $adminLevelCollection->getMaxAdminLevel() + 1; + $searchAdminLevel <= max($this->dataBase->getAdminLevels()); + ++$searchAdminLevel + ) { + $page = 0; + while ($possiblePlaces = $this->dataBase->get( + $this->dataBase->compileKey($tempPlace, true, true, false), + $page, + $this->dataBase->getDbConfig()->getMaxPlacesInOneResponse(), + $locale, + $searchAdminLevel + )) { + foreach ($possiblePlaces as $possiblePlace) { + if ($possiblePlace->getSelectedAddress()->getAdminLevels()->isContainLevels($adminLevelCollection)) { + $result->add($possiblePlace); + } + } + ++$page; + } + } + + return $result; + } + + /** + * Find place according to text phrase what contains in GeocodeQuery + * + * @param GeocodeQuery $query + * + * @return PlaceCollection + */ public function geocodeQuery(GeocodeQuery $query): PlaceCollection { $places = $this->dataBase->get( @@ -73,6 +131,13 @@ public function geocodeQuery(GeocodeQuery $query): PlaceCollection return new PlaceCollection($places ? $places : []); } + /** + * Find place according to coordinates what contains ReverseQuery. Place will be returned with less admin level + * + * @param ReverseQuery $query + * + * @return PlaceCollection + */ public function reverseQuery(ReverseQuery $query): PlaceCollection { $result = $this->findPlaceByCoordinates($query->getCoordinates(), $query->getLocale() ? $query->getLocale() : ''); @@ -290,7 +355,7 @@ private function innerFindTouchedPlaces( * * @return PairedCoordinates[] */ - private function findTouchedCoord(Place $originalPlace, Place $possiblePlace, float $maxDistanceToBorder): array + public function findTouchedCoord(Place $originalPlace, Place $possiblePlace, float $maxDistanceToBorder): array { $result = []; diff --git a/src/Common/Model/AdminLevelCollection.php b/src/Common/Model/AdminLevelCollection.php index 247701e..e26ffd5 100644 --- a/src/Common/Model/AdminLevelCollection.php +++ b/src/Common/Model/AdminLevelCollection.php @@ -117,4 +117,59 @@ public static function fromArray(array $raw): Arrayable return new self($adminLevels); } + + public function getMaxAdminLevel(): int + { + $max = 0; + foreach ($this->adminLevels as $level) { + if (strlen($level->getName()) > 0 && $level->getLevel() > $max) { + $max = $level->getLevel(); + } + } + + return $max; + } + + /** + * Shows is otherCollection contains same levels than original collection + * + * @param AdminLevelCollection $otherCollection + * + * @return bool + */ + public function isContainLevels(self $otherCollection): bool + { + foreach ($otherCollection->all() as $otherLevel) { + if (!isset($this->adminLevels[$otherLevel->getLevel()]) || + trim($otherLevel->getName()) != trim($this->adminLevels[$otherLevel->getLevel()]->getName()) + ) { + return false; + } + } + + return true; + } + + public function add(AdminLevel $adminLevel): bool + { + if (isset($this->adminLevels[$adminLevel->getLevel()])) { + return false; + } + $this->validateBeforeAdd($adminLevel); + + $this->adminLevels[$adminLevel->getLevel()] = $adminLevel; + + return true; + } + + public function delete(int $level): bool + { + if (!isset($this->adminLevels[$level])) { + return false; + } + + unset($this->adminLevels[$level]); + + return true; + } } diff --git a/src/Common/Model/Place.php b/src/Common/Model/Place.php index c78673b..725eaca 100644 --- a/src/Common/Model/Place.php +++ b/src/Common/Model/Place.php @@ -64,9 +64,9 @@ class Place private $objectHash; /** - * @var string|null + * @var string */ - private $typePlace; + private $type = ''; /** * @param Address|Address[] $address @@ -101,7 +101,7 @@ public function __construct( $this->timezone = $timezone; $this->providedBy = $providedBy; $this->bounds = $bounds; - $this->typePlace = $typePlace; + $this->type = $typePlace; } /** @@ -162,17 +162,7 @@ public function getAvailableAddresses(): array */ public function getMaxAdminLevel(): int { - $address = $this->getSelectedAddress(); - - $max = 0; - /** @var AdminLevel $level */ - foreach ($address->getAdminLevels() as $level) { - if (strlen($level->getName()) > 0 && $level->getLevel() > $max) { - $max = $level->getLevel(); - } - } - - return $max; + return $this->getSelectedAddress()->getAdminLevels()->getMaxAdminLevel(); } /** @@ -308,7 +298,7 @@ public function toArray(): array $result['providedBy'] = $this->providedBy; $result['bounds'] = $this->bounds->toArray(); $result['timezone'] = $this->timezone; - $result['typePlace'] = $this->typePlace; + $result['typePlace'] = $this->type; return $result; } @@ -378,19 +368,19 @@ public function setBounds(Bounds $bounds) } /** - * @return string|null + * @return string */ - public function getTypePlace() + public function getType() { - return $this->typePlace; + return $this->type; } /** - * @param string $typePlace + * @param string $type */ - public function setTypePlace(string $typePlace) + public function setType(string $type) { - $this->typePlace = $typePlace; + $this->type = $type; } public function isEqual(self $place): bool diff --git a/src/Common/Model/PlaceCollection.php b/src/Common/Model/PlaceCollection.php index 3395afe..8207bc9 100644 --- a/src/Common/Model/PlaceCollection.php +++ b/src/Common/Model/PlaceCollection.php @@ -68,4 +68,16 @@ public function all(): array { return $this->places; } + + /** + * @param Place $place + * + * @return $this + */ + public function add(Place $place): self + { + $this->places[] = $place; + + return $this; + } } diff --git a/src/Common/Tests/Database/StorageLocationProviderIntegrationDbTest.php b/src/Common/Tests/Database/StorageLocationProviderIntegrationDbTest.php index be8bc39..daa1c7b 100644 --- a/src/Common/Tests/Database/StorageLocationProviderIntegrationDbTest.php +++ b/src/Common/Tests/Database/StorageLocationProviderIntegrationDbTest.php @@ -84,7 +84,7 @@ public function testGetAllPlaces() )); $this->dataBase->add($origPlace); - $result = $this->dataBase->getAllPlaces(); + $result = $this->dataBase->getAllPlaces()->all(); $this->assertTrue(is_array($result)); $this->assertGreaterThan(0, count($result)); @@ -118,7 +118,7 @@ public function cleanUp() $page = 0; $limit = 30; - while ($result = $this->dataBase->getAllPlaces($page * $limit, $limit)) { + while ($result = $this->dataBase->getAllPlaces($page * $limit, $limit)->all()) { foreach ($result as $place) { $this->dataBase->delete($place); } diff --git a/src/Common/Tests/LocationTest.php b/src/Common/Tests/LocationTest.php index 1371cfe..8425838 100644 --- a/src/Common/Tests/LocationTest.php +++ b/src/Common/Tests/LocationTest.php @@ -94,7 +94,7 @@ public function testGetAllPlaces() { $totalCount = 0; $page = 0; - while ($places = $this->location->getAllPlaces($page * 50)) { + while ($places = $this->location->getAllPlaces($page * 50)->all()) { foreach ($places as $place) { $this->assertEquals(Place::class, get_class($place)); ++$totalCount; @@ -109,13 +109,13 @@ public function testGetAllPlaces() */ public function testDeletePlace() { - $places = \SplFixedArray::fromArray($this->location->getAllPlaces()); + $places = \SplFixedArray::fromArray($this->location->getAllPlaces()->all()); $places->rewind(); $this->location->deletePlace($places->current()); $totalCount = 0; $page = 0; - while ($places = $this->location->getAllPlaces($page * 50)) { + while ($places = $this->location->getAllPlaces($page * 50)->all()) { $totalCount += count($places); ++$page; } @@ -190,6 +190,29 @@ public function testNeighborFind(array $originalPlace, array $expected) } } + /** + * @dataProvider providerFindChildPlaces + * + * @param array $levelsForCollection + * @param int $expected + */ + public function testFindChildPlaces(array $levelsForCollection, int $expected) + { + $collection = new AdminLevelCollection(); + foreach ($levelsForCollection as $rawAdminLevel) { + $collection->add(new AdminLevel($rawAdminLevel['level'], $rawAdminLevel['name'])); + } + + $result = $this->location->findChildPlaces($collection); + $this->assertCount($expected, $result); + + /** @var Place $place */ + foreach ($result as $place) { + $this->assertInstanceOf(Place::class, $place); + $this->assertTrue($place->getSelectedAddress()->getAdminLevels()->isContainLevels($collection)); + } + } + /** * @see testNestedPolygons * @case 1 Should return first, main layer @@ -291,6 +314,84 @@ public function providerNeighborFind(): \Iterator ]; } + /** + * @see testFindChildPlaces + * + * @case 1 Empty result + * @case 2 Not full result for existent Places for Kyiv city + * @case 3 All Places for Kyiv city what located in database + * + * @return \Iterator + */ + public function providerFindChildPlaces(): \Iterator + { + yield [ + 'levelsForCollection' => [ + [ + 'level' => 0, + 'name' => 'Ukraine', + ], + [ + 'level' => 1, + 'name' => 'Kyiv region', + ], + [ + 'level' => 2, + 'name' => 'Kyiv', + ], + [ + 'level' => 3, + 'name' => 'Shevchenkivskyi district', + ], + [ + 'level' => 4, + 'name' => 'Volodymyrska Street', + ], + ], + 'expected' => 0, + ]; + + yield [ + 'levelsForCollection' => [ + [ + 'level' => 0, + 'name' => 'Ukraine', + ], + [ + 'level' => 1, + 'name' => 'Kyiv region', + ], + [ + 'level' => 2, + 'name' => 'Kyiv', + ], + [ + 'level' => 3, + 'name' => 'Shevchenkivskyi district', + ], + ], + 'expected' => 4, + ]; + + yield [ + 'levelsForCollection' => [ + [ + 'level' => 0, + 'name' => 'Ukraine', + ], + [ + 'level' => 1, + 'name' => 'Kyiv region', + ], + [ + 'level' => 2, + 'name' => 'Kyiv', + ], + ], + 'expected' => 5, + ]; + } + private function checkDusseldorfAssetsInGermanLang(Place $place) { $this->assertEquals(51.1243747, $place->getBounds()->getEast(), 'Latitude should be in Dusseldorf', 0.1); diff --git a/src/Common/Tests/json-coordinates/belsenplatz-oberkassel-dusseldorf.json b/src/Common/Tests/json-coordinates/de-dusseldorf-belsenplatz-oberkassel.json similarity index 100% rename from src/Common/Tests/json-coordinates/belsenplatz-oberkassel-dusseldorf.json rename to src/Common/Tests/json-coordinates/de-dusseldorf-belsenplatz-oberkassel.json diff --git a/src/Common/Tests/json-coordinates/oberkassel-dusseldorf.json b/src/Common/Tests/json-coordinates/de-dusseldorf-oberkassel.json similarity index 100% rename from src/Common/Tests/json-coordinates/oberkassel-dusseldorf.json rename to src/Common/Tests/json-coordinates/de-dusseldorf-oberkassel.json diff --git a/src/Common/Tests/json-coordinates/dusseldorf.json b/src/Common/Tests/json-coordinates/de-dusseldorf.json similarity index 100% rename from src/Common/Tests/json-coordinates/dusseldorf.json rename to src/Common/Tests/json-coordinates/de-dusseldorf.json diff --git a/src/Common/Tests/json-coordinates/bohdana-khmel-kyiv.json b/src/Common/Tests/json-coordinates/ua-kyiv-bohdana-khmel.json similarity index 100% rename from src/Common/Tests/json-coordinates/bohdana-khmel-kyiv.json rename to src/Common/Tests/json-coordinates/ua-kyiv-bohdana-khmel.json diff --git a/src/Common/Tests/json-coordinates/lva-tolstogo-kyiv.json b/src/Common/Tests/json-coordinates/ua-kyiv-lva-tolstogo.json similarity index 100% rename from src/Common/Tests/json-coordinates/lva-tolstogo-kyiv.json rename to src/Common/Tests/json-coordinates/ua-kyiv-lva-tolstogo.json diff --git a/src/Common/Tests/json-coordinates/saksagangskogo-kyiv.json b/src/Common/Tests/json-coordinates/ua-kyiv-saksagangskogo.json similarity index 100% rename from src/Common/Tests/json-coordinates/saksagangskogo-kyiv.json rename to src/Common/Tests/json-coordinates/ua-kyiv-saksagangskogo.json diff --git a/src/Common/Tests/json-coordinates/tarasa-shevchenko-kyiv.json b/src/Common/Tests/json-coordinates/ua-kyiv-tarasa-shevchenko.json similarity index 100% rename from src/Common/Tests/json-coordinates/tarasa-shevchenko-kyiv.json rename to src/Common/Tests/json-coordinates/ua-kyiv-tarasa-shevchenko.json diff --git a/src/Common/Tests/json-coordinates/volodymyrska-kyiv.json b/src/Common/Tests/json-coordinates/ua-kyiv-volodymyrska.json similarity index 100% rename from src/Common/Tests/json-coordinates/volodymyrska-kyiv.json rename to src/Common/Tests/json-coordinates/ua-kyiv-volodymyrska.json diff --git a/src/Common/Tests/json-coordinates/premier-min-office-london.json b/src/Common/Tests/json-coordinates/uk-london-premier-min-office.json similarity index 100% rename from src/Common/Tests/json-coordinates/premier-min-office-london.json rename to src/Common/Tests/json-coordinates/uk-london-premier-min-office.json diff --git a/src/Common/Tests/json-coordinates/white-house-washington.json b/src/Common/Tests/json-coordinates/us-washington-white-house.json similarity index 100% rename from src/Common/Tests/json-coordinates/white-house-washington.json rename to src/Common/Tests/json-coordinates/us-washington-white-house.json