diff --git a/README.md b/README.md index 5623885..19582fd 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,16 @@ $res = $divisionCode->get('110000'); // 北京市 // Get all the provinces $provinces = $divisionCode->getAllProvinces(); print_r($provinces); + +// Get all the cities in the specified province +$province = '110000'; +$cities = $divisionCode->getCitiesInProvince($province); +print_r($cities); + +// Get all the counties in the specified city +$city = '445200'; +$counties = $divisionCode->getCountiesInCity($city); +print_r($counties); ``` ## Upgrade diff --git a/src/DivisionCode/DivisionCode.php b/src/DivisionCode/DivisionCode.php index af2d05a..2089878 100644 --- a/src/DivisionCode/DivisionCode.php +++ b/src/DivisionCode/DivisionCode.php @@ -11,6 +11,11 @@ class DivisionCode */ public static $codes = []; + /** + * Municipalities in China includes Beijing, Tianjin, Shanghai, Chongqing + */ + const MUNICIPALITIES = ['110000', '120000', '310000', '500000']; + protected $disableSQLite = false; protected $db; @@ -231,7 +236,7 @@ public function getSlice($offset = 0, $limit = 1): array } /** - * Get all the provinces + * Get all the provinces including municipalities and autonomous regions * * @param bool $includeGAT include Hong Kong, Macao and Taiwan? exclude them by default * @return array @@ -260,4 +265,75 @@ public function getAllProvinces($includeGAT = false): array return substr($k, 2) == '0000'; }, ARRAY_FILTER_USE_KEY); } + + /** + * Get all the cities in the specified province + * Municipalities do not have cities so will return the counties(districts) instead + * + * @param string $province + * @return array + */ + public function getCitiesInProvince($province): array + { + // check if it is really a province, return empty array if it is not + if (substr($province, 2) != '0000') { + return []; + } + + $prefix = substr($province, 0, 2); + + if ($this->supportSQLite()) { + if (in_array($province, self::MUNICIPALITIES)) { + $sql = "SELECT code, name FROM division_codes WHERE code LIKE '$prefix%' AND code != '$province'"; + } else { + $sql = "SELECT code, name FROM division_codes WHERE code LIKE '$prefix%00' AND code != '$province'"; + } + $res = $this->db->query($sql); + $arr = []; + while ($row = $res->fetchArray()) { + $arr[$row['code']] = $row['name']; + } + + return $arr; + } + + return array_filter(self::$codes, function ($k) use ($province, $prefix) { + if (in_array($province, self::MUNICIPALITIES)) { + return substr($k, 0, 2) == $prefix && $k != $province; + } + + return substr($k, 0, 2) == $prefix && $k != $province && substr($k, 4) == '00'; + }, ARRAY_FILTER_USE_KEY); + } + + /** + * Get all the counties in the specified city + * + * @param string $city + * @return array + */ + public function getCountiesInCity($city): array + { + // check if it is really a city, return empty array if it is not + if (substr($city, 4) != '00') { + return []; + } + + $prefix = substr($city, 0, 4); + + if ($this->supportSQLite()) { + $sql = "SELECT code, name FROM division_codes WHERE code LIKE '$prefix%' AND code != '$city'"; + $res = $this->db->query($sql); + $arr = []; + while ($row = $res->fetchArray()) { + $arr[$row['code']] = $row['name']; + } + + return $arr; + } + + return array_filter(self::$codes, function ($k) use ($city, $prefix) { + return substr($k, 0, 4) == $prefix && $k != $city; + }, ARRAY_FILTER_USE_KEY); + } } diff --git a/src/DivisionCode/codes.db b/src/DivisionCode/codes.db index fa85d69..7d4003d 100644 Binary files a/src/DivisionCode/codes.db and b/src/DivisionCode/codes.db differ diff --git a/tests/DivisionCodeTest.php b/tests/DivisionCodeTest.php index 60c00d8..32f5ba2 100644 --- a/tests/DivisionCodeTest.php +++ b/tests/DivisionCodeTest.php @@ -118,7 +118,7 @@ public function getSliceProvider(): array } /** - * @dataProvider ggetAllProvincesProvider + * @dataProvider getAllProvincesProvider */ public function testGetAllProvinces($useSQLite, $includeGAT, $expected): void { @@ -127,7 +127,7 @@ public function testGetAllProvinces($useSQLite, $includeGAT, $expected): void $this->assertEquals($expected, count($provinces)); } - public function ggetAllProvincesProvider(): array + public function getAllProvincesProvider(): array { return [ [true, false, 31], @@ -136,4 +136,58 @@ public function ggetAllProvincesProvider(): array [false, true, 34], ]; } + + /** + * @dataProvider getCitiesInProvinceProvider + */ + public function testGetCitiesInProvince($useSQLite, $province, $expected): void + { + $this->divisionCode->useSQLite($useSQLite); + $cities = $this->divisionCode->getCitiesInProvince($province); + $this->assertEquals($expected, count($cities)); + } + + public function getCitiesInProvinceProvider(): array + { + return [ + [true, '110000', 16], + [true, '710000', 0], + [true, '630000', 8], + [true, '150000', 12], + [true, '440000', 21], + [true, '445200', 0], // not a province + [false, '110000', 16], + [false, '710000', 0], + [false, '630000', 8], + [false, '150000', 12], + [false, '440000', 21], + [false, '445200', 0], // not a province + ]; + } + + /** + * @dataProvider getCountiesInCityProvider + */ + public function testGetCountiesInCity($useSQLite, $city, $expected): void + { + $this->divisionCode->useSQLite($useSQLite); + $counties = $this->divisionCode->getCountiesInCity($city); + $this->assertEquals($expected, count($counties)); + } + + public function getCountiesInCityProvider(): array + { + return [ + [true, '110000', 0], + [true, '445200', 5], + [true, '150100', 9], + [true, '630100', 7], + [true, '110101', 0], // not a city + [false, '110000', 0], + [false, '445200', 5], + [false, '150100', 9], + [false, '630100', 7], + [false, '110101', 0], // not a city + ]; + } }