Skip to content

Commit 3641ac5

Browse files
committed
Support for ordered sync/importing
- Order specified in config - Tests for ordered sync - New ErrorUpdatingModelException specifies which Model the error was thrown on
1 parent 692e70e commit 3641ac5

File tree

7 files changed

+116
-14
lines changed

7 files changed

+116
-14
lines changed

config/data-sync.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@
22

33
return [
44
'path' => base_path('sync'),
5+
'order' => [
6+
//
7+
]
58
];

readme.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Laravel utility to keep records synced between enviroments through source contro
66
- Create a JSON file for each model, using the model name as the filename. Example: Product.json would update the Product model
77
- Use nested arrays in place of hardcoded IDs for relationships
88
- Run `php artisan vendor:publish --provider="distinctm\LaravelDataSync\DataSyncBaseServiceProvider" --tag="data-sync-config"` to publish config file. Specify directory for sync data files (default is a new sync directory in the project root)
9-
- Run `php artisan data:sync`
9+
- Run `php artisan data:sync` (or `php artisan data:sync --model={model}` with the model flag to specify a model)
1010

1111
### Optional
1212
If using Laravel Forge, you can have the data sync run automatically on deploy. Edit your deploy script in Site -> App to include:
@@ -21,8 +21,18 @@ fi
2121
## Notes
2222
- use studly case for model name relationships as JSON keys (example: 'option_group' => 'OptionGroup'). This is important for case sensitive file systems.
2323
- empty values are skipped
24-
- the criteria/attributes for updateOrCreate are identified with a preleading underscore
24+
- the criteria/attributes for updateOrCreate are identified with a leading underscore
2525
- nested values represent relationships and are returned using where($key, $value)->first()->id
26+
- order of import can be set in _config/data-sync.php_ with an array:
27+
```
28+
return [
29+
'path' => base_path('sync'),
30+
'order' => [
31+
'Role',
32+
'Supervisor',
33+
]
34+
];
35+
```
2636

2737
## Examples
2838
### User.json:
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace distinctm\LaravelDataSync\Exceptions;
4+
5+
use Exception;
6+
use Throwable;
7+
8+
class ErrorUpdatingModelException extends Exception
9+
{
10+
public function __construct(string $message = "", int $code = 0, Throwable $previous = null)
11+
{
12+
parent::__construct($message, $code, $previous);
13+
14+
$this->message = "Error updating the {$message} model.";
15+
}
16+
}

src/Updater.php

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use distinctm\LaravelDataSync\Exceptions\NoRecordsInvalidJSONException;
88
use Illuminate\Support\Collection;
99
use Illuminate\Support\Facades\File;
10+
use distinctm\LaravelDataSync\Exceptions\ErrorUpdatingModelException;
1011

1112
class Updater
1213
{
@@ -31,11 +32,17 @@ public function __construct($path = null, $model = null)
3132
*/
3233
public function run()
3334
{
34-
$records = collect($this->files)->map(function ($file) {
35-
return $this->syncModel($file);
36-
});
35+
$files = $this->sortModels($this->files);
3736

38-
return $records;
37+
return $files->map(function ($file) {
38+
try {
39+
return $this->syncModel($file);
40+
} catch (\ErrorException $e) {
41+
$model = pathinfo($file, PATHINFO_FILENAME);
42+
43+
throw new ErrorUpdatingModelException(ucwords($model));
44+
}
45+
});
3946
}
4047

4148
/**
@@ -71,7 +78,7 @@ protected function syncModel(string $file)
7178
*
7279
* @param $path
7380
*
74-
* @return array
81+
* @return string
7582
* @throws \distinctm\LaravelDataSync\Exceptions\FileDirectoryNotFoundException
7683
*/
7784
protected function getDirectory($path)
@@ -91,25 +98,49 @@ protected function getDirectory($path)
9198
* @param string $directory
9299
* @param string|null $model
93100
*
94-
* @return array|string
101+
* @return \Illuminate\Support\Collection
95102
*/
96-
protected function getFiles(string $directory, $model)
103+
protected function getFiles(string $directory, $model = null)
97104
{
98105
if ($model) {
99-
return $directory . '/' . $model . '.json';
106+
return Collection::wrap($directory . '/' . $model . '.json');
100107
}
101108

102109
return collect(File::files($directory))->map(function ($path) {
103110
return $path->getPathname();
104-
})->toArray();
111+
});
112+
}
113+
114+
/**
115+
* Sort Models by pre-configured order
116+
*
117+
* @param \Illuminate\Support\Collection $files
118+
* @return \Illuminate\Support\Collection
119+
*/
120+
protected function sortModels(\Illuminate\Support\Collection $files)
121+
{
122+
if(empty(config('data-sync.order'))) {
123+
return $files;
124+
}
125+
126+
return $files->sortBy(function($file) use ($files) {
127+
$filename = pathinfo($file, PATHINFO_FILENAME);
128+
129+
$order = array_search(
130+
studly_case($filename),
131+
config('data-sync.order')
132+
);
133+
134+
return $order !== false ? $order : (count($files) + 1);
135+
});
105136
}
106137

107138
/**
108139
* Filter record criteria
109140
*
110141
* @param object $record
111142
*
112-
* @return array
143+
* @return \Illuminate\Support\Collection
113144
* @throws \distinctm\LaravelDataSync\Exceptions\NoCriteriaException
114145
*/
115146
protected function getCriteria(object $record)
@@ -132,7 +163,7 @@ protected function getCriteria(object $record)
132163
*
133164
* @param object $record
134165
*
135-
* @return array
166+
* @return \Illuminate\Support\Collection
136167
*/
137168
protected function getValues(object $record)
138169
{

tests/Unit/UpdaterTest.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use distinctm\LaravelDataSync\Tests\Fakes\UpdaterFake;
66
use Exception;
7+
use distinctm\LaravelDataSync\Exceptions\ErrorUpdatingModelException;
78

89
class UpdaterTest extends TestCase
910
{
@@ -86,7 +87,6 @@ public function invalid_json_throws_an_exception()
8687
} catch (Exception $e) {
8788
$this->assertContains('No records or invalid JSON for', $e->getMessage());
8889
}
89-
9090
}
9191

9292
/** @test */
@@ -101,6 +101,34 @@ public function the_json_must_contain_a_key_with_an_underscore()
101101
} catch (Exception $e) {
102102
$this->assertEquals('No criteria/attributes detected', $e->getMessage());
103103
}
104+
}
105+
106+
/** @test */
107+
public function order_of_imports_can_be_defined_in_config()
108+
{
109+
config()->set('data-sync.order', [
110+
'Supervisor',
111+
'Roles'
112+
]);
113+
114+
$updater = new UpdaterFake(__DIR__ . '/../test-data/ordered');
115+
$updater->run();
104116

117+
$this->assertDatabaseHas('roles', ['slug' => 'update-student-records']);
118+
$this->assertDatabaseHas('supervisors', ['name' => 'CEO']);
119+
}
120+
121+
/** @test */
122+
public function exception_is_thrown_if_imports_are_in_incorrect_order()
123+
{
124+
config()->set('data-sync.order', [
125+
'Roles',
126+
'Supervisor'
127+
]);
128+
129+
$this->expectException(ErrorUpdatingModelException::class);
130+
131+
$updater = new UpdaterFake(__DIR__ . '/../test-data/ordered');
132+
$updater->run();
105133
}
106134
}

tests/test-data/ordered/roles.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[
2+
{
3+
"_slug": "update-student-records",
4+
"category": "testing",
5+
"supervisor": {
6+
"name": "CEO"
7+
}
8+
}
9+
]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[
2+
{
3+
"_name": "CEO"
4+
}
5+
]

0 commit comments

Comments
 (0)