├── tests ├── .gitkeep └── unit │ ├── TestCsvFile.php │ ├── TestFile.php │ ├── TestTemporaryModel.php │ ├── TestCsvLine.php │ ├── TestParseCsv.php │ ├── TestGetTableConfig.php │ ├── TestRuntimeModel.php │ ├── TestDbFileBuilder.php │ ├── TestGetTableState.php │ ├── TestGetCsvState.php │ ├── TestValidatorFormInputModel.php │ ├── TestCsvFileBuilder.php │ ├── TestGetTableName.php │ ├── TestGeneralState.php │ ├── TestCsvFileHandler.php │ └── TestStateHandler.php ├── .gitignore ├── src ├── Jacopo │ └── LaravelImportExport │ │ ├── start.php │ │ ├── Models │ │ ├── Exceptions │ │ │ ├── NoDataException.php │ │ │ ├── ClassNotFoundException.php │ │ │ ├── FileNotFoundException.php │ │ │ ├── HeadersNotSetException.php │ │ │ └── UnalignedArrayException.php │ │ ├── StateHandling │ │ │ ├── StateArray.php │ │ │ ├── StateInterface.php │ │ │ ├── Export │ │ │ │ ├── GeneralState.php │ │ │ │ ├── StateHandler.php │ │ │ │ ├── ExportCompleteState.php │ │ │ │ ├── GetTableConfig.php │ │ │ │ └── GetTableName.php │ │ │ ├── Import │ │ │ │ ├── GeneralState.php │ │ │ │ ├── ImportCompleteState.php │ │ │ │ ├── StateHandler.php │ │ │ │ ├── GetCsvState.php │ │ │ │ └── GetTableState.php │ │ │ ├── StateHandler.php │ │ │ └── GeneralState.php │ │ ├── ParseCsv │ │ │ ├── CsvFile.php │ │ │ ├── File.php │ │ │ ├── CsvLine.php │ │ │ ├── ParseCsv.php │ │ │ ├── DbFileBuilder.php │ │ │ ├── CsvFileBuilder.php │ │ │ └── CsvFileHandler.php │ │ ├── InputWrapper.php │ │ ├── ValidatorFormInputModel.php │ │ ├── TemporaryModel.php │ │ ├── DatabaseSchemaManager.php │ │ └── RuntimeModel.php │ │ ├── routes.php │ │ └── LaravelImportExportServiceProvider.php ├── config │ ├── baseconf.php │ └── database.php ├── controllers │ ├── HomeController.php │ ├── BaseController.php │ ├── ImportController.php │ └── ExportController.php ├── views │ ├── home │ │ └── index.blade.php │ └── layouts │ │ └── default.blade.php └── migrations │ └── 2013_09_25_222039__laravel_import_export_temporary_table.php ├── examples ├── import_step1.jpg └── import_step2.jpg ├── public ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── css │ ├── default.css │ ├── bootstrap-theme.min.css │ └── bootstrap-theme.css └── js │ └── bootstrap.min.js ├── .travis.yml ├── phpunit.xml ├── composer.json ├── LICENSE └── README.md /tests/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /src/Jacopo/LaravelImportExport/start.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | use \ArrayIterator; 9 | 10 | class StateArray extends ArrayIterator {} 11 | -------------------------------------------------------------------------------- /src/config/baseconf.php: -------------------------------------------------------------------------------- 1 | "_import_export_temporary_table", 5 | "default_title" => "Laravel-Import-Export", 6 | "session_import_key" => "import_state", 7 | "session_export_key" => "export_state", 8 | "base_application_route" => "importer", 9 | "connection_name" => "import", 10 | ); -------------------------------------------------------------------------------- /tests/unit/TestCsvFile.php: -------------------------------------------------------------------------------- 1 | csv_file = new CsvFile(); 12 | } 13 | 14 | //@todo write tests 15 | public function testToMakePass() 16 | {} 17 | } -------------------------------------------------------------------------------- /src/controllers/HomeController.php: -------------------------------------------------------------------------------- 1 | layout->nest('content','laravel-import-export::home/index'); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /src/views/home/index.blade.php: -------------------------------------------------------------------------------- 1 |
Welcome to laravel import-export package. This software is built to import and export data from various format file to an sql database.
4 |What are you waiting for?
5 |6 | Start importing » 7 |
8 |
11 |
12 | ## This package is no longer supported: either any bugfix or any new feature will be added. Install at your own risk!
13 |
14 | ##Features
15 |
16 | - Import and export data from Csv file into database
17 | - Multi DBMS: works with all DBMS supported by Laravel ORM
18 | - Create database schema: allow you to create database schema when importing data
19 |
20 | ##Under the box: features incoming in 0.2.0
21 |
22 | - Import and export an arbitrary number of lines
23 | - Import and export JSON and XML
24 | - Database access configurabile with a GUI
25 |
26 | ## Requirements
27 |
28 | - PHP >= 5.3.7
29 | - Composer
30 | - Laravel framework 4+
31 | - DBMS that support transactions and supported by Laravel ORM
32 |
33 | ##Installation with Composer
34 |
35 | To install Import-Export with Composer, add this line to your composer.json file in the `require field:
36 |
37 | ```json
38 | "jacopo/laravel-import-export": "dev-master"
39 | ```
40 | Also remember to set the minimum-stability to "dev" in your composer.json file
41 |
42 | Then open `app/config/app.php` and add the following line in the `providers` array:
43 |
44 | ```php
45 | 'providers' => array(
46 | 'Jacopo\LaravelImportExport\LaravelImportExportServiceProvider',
47 | )
48 | ```
49 |
50 | After you need to execute the following commands:
51 |
52 | ```php
53 | php artisan config:publish jacopo/laravel-import-export
54 | php artisan asset:publish jacopo/laravel-import-export
55 | ```
56 | Now you have under `app/config/packages/jacopo/laravel-import-export` the package configuration files. At this point you need to configure the database access. Open the file `app/config/packages/jacopo/laravel-import-export/database.php` and update it with the database access information. When done run the following command to initialize ImportExport database.
57 |
58 | ```php
59 | php artisan migrate --package="jacopo/laravel-import-export" --database="import"
60 | ```
61 | This command will create a `_import_export_temporary_table` in the db, you can change the name of the table editing the the key: `table_prefix` under the file `app/config/packages/jacopo/laravel-import-export/baseconf.php`.
62 |
63 | Congratulations! Now you can view the application at the url: `http://url-of-your-baseapp/importer`. If needed you can change the base route editing the the key: `base_application_route` under the file `app/config/packages/jacopo/laravel-import-export/baseconf.php`.
64 |
--------------------------------------------------------------------------------
/tests/unit/TestCsvFileBuilder.php:
--------------------------------------------------------------------------------
1 | makePartial();
25 | $mock->shouldReceive('parseCsvFile')
26 | ->times(4)
27 | ->andReturn($mocked_csv_data[0],$mocked_csv_data[1],$mocked_csv_data[2],$mocked_csv_data[3]);
28 | $this->mockConfig();
29 |
30 | $builder = new CsvFileBuilder(array(),array(),array("first_line_headers"=>false),null,$mock);
31 | $builder->buildFromCsv();
32 |
33 | $csv_file = $builder->getCsvFile();
34 |
35 | $this->assertInstanceOf('Jacopo\LaravelImportExport\Models\ParseCsv\CsvFile', $csv_file);
36 |
37 | // check headers
38 | $this->assertEmpty($csv_file->getCsvHeader());
39 |
40 | // check data
41 | foreach($csv_file as $key_csv_line => $csv_line)
42 | {
43 | $this->assertInstanceOf('Jacopo\LaravelImportExport\Models\ParseCsv\CsvLine', $csv_line);
44 | $this->assertSame($mocked_csv_data[$key_csv_line], $csv_line->getAttributes());
45 | }
46 | }
47 |
48 | public function testBuildCreateFileFromCsvSuccessWithHeaders()
49 | {
50 | $mocked_csv_data = array(
51 | array("name","surname"),
52 | array("Jacopo","Beschi"),
53 | false,
54 | );
55 | $mock = \Mockery::mock('Jacopo\LaravelImportExport\Models\ParseCsv\ParseCsv')->makePartial();
56 | $mock->shouldReceive('parseCsvFile')
57 | ->times(3)
58 | ->andReturn($mocked_csv_data[0],$mocked_csv_data[1],$mocked_csv_data[2]);
59 |
60 | $builder = new CsvFileBuilder(array(),array(),array("first_line_headers"=>true),null,$mock);
61 | $builder->buildFromCsv();
62 |
63 | $csv_file = $builder->getCsvFile();
64 |
65 | $this->assertInstanceOf('Jacopo\LaravelImportExport\Models\ParseCsv\CsvFile', $csv_file);
66 |
67 | // check headers
68 | $csv_file->rewind();
69 | $this->assertSame($mocked_csv_data[0],$csv_file->getCsvHeader());
70 |
71 | // check values
72 | $second = $csv_file->current();
73 | $this->assertInstanceOf('Jacopo\LaravelImportExport\Models\ParseCsv\CsvLine', $second);
74 | $this->assertSame($mocked_csv_data[1], $second->getAttributes());
75 | }
76 |
77 | protected function mockConfig()
78 | {
79 | $app = m::mock('AppMock');
80 | $app->shouldReceive('instance')->once()->andReturn($app);
81 |
82 | Illuminate\Support\Facades\Facade::setFacadeApplication($app);
83 | Illuminate\Support\Facades\Config::swap($config = m::mock('ConfigMock'));
84 |
85 | $config->shouldReceive('get')
86 | ->andReturn(true);
87 | }
88 |
89 | }
--------------------------------------------------------------------------------
/src/Jacopo/LaravelImportExport/Models/ParseCsv/CsvFileBuilder.php:
--------------------------------------------------------------------------------
1 |
6 | */
7 | class CsvFileBuilder
8 | {
9 |
10 | protected $config_file_parse;
11 | protected $config_model;
12 | protected $csv_file;
13 | protected $csv_parser;
14 | /**
15 | * 'first_line_headers' if first line of the file contains data headers
16 | * @var Array
17 | */
18 | protected $config;
19 |
20 | /**
21 | *
22 | * @param $config_model configuration for the RuntimeModel
23 | * @param $config_file_parse configuration for the csv file parser
24 | * @param $config configuration for the builder
25 | * @param $csv_file instance of CsvFile
26 | * @param $csv_parser istance of ParseCsv
27 | */
28 | public function __construct(array $config_model = array(), array $config_file_parse = array(), $config = array(), CsvFile $csv_file = null, ParseCsv $csv_parser = null)
29 | {
30 | $this->config_model = $config_model;
31 | $this->config_file_parse = $config_file_parse;
32 | $this->config = $config;
33 | $this->csv_file = $csv_file ? $csv_file : new CsvFile;
34 | $this->csv_parser = $csv_parser ? $csv_parser : new ParseCsv;
35 | }
36 |
37 | public function setConfigModel($config)
38 | {
39 | $this->config_model = $config;
40 | }
41 | public function setConfigFileParse($config)
42 | {
43 | $this->config_file_parse = $config;
44 | }
45 | public function setConfig($config)
46 | {
47 | $this->config = $config;
48 | }
49 | /**
50 | * This method build the CsvFile from a csv file
51 | *
52 | * @throws FileNotFoundException
53 | * @throws InvalidArgumentException
54 | * @throws UnalignedArrayException
55 | * @throws DomainException
56 | */
57 | public function buildFromCsv(CsvLine $csv_line_base = null)
58 | {
59 | $this->csv_line_base = $csv_line_base ? $csv_line_base : new CsvLine;
60 | $this->csv_parser->setConfig($this->config_file_parse);
61 | $this->csv_line_base->setConfig($this->config_model);
62 |
63 | $this->updateCsvHeader();
64 |
65 | // Csv loop
66 | while ( ($csv_line_array = $this->csv_parser->parseCsvFile()) != false )
67 | {
68 | if( count($csv_line_array) && (! ( (count($csv_line_array) == 1) && is_null($csv_line_array[0]) ) ) )
69 | {
70 | // create csv_line
71 | $csv_line = clone $this->csv_line_base;
72 | $csv_line->forceSetAttributes($csv_line_array);
73 | $this->appendLine($csv_line);
74 | }
75 | }
76 | }
77 |
78 | public function getCsvFile()
79 | {
80 | return $this->csv_file;
81 | }
82 |
83 | protected function appendLine(CsvLine $line)
84 | {
85 | $this->csv_file->append($line);
86 | }
87 |
88 | /**
89 | * Update the csv header with first row of the file
90 | * if "first_line_headers" is enabled
91 | */
92 | protected function updateCsvHeader()
93 | {
94 | if( $this->config['first_line_headers'] )
95 | {
96 | $csv_line_array = $this->csv_parser->parseCsvFile();
97 | $this->csv_file->setCsvHeader($csv_line_array);
98 | }
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/src/Jacopo/LaravelImportExport/Models/StateHandling/StateHandler.php:
--------------------------------------------------------------------------------
1 |
6 | */
7 |
8 | use Illuminate\Support\Facades\Session;
9 | use Illuminate\Support\Facades\DB;
10 | use Illuminate\Support\Facades\Config;
11 | use Illuminate\Support\MessageBag;
12 | use Jacopo\LaravelImportExport\Models\Exceptions\NoDataException;
13 | use Jacopo\LaravelImportExport\Models\Exceptions\ClassNotFoundException;
14 |
15 | class StateHandler
16 | {
17 | protected $session_key;
18 | protected $initial_state;
19 | protected $state_array;
20 |
21 | /**
22 | * Initialize starting import state from session
23 | */
24 | public function initializeState()
25 | {
26 | if (Session::has($this->session_key))
27 | {
28 | $this->setStateArray( Session::get($this->session_key) );
29 | }
30 | else
31 | {
32 | $this->resetState();
33 | }
34 | return $this->getStateArray();
35 | }
36 |
37 | /**
38 | * Reset the session_array state
39 | */
40 | public function resetState()
41 | {
42 | // clean states
43 | $this->setStateArray( new StateArray() );
44 | $state_array = $this->getStateArray();
45 | $state_array->append( new $this->initial_state );
46 | Session::put($this->session_key,$state_array);
47 | }
48 |
49 | /**
50 | * Fill layout content with forms
51 | */
52 | public function setContent($layout)
53 | {
54 | $content = '';
55 |
56 | $state_array = $this->getStateArray();
57 | foreach($state_array as $state_key => $state)
58 | {
59 | $content.=$state->getForm();
60 | }
61 | $layout->content = $content;
62 | }
63 |
64 | /**
65 | * Process the current form
66 | * @return $executed if success
67 | * @throws ClassNotFoundException
68 | */
69 | public function processForm()
70 | {
71 | $current_state = $this->getCurrent();
72 | $executed_process = $current_state->processForm();
73 | // set next state
74 | $executed_next_state = $this->setNextState($current_state,$executed_process);
75 | // return success
76 | return ( $executed_process && $executed_next_state );
77 | }
78 |
79 | /**
80 | * Set the next state
81 | *
82 | * @return Boolean $success
83 | */
84 | protected function setNextState($current_state, $executed)
85 | {
86 | try
87 | {
88 | $next_state = $current_state->getNextState();
89 | }
90 | catch(ClassNotFoundException $e)
91 | {
92 | Session::put('Errors', new MessageBag( array("Class" => "Class not found") ) );
93 | return false;
94 | }
95 |
96 | if($executed)
97 | {
98 | // append next state
99 | $this->append($next_state);
100 | }
101 | else
102 | {
103 | // replace current state
104 | $this->replaceCurrent($next_state);
105 | }
106 |
107 | return true;
108 | }
109 |
110 | protected function append($value)
111 | {
112 | $this->state_array->append($value);
113 | }
114 |
115 | public function getCurrent()
116 | {
117 | return $this->getLastState();
118 | }
119 |
120 | /**
121 | * Replace the current state
122 | */
123 | public function replaceCurrent($value)
124 | {
125 | $key = $this->getLastKey();
126 | if($key >= 0)
127 | {
128 | $this->getStateArray()->offsetSet($key,$value);
129 | }
130 | else
131 | {
132 | throw new NoDataException;
133 | }
134 | }
135 |
136 | public function getLastState()
137 | {
138 | $key = $this->getLastKey();
139 | if($key >= 0)
140 | {
141 | $state_array = $this->getStateArray();
142 | return $state_array[$key];
143 | }
144 | else
145 | {
146 | throw new NoDataException;
147 | }
148 | }
149 |
150 | public function getLastKey()
151 | {
152 | return ( count($this->state_array) - 1 );
153 | }
154 |
155 | public function getStateArray()
156 | {
157 | return $this->state_array;
158 | }
159 |
160 | public function setStateArray($value)
161 | {
162 | $this->state_array = $value;
163 | }
164 |
165 | }
166 |
--------------------------------------------------------------------------------
/tests/unit/TestGetTableName.php:
--------------------------------------------------------------------------------
1 | state = new GetTableName();
15 | }
16 |
17 | public function tearDown()
18 | {
19 | m::close();
20 | }
21 |
22 | public function testFillFormInput()
23 | {
24 | $input_mock = m::mock('Jacopo\LaravelImportExport\Models\InputWrapper');
25 | $input_mock->shouldReceive(
26 | 'get',
27 | 'get',
28 | 'get'
29 | )->andReturn(
30 | 'table_name',
31 | '50',
32 | ','
33 | );
34 | $this->state->fillFormInput($input_mock);
35 | $form_input = $this->state->getFormInput();
36 |
37 | $this->assertEquals('table_name', $form_input["table_name"]);
38 | $this->assertEquals('50', $form_input["max_rows"]);
39 | $this->assertEquals(',', $form_input["separator"]);
40 | }
41 |
42 | public function testGetTableValuesSelect()
43 | {
44 | $expected_tables = array(
45 | "first" => "first",
46 | "second" => "second",
47 | );
48 | $mock_dbm = m::mock('Jacopo\LaravelImportExport\Models\DatabaseSchemaManager');
49 | $mock_dbm->shouldReceive('getTableList')
50 | ->once()
51 | ->andReturn(array("first","second"));
52 | $mock_state = m::mock('Jacopo\LaravelImportExport\Models\StateHandling\Export\GetTableName')->makePartial();
53 |
54 | $table_list = $mock_state->getTableValuesSelect($mock_dbm);
55 | $this->assertEquals($expected_tables, $table_list);
56 | }
57 |
58 | public function testGetTableHeader()
59 | {
60 | $mock_cols = m::mock("StdClass")
61 | ->shouldReceive('toArray')
62 | ->andReturn(array("name"=>"name"))
63 | ->getMock();
64 | $array_mock_cols = array($mock_cols);
65 |
66 | $mock_dbm = m::mock('Jacopo\LaravelImportExport\Models\DatabaseSchemaManager');
67 | $mock_dbm->shouldReceive('getTableColumns')
68 | ->once()
69 | ->andReturn($array_mock_cols);
70 |
71 | $mock_state = m::mock('Jacopo\LaravelImportExport\Models\StateHandling\Export\GetTableName')->makePartial();
72 | $mock_state->shouldReceive('getFormInput')
73 | ->once()
74 | ->andReturn(array("table_name"=>1));
75 | $expected_columns = array("name");
76 |
77 | $columns = $mock_state->getTableHeader($mock_dbm);
78 | $this->assertEquals($expected_columns,$columns);
79 | }
80 |
81 | public function testProcessFormSuccess()
82 | {
83 | $mock_validator = m::mock('Jacopo\LaravelImportExport\Models\ValidatorFormInputModel');
84 | $mock_validator->shouldReceive('validateInput')
85 | ->once()
86 | ->andReturn(true);
87 | $this->mockSessionPut();
88 |
89 | $form_input = array(
90 | 'table_name' => 'table',
91 | 'max_rows' => '50',
92 | 'separator' => ','
93 | );
94 |
95 | $state_stub = new GetTableNameStub;
96 |
97 | $executed = $state_stub->processForm($form_input,$mock_validator);
98 | $this->assertSame(true,$executed);
99 | }
100 |
101 | public function testProcessFormFails()
102 | {
103 | $mock_validator = m::mock('Jacopo\LaravelImportExport\Models\ValidatorFormInputModel');
104 | $mock_validator->shouldReceive('validateInput')
105 | ->once()
106 | ->andReturn(false);
107 |
108 | $form_input = array(
109 | 'table_name' => 'table',
110 | 'max_rows' => '50',
111 |
112 | );
113 |
114 |
115 | $executed = $this->state->processForm($form_input,$mock_validator);
116 | $this->assertSame(false,$executed);
117 | }
118 |
119 | protected function mockSessionPut()
120 | {
121 | $app = m::mock('AppMock');
122 | $app->shouldReceive('instance')->once()->andReturn($app);
123 |
124 | Illuminate\Support\Facades\Facade::setFacadeApplication($app);
125 | Illuminate\Support\Facades\Session::swap($input = m::mock('SessionMock'));
126 |
127 | $input->shouldReceive('put')->once()->andReturn(true);
128 | }
129 |
130 | }
131 |
132 | class GetTableNameStub extends GetTableName
133 | {
134 | protected function getDataTable()
135 | {
136 | return true;
137 | }
138 |
139 | protected function getTableHeader($dbm = null)
140 | {
141 | return array("column1");
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/tests/unit/TestGeneralState.php:
--------------------------------------------------------------------------------
1 | mockSessionMessageBag();
17 | $error_all_mock = m::mock('StdClass');
18 | $error_all_mock->shouldReceive('all')
19 | ->once()
20 | ->andReturn(array('Error1'));
21 |
22 | $mock = m::mock('Jacopo\LaravelImportExport\Models\StateHandling\GeneralState')->makePartial();
23 | $mock->shouldReceive('getErrors')
24 | ->once()
25 | ->andReturn($error_all_mock);
26 |
27 | $header = $mock->getErrorHeader();
28 | $this->assertSame("