Skip to content
This repository has been archived by the owner on Mar 19, 2022. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Vizhanov committed Apr 13, 2016
0 parents commit 489b3f1
Show file tree
Hide file tree
Showing 32 changed files with 1,552 additions and 0 deletions.
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
### OSX template
.DS_Store

### Composer template
composer.phar
composer.lock

### Windows template
Thumbs.db
ehthumbs.db
Desktop.ini

### Linux template
*~
.directory

### JetBrains template
.idea
12 changes: 12 additions & 0 deletions Module.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
/**
* @author Alexander Vizhanov <lembadm@gmail.com>
* @copyright 2015 AstwellSoft <astwellsoft.com>
* @package lembadm\geodb
*/

namespace lembadm\geodb;

class Module extends \yii\base\Module
{
}
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# yii2-geodb
The yii2-geodb geographical database, covers all countries and more than four million cities
Based upon geodb database (http://download.geodb.org/export/dump).

Provide RESTful controllers for:
* Countries
* Regions
* Cities
* Cities names
* Timezones

Provide controller for Select2 input widget

## Requirements
* The minimum requirement by Yii is that your Web server supports PHP 5.5.
* `memory_limit` set to >= 128Mb

## Installation
The preferred way to install this extension is through [composer](http://getcomposer.org/download/).

Either run

```
php composer.phar require --prefer-source "lembadm/yii2-geodb" "dev-master"
```

or add

```
"lembadm/yii2-geodb": "dev-master"
```

to the require section of your `composer.json` file.

Add to `config/web.php` and `config/console.php`
```
'modules' => [
...
'geo' => 'lembadm\geodb\Module',
...
],
```

Then run the migration
```
./yii migrate/up --migrationPath='@vendor/lembadm/yii2-geodb/migrations'
```

## Usage
See demo folder

## Contributors
+ [Alexander Vizhanov](https://github.com/lembadm)

## License
This work is licensed under a Creative Commons Attribution 3.0 License,
see (http://creativecommons.org/licenses/by/3.0/)
The Data is provided "as is" without warranty or any representation of accuracy, timeliness or completeness.

## Comments / Suggestions
Please provide any helpful feedback or requests.

Sorry for my English. Thanks.
206 changes: 206 additions & 0 deletions components/ImportTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
<?php
/**
* @author Alexander Vizhanov <lembadm@gmail.com>
* @copyright 2016 AstwellSoft <astwellsoft.com>
* @package lembadm\geodb
*/

namespace lembadm\geodb\components;

use yii;
use yii\base\ErrorException;
use ZipArchive;

/**
* @mixin \yii\db\Migration
*/
trait ImportTrait
{
/**
* @var string Table name
*/
private $table;

/**
* @var string Columns list (coma separated)
*/
private $columns;

/**
* @var int Number of inserted rows per iteration
*/
private $batchInsertLimit = 100000;

/**
* @var string Import file name
*/
private $importFileName;

/**
* @var string Import file path
*/
private $importFilePath;

/**
* @var int Value of `Yii::$app->log->flushInterval` before import
*/
private $initialFlushInterval = 0;

/**
* @var array Value of `Yii::$app->log->targets` before import
*/
private $initialLogTargets = [];

/**
* @param string $table Table name
* @param array $columns Array of columns. Must match count and order of the columns in the import file
*
* @throws ErrorException
*/
protected function import($table, array $columns)
{
$this->table = $table;
$this->columns = implode(', ', $columns);

try {
$this->preImport();

foreach ($this->getFileChunk() as $insertData) {
$this->insertBatch($insertData);
}
} finally {
$this->postImport();
}
}

/**
* Preparing to import
* Change Yii logging settings, unpacking data to import
*
* @throws ErrorException
*/
private function preImport()
{
// Backup Yii logging settings
$this->initialFlushInterval = Yii::$app->log->flushInterval;
$this->initialLogTargets = Yii::$app->log->targets;

// Disable logging (memory leaks and reduces the HDD load)
Yii::$app->log->flushInterval = 1;
Yii::$app->log->targets = [];

// Unpack the archived data to a temporary directory
$zip = new ZipArchive;
$res = $zip->open($this->getImportSrcPath());

switch (true) {
case $res === ZipArchive::ER_INCONS:
throw new ErrorException('ZipArchive: Zip archive inconsistent.');
case $res === ZipArchive::ER_MEMORY:
throw new ErrorException('ZipArchive: Malloc failure.');
case $res === ZipArchive::ER_NOENT:
throw new ErrorException('ZipArchive: No such file.');
case $res === ZipArchive::ER_NOZIP:
throw new ErrorException('ZipArchive: Not a zip archive.');
case $res === ZipArchive::ER_OPEN:
throw new ErrorException("ZipArchive: Can't open file {$this->getImportSrcPath()}");
case $res === ZipArchive::ER_READ:
throw new ErrorException('ZipArchive: Read error.');
case $res === ZipArchive::ER_SEEK:
throw new ErrorException('ZipArchive: Seek error.');
}

$runtimePath = Yii::getAlias('@runtime');

$this->importFileName = $zip->statIndex(0)['name'];
$this->importFilePath = "{$runtimePath}/{$this->importFileName}";

if (!$zip->extractTo($runtimePath, [$this->importFileName])) {
throw new ErrorException("ZipArchive: Can't extract file to {$runtimePath}");
}

$zip->close();
}

/**
* Restore after import
* Remove temporary files, restore Yii logging settings
*/
private function postImport()
{
// Delete the temporary data
try {
unlink($this->importFilePath);
} catch (ErrorException $e) {}

// Restore Yii logging settings
Yii::$app->log->targets = $this->initialLogTargets;
Yii::$app->log->flushInterval = $this->initialFlushInterval;
}

/**
* Insert into Database
*
* @param string $data Part of the SQL INSERT VALUES syntax
*
* @throws yii\db\Exception
*/
private function insertBatch($data)
{
$data = rtrim(rtrim($data), ',');

$this->db->createCommand()
->setSql("INSERT INTO {$this->table} ({$this->columns}) VALUES {$data}")
->execute();
}

/**
* Getting the full path to the file by the class name
*
* @return string Path to the archive data file
*/
private function getImportSrcPath()
{
$calledClass = get_called_class();
$classParts = explode('_', $calledClass);
array_splice($classParts, 0, 3);

return dirname(__DIR__) . '/data/' . implode('_', $classParts) . '.zip';
}

/**
* Getting part of the data from the imported file
*
* @return \Generator
* @throws ErrorException
*/
private function getFileChunk()
{
$counter = $insertData = null;
$handle = fopen($this->importFilePath, 'r');

if (!$handle) {
throw new ErrorException("fopen: can't open file {$this->importFilePath}");
}

while ($buffer = fgets($handle)) {
$counter++;
$insertData .= $buffer;
unset($buffer);
if ($counter === $this->batchInsertLimit) {
yield $insertData;
$insertData = $counter = null;
}
}

if (!feof($handle)) {
throw new ErrorException("fgets: unexpected end of file {$this->importFilePath}");
}

if ($counter) {
yield $insertData;
}

fclose($handle);
}
}
21 changes: 21 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "lembadm/yii2-geodb",
"description": "The yii2-geodb geographical database, covers all countries and more than four million cities",
"keywords": ["yii2", "geodb", "cities", "countries", "geodb"],
"type": "yii2-extension",
"license": "CC-BY-3.0",
"authors": [
{
"name": "Alexander Vizhanov",
"email": "lembadm@gmail.com"
}
],
"require": {
"yiisoft/yii2": "^2.0"
},
"autoload": {
"psr-4": {
"lembadm\\geodb\\": ""
}
}
}
18 changes: 18 additions & 0 deletions controllers/RestCityController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
/**
* @author Alexander Vizhanov <lembadm@gmail.com>
* @copyright 2015 AstwellSoft <astwellsoft.com>
* @package lembadm\geodb
*/

namespace lembadm\geodb\controllers;

use yii\rest\ActiveController;

/**
* Class RestCityController
*/
class RestCityController extends ActiveController
{
public $modelClass = 'lembadm\geodb\models\City';
}
18 changes: 18 additions & 0 deletions controllers/RestCityNameController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
/**
* @author Alexander Vizhanov <lembadm@gmail.com>
* @copyright 2015 AstwellSoft <astwellsoft.com>
* @package lembadm\geodb
*/

namespace lembadm\geodb\controllers;

use yii\rest\ActiveController;

/**
* Class RestCityNameController
*/
class RestCityNameController extends ActiveController
{
public $modelClass = 'lembadm\geodb\models\CityName';
}
18 changes: 18 additions & 0 deletions controllers/RestCountryController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
/**
* @author Alexander Vizhanov <lembadm@gmail.com>
* @copyright 2015 AstwellSoft <astwellsoft.com>
* @package lembadm\geodb
*/

namespace lembadm\geodb\controllers;

use yii\rest\ActiveController;

/**
* Class RestCountryController
*/
class RestCountryController extends ActiveController
{
public $modelClass = 'lembadm\geodb\models\Country';
}
Loading

0 comments on commit 489b3f1

Please sign in to comment.