Skip to content

Commit

Permalink
init draft
Browse files Browse the repository at this point in the history
  • Loading branch information
windmaomao committed Jul 23, 2014
0 parents commit 2dcb494
Show file tree
Hide file tree
Showing 14 changed files with 498 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/vendor
composer.phar
composer.lock
.DS_Store
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
language: php

php:
- 5.3
- 5.4
- 5.5
- 5.6
- hhvm

before_script:
- composer self-update
- composer install --prefer-source --no-interaction --dev

script: phpunit
23 changes: 23 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "qplot/importer",
"description": "",
"authors": [
{
"name": "Fang Jin",
"email": "windmaomao@gmail.com"
}
],
"require": {
"php": ">=5.4.0",
"illuminate/support": "4.2.*"
},
"autoload": {
"classmap": [
"src/migrations"
],
"psr-0": {
"QPlot\\Importer\\": "src/"
}
},
"minimum-stability": "stable"
}
18 changes: 18 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="Package Test Suite">
<directory suffix=".php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>
Empty file added public/.gitkeep
Empty file.
267 changes: 267 additions & 0 deletions src/QPlot/Importer/Importer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
<?php namespace QPlot\Importer;

use Illuminate\Support\MessageBag;
use Whoops\Example\Exception;

/**
* Class Setting
* @package QPlot\EnvironmentColor
*/
class Importer {

/**
* Messages outputted during the import process
*
* @var
*/
private $messages;

/**
* Database connection;
*/
private $db;

/**
* Import source folder;
*/
private $path;

/**
* beforeSave Callback
*/
private $beforeSaveCallback;

/**
* Internally store pre processed data
*
* @var
*/
private $data;

/**
* The Laravel application instance.
*
* @var \Illuminate\Foundation\Application
*/
protected $app;

/**
* @param \Illuminate\Foundation\Application $app
*/
public function __construct($app=null){
if(!$app){
$app = app(); //Fallback when $app is not given
}
$this->app = $app;
$this->messages = new MessageBag();
}

/**
* Import
*/
public function import($callback = '') {
$config = $this->app['config'];

// check path exists
$this->path = base_path() . '/' . $config->get('importer::config.path');
if (!file_exists($this->path)) {
$this->messages->add('error', 'import path does not exist');
return;
}

// check database connection
$db = $this->app['config']->get('importer::config.database');
try {
$this->db = $this->app['db']->connection($db);
} catch (Exception $e) {
$this->messages->add('error', 'database connection error for ' . $db);
return;
}

// assign callback
$this->beforeSaveCallback = $callback;
if (!is_callable($this->beforeSaveCallback)) {
$this->messages->add('error', 'callback function is not callable.');
return;
}

// process all imports
$this->data = [];
$imports = $config->get('importer::config.imports');
foreach($imports as $name => &$import) {
$import['name'] = $name;
$this->process($import);
}
}

/**
*
*/
public function getMessages() {
return $this->messages;
}

/**
* Process each import file
*/
protected function process($import) {
$changeme = $this->app['hash']->make('changeme');

// Preset variables
$mapping = $import['mapping'];
$unique_field = $import['unique'];
$Table = str_plural($import['model']); // etc. users
$Model = ucfirst($import['model']); // etc. User
$Rules = $import['rules'];
$data = [];

// Load file
$filename = $this->path . '/' . $import['file'];

if (!file_exists($filename)) {
$this->messages->add('error', 'File missing: ' . $filename);
}
ini_set("auto_detect_line_endings", true);
$content = file($filename, FILE_IGNORE_NEW_LINES);

// Load csv and fetch header
$rows = array_map('str_getcsv', $content);
$headers = array_shift($rows); // header required

// Check unique col
if ($unique_field) {
if (!isset($mapping[$unique_field])) {
$this->messages->add('error', 'Unique Column Def missing: ' . $unique_field);
return;
}
$unique_col = $mapping[$unique_field];

// Check existing rows
$unique_mapping_index = array_search($unique_col, $headers);
if (!$unique_mapping_index) {
$this->messages->add('error', 'Unique Column missing: ' . $unique_field);
return;
}
$unique_cols = array_pluck($rows, $unique_mapping_index);
$query = call_user_func(
$Model . '::whereIn',
$unique_field, $unique_cols
);
$data['unique'] = $query->lists($unique_field);
} else {
$data['unique'] = [];
}

// Check mapping and prepare
foreach($mapping as $field => $map) {
if (!is_array($map)) {
if (!in_array($map, $headers)) {
$this->messages->add('error', 'Column missing: ' . $map);
return;
}
} else {
// for reference row
if ($map['type'] == 'reference') {
$ref_mapping_index = array_search($map['column'], $headers);
if (!$ref_mapping_index) {
$this->messages->add('error', 'Unique Column missing: ' . $map['column']);
return;
}
$ref_cols = array_pluck($rows, $ref_mapping_index);
$RefModel = ucfirst($map['model']);
$query = call_user_func(
$RefModel . '::whereIn',
$map['foreign_field'], $ref_cols
);
$result_fields = [$map['foreign_ref']];
$result_fields = array_merge($result_fields, [$map['foreign_field']]);
$result_fields = array_merge($result_fields, $map['foreign_data']);
$results = $query->get($result_fields);
// $data[$field] = $query->lists($map['foreign_ref'], $map['foreign_field']);
$ref_data = [];
foreach($results as $result) {
$key = $result[$map['foreign_field']];
$ref_data[$key] = $result->toArray();
}
$data[$field] = $ref_data;
}
}
}

// Import
$importing = [];
$records = [];
$i = 0;
foreach($rows as $item) {
$i++;

// assemble row
$row = array_combine($headers, $item);

// remove non-unique
if ($unique_field) {
$unique_value = $row[$unique_col];
// skip existing one
if (in_array($unique_value, $data['unique']) || in_array($unique_value, $importing)) {
continue;
}
} else {
$unique_value = $i;
}

// assemble record
$record = [];
foreach($mapping as $field => $map) {
if (!is_array($map)) {
$record[$field] = $row[$map];
} else {
switch($map['type']) {
case 'constant':
$record[$field] = $map['value'];
break;
case 'reference':
$ref_data = &$data[$field];
$value = $row[$map['column']];
if (isset($ref_data[$value]))
{
$record[$field] = $ref_data[$value][$map['foreign_ref']];
} else
{
$this->messages->add('error', 'Reference missing: ' . $unique_field);
}
break;
}
}
}

// call custom function
call_user_func_array($this->beforeSaveCallback, array($import, &$record, &$data));

// validate record
$validator = $this->app['validator'];
$validation = $validator->make($record, $Rules);
if ($validation->fails()) {
foreach($validation->messages()->all() as $msg) {
$this->messages->add('error', "[$Model] " . $unique_value . ': ' . $msg);
}
} else {
$importing[] = $unique_value;
$records[] = $record;
}
}
// Debugbar::info($records);

if ($records) {
try {
$this->db->table($Table)->insert($records);
} catch(Exception $e) {
$this->messages->add('error', $e->getMessage());
return;
}
}

$this->messages->add('info', count($importing) . ' ' . $import['name'] . ' has been imported');
return;
}

}
49 changes: 49 additions & 0 deletions src/QPlot/Importer/ImporterServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php namespace QPlot\Importer;

use Illuminate\Support\ServiceProvider;

class ImporterServiceProvider extends ServiceProvider {

/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;

/**
* Bootstrap the application events.
*
* @return void
*/
public function boot()
{
$this->package('qplot/importer');
$this->app['config']->package('qplot/importer', $this->guessPackagePath() . '/config');

}

/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app['importer'] = $this->app->share(function ($app)
{
return new Importer();
});
}

/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return array('importer');
}

}
Empty file added src/config/.gitkeep
Empty file.
Loading

0 comments on commit 2dcb494

Please sign in to comment.