📜 ⬆️ ⬇️

Geographer Package - First Working Version

First of all, I would like to thank for more than 80 stars on GitHub, which Habr's readers gave me on the results of the previous post . And this despite the fact that the repository was almost empty, and the link was not obvious. On the face of the utility of this package!


For those who missed the first post, a small repetition. If you have something in the application:



Or something like that (VC could not translate South Melbourne at all):



That meet (knock drums) - Geographer library is available in PHP-version . In this article, I will show the advantages of switching to a new package using my own website as an example. Actually, this is how the idea to create a library came - I noticed that I often begin to repeat the same functionality in different applications, and to repeat today in the world of developers - well, just somehow unfashionable.


Installation


You can install the package as a single command, as it is published in Packagist:


composer require menarasolutions/geographer 

There are no dependencies - this is one of the main principles of development at the moment. I do not want to oblige users of the package to install additional software or other packages. Nevertheless, it is planned to add optional integrations - Memcached, MongoDB.


Example 1: A simple list of countries


The most commonplace example, is found on a huge number of sites. The developer needs to show a drop-down list of countries in the world, probably with support for different languages.


As it was in my application:


  public static function getCountryNameByCode($countryCode, $language) { return Config::get('texts.countries')[$language][$countryCode]; } 

Everything is pretty trite here - the Config class facade gives access to the arrays specified in text files, and then we get the necessary translation using the keys of the language and country code. Simply nowhere, just as did, for sure, many.


Disadvantages of this approach:
- It was necessary to keep these translations within their application, and they do not have a direct relationship with business logic; - At the beginning, all translations must be added manually. I can't just take and start working with a new language;
- It is possible to read the code, but it is not very intuitive.


When switching to a geography library, it became:


  public static function getCountryNameByCode($countryCode, $language) { return Geographer::findOneByCode($countryCode) ->setLanguage($language) ->getName(); } 

Benefits gained:
- Now translations are out of the application and from time to time they are updated and improved themselves;
- Many popular languages ​​are available immediately out of the box;
- The code has become more intuitive, easy to read;
- It is possible to throw a suitable exception at a specific stage - the country was not found, the language was not found.


Example 2: the name of the item in the correct form


But this is more difficult, and here the advantages of switching to a separate library are much more noticeable. I have pages with similar links on my site:



Or such SEO-optimized comments:



The simplest, banal solution is to add a few more arrays or reference books to our application for each form of the word. Thus, we already have hundreds or thousands of translations, and many of them will have to be added or edited by hand - most directories like Geonames do not provide declensions.


Maybe it will turn out something like:


  public static function getCountryNameByCode($countryCode, $language, $form = 'default') { return Config::get('texts.countries')[$language][$countryCode][$form]; } 

But sometimes there will not be the necessary form and we will want to add some conditions - say, if there is no correct form "from", then we deduce the preposition "from" and the standard form, probably changing its ending. And the method will slowly turn into a monster with a bunch of conditions, or we will need to add new classes - and our application should focus on something completely different.


But this is not all - most of us use templates and text files on websites, and the question arises where to store the preposition - in the directory of countries (or cities) or in the template string. That is, to have a template like "Events in: city" or "Events: city". In the first case, there will be nuances with names that require excellent prepositions, such as "in France." In the second, there will be a huge number of repetitions in the dictionaries, or additional logic in the code.


In case of using my library:


  public static function getCountryNameByCode($countryCode, $language, $form = 'default') { return Geographer::findOneByCode($countryCode) ->inflict($form) ->setLanguage($language) ->getName(); } 

Prepositions can be turned on and off using the includePrepositions() and excludePrepositions() methods, which allows the library to be used in any template. Think about what the correct preposition is not necessary. It is not necessary to take care of how the current language inclines the names of countries and whether the prepositions change from this.


API Overview


Methods on collections


Arrays of subdivisions (countries, regions, or cities) are implemented through today's popular collections — smart arrays that support the Fluent API:


 $states->sortBy('name'); //     $states->setLanguage('ru')->sortBy('name'); //    $states->find(['code' => 472039]); //      $states->findOne(['code' => 472039]); //     $states->findOneByCode(472039); //     

General methods


All classes of divisions are descendants of one class and have common methods:


 $object->toArray(); //      $object->parent(); //   (  ,   ) $object->getCode(); //  ID $object->getShortName(); //     $object->getLongName(); // ,   

All data on the unit can be obtained in different ways:


 $object->getName(); //   (   ) $object->name; //   $object['name']; //     $object->toArray()['name']; //      

Class planet


 $earth->getAfrica(); //   $earth->getEurope(); //   $earth->getNorthAmerica(); //      $earth->getSouthAmerica(); $earth->getAsia(); $earth->getOceania(); $earth->getCountries(); //    $earth->withoutMicro(); //      100,000 

The relationship between the library and the application


If we put all the data on geographic units in a separate library, we can safely clean our arrays (or database, or something else), but we still need to somehow fix the connection between a specific city (or country or region). ) records in our database with a record in the library.


The long-term policy of the library is to provide the developer with as many unique identifiers as possible so that the developer can choose for himself what to catch (and it is not likely that new fields should be added to the database).


Currently, countries have ISO 3611-2, ISO 3611-3 and Geonames codes. Areas have codes ISO 3166, FIPS and Geonames. Cities have only Geonames codes - this is the most inflexible place.


Thus, in order to display on the site, say, the user's city, we can store the geonames_id in the users table, and according to it, restore the object:


 $city = City::build($geonames_id); 

Most modern frameworks can even do this conversion automatically. I specifically chose various international identification systems - the developer and his applications should not be tied to the Geographer library. It should be just as easy to give up as it is to start using it.


Coverage for today


The base has all the cities of the world with a population of over 50 thousand people, all regions and countries.


Each country has data:



Cities and regions have names and unique identifiers.


The names are translated into languages: Russian, English, Spanish, Italian, French, Chinese (Mandarin).
For countries, this is 100% coverage, for regions and cities - less, but constantly supplemented. For non-translatable cities, it is proposed to add the possibility of simple transliteration.


All countries are properly inclined - checked through online spelling dictionaries.


Future plans


  1. It is planned to add a primitive geo-index so that by coordinates you can get the nearest settlement.


  2. Different languages ​​are likely to be posted in separate repositories so that the developer does not need to download unnecessary JSON directories. Moreover, JSON directories will become independent of client libraries — future Python and Ruby clients can be tied to them.

The mission is simple - to become a standard geo-library of web developers. If you achieve sufficient popularity, you can expect users from different countries to make amendments to translations via pull-requests - reference books will constantly improve themselves, like wiki.


I would be very happy to hear comments and suggestions to the API!


')

Source: https://habr.com/ru/post/304098/


All Articles