|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace App\Http\Controllers\Frontend; |
|
4
|
|
|
|
|
5
|
|
|
// VALIDATION: change the requests to match your own file names if you need form validation |
|
6
|
|
|
use App\Http\Requests\Frontend\ImportRequest; |
|
7
|
|
|
use App\Http\Requests\Frontend\ImportRequest as StoreRequest; |
|
8
|
|
|
use App\Http\Requests\Frontend\ImportRequest as UpdateRequest; |
|
9
|
|
|
use App\Http\Traits\UsesEnums; |
|
10
|
|
|
use App\Http\Traits\UsesPolicies; |
|
11
|
|
|
use App\Models\Batch; |
|
12
|
|
|
use App\Models\Import; |
|
13
|
|
|
use App\Models\Project; |
|
14
|
|
|
use App\Wizard\Import\ProjectSteps\ApproveImportStep; |
|
15
|
|
|
use App\Wizard\Import\ProjectSteps\DisplayResultsStep; |
|
16
|
|
|
use App\Wizard\Import\ProjectSteps\SelectWorksheetsStep; |
|
17
|
|
|
use App\Wizard\Import\ProjectSteps\SetSpreadsheetStep; |
|
18
|
|
|
use Backpack\CRUD\app\Http\Controllers\CrudController; |
|
19
|
|
|
use Illuminate\Http\RedirectResponse; |
|
20
|
|
|
use Illuminate\Http\Request; |
|
21
|
|
|
use Smajti1\Laravel\Exceptions\StepNotFoundException; |
|
22
|
|
|
use Smajti1\Laravel\Wizard; |
|
23
|
|
|
use const null; |
|
24
|
|
|
use function method_exists; |
|
25
|
|
|
|
|
26
|
|
|
class ImportCrudController extends CrudController |
|
27
|
|
|
{ |
|
28
|
|
|
use UsesPolicies, UsesEnums; |
|
29
|
|
|
/** |
|
30
|
|
|
* @var array |
|
31
|
|
|
*/ |
|
32
|
|
|
public $steps = [ |
|
33
|
|
|
'spreadsheet' => SetSpreadsheetStep::class, |
|
34
|
|
|
'worksheets' => SelectWorksheetsStep::class, |
|
35
|
|
|
'approve' => ApproveImportStep::class, |
|
36
|
|
|
'report' => DisplayResultsStep::class, |
|
37
|
|
|
]; |
|
38
|
|
|
/** |
|
39
|
|
|
* @var Wizard |
|
40
|
|
|
*/ |
|
41
|
|
|
protected $wizard; |
|
42
|
|
|
|
|
43
|
|
|
/** |
|
44
|
|
|
* ImportCrudController constructor. |
|
45
|
|
|
* |
|
46
|
|
|
* @throws \Smajti1\Laravel\Exceptions\StepNotFoundException |
|
47
|
|
|
*/ |
|
48
|
|
|
public function __construct() |
|
49
|
|
|
{ |
|
50
|
|
|
parent::__construct(); |
|
51
|
|
|
$this->wizard = new Wizard($this->steps, $sessionKeyName = 'project-import'); |
|
52
|
|
|
|
|
53
|
|
|
view()->share(['wizard' => $this->wizard]); |
|
54
|
|
|
} |
|
55
|
|
|
|
|
56
|
|
|
/** |
|
57
|
|
|
* @throws \Exception |
|
58
|
|
|
*/ |
|
59
|
|
|
public function setup() |
|
60
|
|
|
{ |
|
61
|
|
|
/* |
|
62
|
|
|
|-------------------------------------------------------------------------- |
|
63
|
|
|
| BASIC CRUD INFORMATION |
|
64
|
|
|
|-------------------------------------------------------------------------- |
|
65
|
|
|
*/ |
|
66
|
|
|
$this->crud->setModel(Import::class); |
|
67
|
|
|
$this->crud->setRoute(config('backpack.base.route_prefix') . '/projects/' . request()->project . '/imports'); |
|
68
|
|
|
$this->crud->setEntityNameStrings('Import', 'Imports'); |
|
69
|
|
|
$this->addCustomDoctrineColumnTypes(); |
|
70
|
|
|
//$this->crud->setFromDb(); |
|
|
|
|
|
|
71
|
|
|
|
|
72
|
|
|
// ------ CRUD FIELDS |
|
73
|
|
|
|
|
74
|
|
|
// $this->crud->addFields(); |
|
|
|
|
|
|
75
|
|
|
// $this->crud->addFields($array_of_arrays, 'update/create/both'); |
|
|
|
|
|
|
76
|
|
|
// $this->crud->removeField('name', 'update/create/both'); |
|
|
|
|
|
|
77
|
|
|
// $this->crud->removeFields($array_of_names, 'update/create/both'); |
|
|
|
|
|
|
78
|
|
|
|
|
79
|
|
|
// ------ CRUD COLUMNS |
|
80
|
|
|
// $this->crud->addColumn(); // add a single column, at the end of the stack |
|
|
|
|
|
|
81
|
|
|
// $this->crud->addColumns(); // add multiple columns, at the end of the stack |
|
|
|
|
|
|
82
|
|
|
// $this->crud->removeColumn('column_name'); // remove a column from the stack |
|
|
|
|
|
|
83
|
|
|
// $this->crud->removeColumns(['column_name_1', 'column_name_2']); // remove an array of columns from the stack |
|
|
|
|
|
|
84
|
|
|
// $this->crud->setColumnDetails('column_name', ['attribute' => 'value']); // adjusts the properties of the passed in column (by name) |
|
|
|
|
|
|
85
|
|
|
// $this->crud->setColumnsDetails(['column_1', 'column_2'], ['attribute' => 'value']); |
|
|
|
|
|
|
86
|
|
|
|
|
87
|
|
|
// ------ CRUD BUTTONS |
|
88
|
|
|
// possible positions: 'beginning' and 'end'; defaults to 'beginning' for the 'line' stack, 'end' for the others; |
|
89
|
|
|
// $this->crud->addButton($stack, $name, $type, $content, $position); // add a button; possible types are: view, model_function |
|
|
|
|
|
|
90
|
|
|
// $this->crud->addButtonFromModelFunction($stack, $name, $model_function_name, $position); // add a button whose HTML is returned by a method in the CRUD model |
|
|
|
|
|
|
91
|
|
|
// $this->crud->addButtonFromView($stack, $name, $view, $position); // add a button whose HTML is in a view placed at resources\views\vendor\backpack\crud\buttons |
|
|
|
|
|
|
92
|
|
|
// $this->crud->removeButton($name); |
|
|
|
|
|
|
93
|
|
|
// $this->crud->removeButtonFromStack($name, $stack); |
|
|
|
|
|
|
94
|
|
|
// $this->crud->removeAllButtons(); |
|
|
|
|
|
|
95
|
|
|
// $this->crud->removeAllButtonsFromStack('line'); |
|
|
|
|
|
|
96
|
|
|
|
|
97
|
|
|
// ------ CRUD ACCESS |
|
98
|
|
|
$this->crud->allowAccess(['list', 'show']); |
|
99
|
|
|
$this->crud->denyAccess(['create', 'update', 'delete', 'importProject']); |
|
100
|
|
|
|
|
101
|
|
|
// ------ CRUD REORDER |
|
102
|
|
|
// $this->crud->enableReorder('label_name', MAX_TREE_LEVEL); |
|
|
|
|
|
|
103
|
|
|
// NOTE: you also need to do allow access to the right users: $this->crud->allowAccess('reorder'); |
|
104
|
|
|
|
|
105
|
|
|
// ------ CRUD DETAILS ROW |
|
106
|
|
|
// $this->crud->enableDetailsRow(); |
|
|
|
|
|
|
107
|
|
|
// NOTE: you also need to do allow access to the right users: $this->crud->allowAccess('details_row'); |
|
108
|
|
|
// NOTE: you also need to do overwrite the showDetailsRow($id) method in your EntityCrudController to show whatever you'd like in the details row OR overwrite the views/backpack/crud/details_row.blade.php |
|
109
|
|
|
|
|
110
|
|
|
// ------ REVISIONS |
|
111
|
|
|
// You also need to use \Venturecraft\Revisionable\RevisionableTrait; |
|
112
|
|
|
// Please check out: https://laravel-backpack.readme.io/docs/crud#revisions |
|
113
|
|
|
// $this->crud->allowAccess('revisions'); |
|
|
|
|
|
|
114
|
|
|
|
|
115
|
|
|
// ------ AJAX TABLE VIEW |
|
116
|
|
|
// Please note the drawbacks of this though: |
|
117
|
|
|
// - 1-n and n-n columns are not searchable |
|
118
|
|
|
// - date and datetime columns won't be sortable anymore |
|
119
|
|
|
// $this->crud->enableAjaxTable(); |
|
|
|
|
|
|
120
|
|
|
|
|
121
|
|
|
// ------ DATATABLE EXPORT BUTTONS |
|
122
|
|
|
// Show export to PDF, CSV, XLS and Print buttons on the table view. |
|
123
|
|
|
// Does not work well with AJAX datatables. |
|
124
|
|
|
// $this->crud->enableExportButtons(); |
|
|
|
|
|
|
125
|
|
|
|
|
126
|
|
|
// ------ ADVANCED QUERIES |
|
127
|
|
|
// $this->crud->addClause('active'); |
|
|
|
|
|
|
128
|
|
|
// $this->crud->addClause('type', 'car'); |
|
|
|
|
|
|
129
|
|
|
// $this->crud->addClause('where', 'name', '==', 'car'); |
|
|
|
|
|
|
130
|
|
|
// $this->crud->addClause('whereName', 'car'); |
|
|
|
|
|
|
131
|
|
|
// $this->crud->addClause('whereHas', 'posts', function($query) { |
|
|
|
|
|
|
132
|
|
|
// $query->activePosts(); |
|
|
|
|
|
|
133
|
|
|
// }); |
|
134
|
|
|
// $this->crud->addClause('withoutGlobalScopes'); |
|
|
|
|
|
|
135
|
|
|
// $this->crud->addClause('withoutGlobalScope', VisibleScope::class); |
|
|
|
|
|
|
136
|
|
|
// $this->crud->with(); // eager load relationships |
|
|
|
|
|
|
137
|
|
|
// $this->crud->orderBy(); |
|
|
|
|
|
|
138
|
|
|
// $this->crud->groupBy(); |
|
|
|
|
|
|
139
|
|
|
// $this->crud->limit(); |
|
|
|
|
|
|
140
|
|
|
} |
|
141
|
|
|
|
|
142
|
|
|
/** |
|
143
|
|
|
* @param UpdateRequest $request |
|
144
|
|
|
* @param Project $project |
|
145
|
|
|
* |
|
146
|
|
|
* @return RedirectResponse |
|
147
|
|
|
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException |
|
148
|
|
|
* @throws \Illuminate\Auth\Access\AuthorizationException |
|
149
|
|
|
*/ |
|
150
|
|
|
public function createBatch(ImportRequest $request, Project $project) |
|
151
|
|
|
{ |
|
152
|
|
|
$this->policyAuthorize('import', $project, $project->id); |
|
153
|
|
|
/* @var Batch $batch */ |
|
154
|
|
|
if ($this->wizard->dataHas('batch_id')) { |
|
155
|
|
|
$batch = Batch::findOrFail($this->wizard->dataGet('batch_id')); |
|
156
|
|
|
} else { |
|
157
|
|
|
$batch = Batch::make(['project_id' => $project->id]); |
|
158
|
|
|
$this->setWizardData('batch_id', $batch->id); |
|
159
|
|
|
} |
|
160
|
|
|
|
|
161
|
|
|
return $this->processImportProject($request, $project, $batch, $this->wizard->first()->key); |
|
162
|
|
|
} |
|
163
|
|
|
|
|
164
|
|
|
/** |
|
165
|
|
|
* @param Request $request |
|
166
|
|
|
* @param Project $project |
|
167
|
|
|
* @param Batch $batch |
|
168
|
|
|
* @param null $step |
|
|
|
|
|
|
169
|
|
|
* |
|
170
|
|
|
* @return \Backpack\CRUD\app\Http\Controllers\Response |
|
|
|
|
|
|
171
|
|
|
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException |
|
172
|
|
|
* @throws \Illuminate\Auth\Access\AuthorizationException |
|
173
|
|
|
*/ |
|
174
|
|
|
public function importProject(Request $request, Project $project, Batch $batch, $step = null) |
|
175
|
|
|
{ |
|
176
|
|
|
$this->policyAuthorize('import', $project, $project->id); |
|
177
|
|
|
//if we get to here we're authorized to import a project, so we authorize create |
|
178
|
|
|
$this->crud->allowAccess(['create', 'edit']); |
|
179
|
|
|
try { |
|
180
|
|
|
if (null === $step) { |
|
181
|
|
|
if (null === $batch->id) { |
|
|
|
|
|
|
182
|
|
|
$step = $this->wizard->first(); |
|
183
|
|
|
$this->wizard->data([]); |
|
184
|
|
|
} else { |
|
185
|
|
|
$step = $this->wizard->firstOrLastProcessed(); |
|
186
|
|
|
} |
|
187
|
|
|
} else { |
|
188
|
|
|
$step = $this->wizard->getBySlug($step); |
|
189
|
|
|
} |
|
190
|
|
|
} catch (StepNotFoundException $e) { |
|
191
|
|
|
abort(404); |
|
192
|
|
|
} |
|
193
|
|
|
//we've jumped into a middle step of the sequence (we should always have worksheets after step 1) |
|
194
|
|
|
if ($step->number > 1 && ! $batch->dataHas('googlesheets')) { |
|
195
|
|
|
$step = $this->wizard->first(); |
|
196
|
|
|
} |
|
197
|
|
|
if ($this->wizard->data() != $batch->step_data) { |
|
198
|
|
|
//we've returned from an incomplete import |
|
199
|
|
|
//and have to load the previous step data into the wizard from the database |
|
200
|
|
|
$this->wizard->data($batch->step_data); |
|
201
|
|
|
} |
|
202
|
|
|
|
|
203
|
|
|
$this->setWizardData('batch_id', $batch->id); |
|
204
|
|
|
$this->setWizardData('project_id', $project->id); |
|
205
|
|
|
|
|
206
|
|
|
if (method_exists($step, 'preProcess')) { |
|
207
|
|
|
$step->preProcess($request, $this->wizard); |
|
208
|
|
|
} |
|
209
|
|
|
|
|
210
|
|
|
$this->crud->setCreateView('frontend.import.project.wizard'); |
|
211
|
|
|
$this->crud->setRoute(config('backpack.base.route_prefix') . 'projects/' . $project->id); |
|
212
|
|
|
$this->crud->addFields($step->fields(), 'create'); |
|
213
|
|
|
$this->data['step'] = $step; |
|
214
|
|
|
$this->data['project'] = $project; |
|
215
|
|
|
$this->data['batch'] = $batch; |
|
216
|
|
|
$this->data['wizard_data'] = $this->wizard->data(); |
|
217
|
|
|
if (isset($this->wizard->data()[$step->key])) { |
|
218
|
|
|
$data = $this->wizard->data()[$step->key]; |
|
219
|
|
|
if ($step->key === 'spreadsheet') { |
|
220
|
|
|
foreach ($data as $key => $datum) { |
|
221
|
|
|
$this->crud->create_fields[$key]['value'] = $datum; |
|
222
|
|
|
} |
|
223
|
|
|
} |
|
224
|
|
|
if ($step->key === 'worksheets') { |
|
225
|
|
|
$this->crud->create_fields['worksheets']['value'] = $data; |
|
226
|
|
|
} |
|
227
|
|
|
if ($step->key === 'approve') { |
|
228
|
|
|
$this->crud->create_fields['approve']['value'] = $data; |
|
229
|
|
|
} |
|
230
|
|
|
} |
|
231
|
|
|
|
|
232
|
|
|
return parent::create(); |
|
233
|
|
|
} |
|
234
|
|
|
|
|
235
|
|
|
/** |
|
236
|
|
|
* @param UpdateRequest $request |
|
237
|
|
|
* @param Project $project |
|
238
|
|
|
* @param Batch $batch |
|
239
|
|
|
* @param null $step |
|
240
|
|
|
* |
|
241
|
|
|
* @return RedirectResponse |
|
242
|
|
|
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException |
|
243
|
|
|
* @throws \Illuminate\Auth\Access\AuthorizationException |
|
244
|
|
|
*/ |
|
245
|
|
|
public function processImportProject(ImportRequest $request, Project $project, Batch $batch, $step = null): RedirectResponse |
|
246
|
|
|
{ |
|
247
|
|
|
$this->policyAuthorize('import', $project, $project->id); |
|
248
|
|
|
//if we get to here we're authorized to import a project, so we authorize create |
|
249
|
|
|
$this->crud->allowAccess(['create']); |
|
250
|
|
|
try { |
|
251
|
|
|
$step = $this->wizard->getBySlug($step); |
|
252
|
|
|
} catch (StepNotFoundException $e) { |
|
253
|
|
|
abort(404); |
|
254
|
|
|
} |
|
255
|
|
|
|
|
256
|
|
|
//here we validate the input from the step |
|
257
|
|
|
|
|
258
|
|
|
if (method_exists($step, 'validate')) { |
|
259
|
|
|
$step->validate($request); |
|
260
|
|
|
} else { |
|
261
|
|
|
$this->validate($request, $step->rules($request)); |
|
262
|
|
|
} |
|
263
|
|
|
|
|
264
|
|
|
//$request->flash(); |
|
|
|
|
|
|
265
|
|
|
|
|
266
|
|
|
//handle the next/last step |
|
267
|
|
|
$step->process($request); |
|
268
|
|
|
|
|
269
|
|
|
$batch->step_data = $this->wizard->data(); |
|
270
|
|
|
$batch->next_step = $this->wizard->nextSlug(); |
|
271
|
|
|
$batch->run_description = $batch->run_description ?? $this->wizard->dataGet('title'); |
|
272
|
|
|
$batch->save(); |
|
273
|
|
|
|
|
274
|
|
|
//and redirect to the next step if valid |
|
275
|
|
|
//$request->session()->save(); |
|
|
|
|
|
|
276
|
|
|
return redirect()->route('frontend.project.import', |
|
277
|
|
|
['project' => $project->id, 'batch' => $batch, 'step' => $this->wizard->nextSlug()]); |
|
278
|
|
|
} |
|
279
|
|
|
|
|
280
|
|
|
/** |
|
281
|
|
|
* @param UpdateRequest $request |
|
282
|
|
|
* |
|
283
|
|
|
* @return RedirectResponse |
|
284
|
|
|
*/ |
|
285
|
|
|
public function store(StoreRequest $request) |
|
|
|
|
|
|
286
|
|
|
{ |
|
287
|
|
|
// your additional operations before save here |
|
288
|
|
|
$redirect_location = parent::storeCrud(); |
|
289
|
|
|
// your additional operations after save here |
|
290
|
|
|
// use $this->data['entry'] or $this->crud->entry |
|
|
|
|
|
|
291
|
|
|
return $redirect_location; |
|
292
|
|
|
} |
|
293
|
|
|
|
|
294
|
|
|
/** |
|
295
|
|
|
* @param UpdateRequest $request |
|
296
|
|
|
* |
|
297
|
|
|
* @return RedirectResponse |
|
298
|
|
|
*/ |
|
299
|
|
|
public function update(UpdateRequest $request) |
|
|
|
|
|
|
300
|
|
|
{ |
|
301
|
|
|
// your additional operations before save here |
|
302
|
|
|
$redirect_location = parent::updateCrud(); |
|
303
|
|
|
// your additional operations after save here |
|
304
|
|
|
// use $this->data['entry'] or $this->crud->entry |
|
|
|
|
|
|
305
|
|
|
return $redirect_location; |
|
306
|
|
|
} |
|
307
|
|
|
|
|
308
|
|
|
/** |
|
309
|
|
|
* @param $step |
|
310
|
|
|
* |
|
311
|
|
|
* @return array |
|
312
|
|
|
*/ |
|
313
|
|
|
private static function getWizardStep($step): array |
|
|
|
|
|
|
314
|
|
|
{ |
|
315
|
|
|
$steps = [ |
|
316
|
|
|
[ |
|
317
|
|
|
'name' => 'spreadsheet', |
|
318
|
|
|
'step' => 1, |
|
319
|
|
|
'title' => 'Start with a Spreadsheet URL...', |
|
320
|
|
|
'instructions' => 'Some instructions', |
|
321
|
|
|
], |
|
322
|
|
|
[ |
|
323
|
|
|
'name' => 'worksheets', |
|
324
|
|
|
'step' => 2, |
|
325
|
|
|
'title' => 'Choose the Worksheets...', |
|
326
|
|
|
'instructions' => 'Some instructions', |
|
327
|
|
|
], |
|
328
|
|
|
[ |
|
329
|
|
|
'name' => 'approve', |
|
330
|
|
|
'step' => 3, |
|
331
|
|
|
'title' => 'Approve the changes..', |
|
332
|
|
|
'instructions' => 'Some instructions', |
|
333
|
|
|
], |
|
334
|
|
|
[ |
|
335
|
|
|
'name' => 'import', |
|
336
|
|
|
'step' => 4, |
|
337
|
|
|
'title' => 'Start with a Spreadsheet URL', |
|
338
|
|
|
'instructions' => 'Some instructions', |
|
339
|
|
|
], |
|
340
|
|
|
]; |
|
341
|
|
|
$key = $step !== null ? array_search($step, array_column($steps, 'name')) : 0; |
|
342
|
|
|
if ($key === false) { |
|
343
|
|
|
abort(404, 'Invalid Step'); |
|
344
|
|
|
} |
|
345
|
|
|
$steps[$key]['next'] = ($key < count($steps)) ? $steps[$key + 1]['name'] : false; |
|
346
|
|
|
|
|
347
|
|
|
return $steps[$key]; |
|
348
|
|
|
} |
|
349
|
|
|
|
|
350
|
|
|
/** |
|
351
|
|
|
* @param string $key |
|
352
|
|
|
* @param mixed $value |
|
353
|
|
|
*/ |
|
354
|
|
|
private function setWizardData($key, $value): void |
|
355
|
|
|
{ |
|
356
|
|
|
$data = $this->wizard->data(); |
|
357
|
|
|
$data[$key] = $value; |
|
358
|
|
|
$this->wizard->data($data); |
|
359
|
|
|
} |
|
360
|
|
|
} |
|
361
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.