Completed
Push — master ( 24f26d...acbbc3 )
by Adrian
11:01
created

Model::getDetailFields()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 29
Code Lines 19

Duplication

Lines 29
Ratio 100 %

Code Coverage

Tests 20
CRAP Score 4

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 29
loc 29
ccs 20
cts 20
cp 1
rs 8.5806
cc 4
eloc 19
nc 6
nop 1
crap 4
1
<?php
2
namespace Anavel\Crud\Abstractor\Eloquent;
3
4
use Anavel\Crud\Abstractor\Eloquent\Traits\HandleFiles;
5
use Anavel\Crud\Abstractor\Eloquent\Traits\ModelFields;
6
use Anavel\Crud\Contracts\Abstractor\Field as FieldContract;
7
use Anavel\Crud\Contracts\Abstractor\Model as ModelAbstractorContract;
8
use Anavel\Crud\Abstractor\ConfigurationReader;
9
use Anavel\Crud\Contracts\Abstractor\Relation;
10
use Anavel\Crud\Contracts\Abstractor\RelationFactory as RelationFactoryContract;
11
use Anavel\Crud\Contracts\Abstractor\FieldFactory as FieldFactoryContract;
12
use ANavallaSuiza\Laravel\Database\Contracts\Dbal\AbstractionLayer;
13
use FormManager\ElementInterface;
14
use Illuminate\Database\Eloquent\Model as LaravelModel;
15
use App;
16
use Anavel\Crud\Contracts\Form\Generator as FormGenerator;
17
use Anavel\Crud\Abstractor\Exceptions\AbstractorException;
18
use Illuminate\Http\Request;
19
use Illuminate\Support\Collection;
20
use League\Flysystem\Adapter\Local;
21
use League\Flysystem\Filesystem;
22
23
class Model implements ModelAbstractorContract
24
{
25
    use ConfigurationReader;
26
    use ModelFields;
27
    use HandleFiles;
28
29
    protected $dbal;
30
    protected $relationFactory;
31
    protected $fieldFactory;
32
    protected $generator;
33
34
    protected $model;
35
    protected $config;
36
37
    protected $slug;
38
    protected $name;
39
    protected $instance;
40
41 17
    public function __construct(
42
        $config,
43
        AbstractionLayer $dbal,
44
        RelationFactoryContract $relationFactory,
45
        FieldFactoryContract $fieldFactory,
46
        FormGenerator $generator
47
    ) {
48 17
        if (is_array($config)) {
49 17
            $this->model = $config['model'];
50 17
            $this->config = $config;
51 17
        } else {
52
            $this->model = $config;
53
            $this->config = [];
54
        }
55
56 17
        $this->dbal = $dbal;
57 17
        $this->relationFactory = $relationFactory;
58 17
        $this->fieldFactory = $fieldFactory;
59 17
        $this->generator = $generator;
60 17
    }
61
62 3
    public function setSlug($slug)
63
    {
64 3
        $this->slug = $slug;
65
66 3
        return $this;
67
    }
68
69 2
    public function setName($name)
70
    {
71 2
        $this->name = $name;
72
73 2
        return $this;
74
    }
75
76 5
    public function setInstance($instance)
77
    {
78 5
        $this->instance = $instance;
79
80 5
        return $this;
81
    }
82
83
    public function getSlug()
84
    {
85
        return $this->slug;
86
    }
87
88
    public function getName()
89
    {
90
        return transcrud($this->name);
91
    }
92
93 1
    public function getModel()
94
    {
95 1
        return $this->model;
96
    }
97
98
    public function getInstance()
99
    {
100
        return $this->instance;
101
    }
102
103
    /**
104
     * @return array
105
     */
106
    public function getConfig()
107
    {
108
        return $this->config;
109
    }
110
111
    public function isSoftDeletes()
112
    {
113
        return $this->getConfigValue('soft_deletes') ? true : false;
114
    }
115
116 8
    public function getColumns($action, $withForeignKeys = false)
117
    {
118 8
        $tableColumns = $this->dbal->getTableColumns();
119
120 8
        $filteredColumns = [];
121 8
        foreach ($tableColumns as $name => $column) {
122 8
            $filteredColumns[str_replace('`', '', $name)] = $column;
123 8
        }
124 8
        $tableColumns = $filteredColumns;
125
126
127 8
        $foreignKeysName = [];
128 8
        if ($withForeignKeys === false) {
129 8
            $foreignKeys = $this->dbal->getTableForeignKeys();
130
131 8
            foreach ($foreignKeys as $foreignKey) {
132
                foreach ($foreignKey->getColumns() as $columnName) {
133
                    $foreignKeysName[] = $columnName;
134
                }
135 8
            }
136 8
        }
137
138 8
        $customDisplayedColumns = $this->getConfigValue($action, 'display');
139 8
        $customHiddenColumns = $this->getConfigValue($action, 'hide') ? : [];
140
141 8
        $relations = $this->getRelations();
142
143 8
        $columns = array();
144 8
        if (! empty($customDisplayedColumns) && is_array($customDisplayedColumns)) {
145 8
            foreach ($customDisplayedColumns as $customColumn) {
146 8
                if (strpos($customColumn, '.')) {
147
                    $customColumnRelation = explode('.', $customColumn);
148
149
                    if (! $relations->has($customColumnRelation[0])) {
150
                        throw new AbstractorException("Relation " . $customColumnRelation[0] . " not configured on " . $this->getModel());
151
                    }
152
153
                    $relation = $relations->get($customColumnRelation[0]);
154
155
                    $relationColumns = $relation->getModelAbstractor()->getColumns($action);
156
157
                    if (! array_key_exists($customColumnRelation[1], $relationColumns)) {
158
                        throw new AbstractorException("Column " . $customColumnRelation[1] . " does not exist on relation ".$customColumnRelation[0]. " of model " . $this->getModel());
159
                    }
160
161
                    $columns[$customColumn] = $relationColumns[$customColumnRelation[1]];
162
                } else {
163 8
                    if (! array_key_exists($customColumn, $tableColumns)) {
164
                        throw new AbstractorException("Column " . $customColumn . " does not exist on " . $this->getModel());
165
                    }
166
167 8
                    $columns[$customColumn] = $tableColumns[$customColumn];
168
                }
169 8
            }
170 8
        } else {
171
            foreach ($tableColumns as $name => $column) {
172
                if (in_array($name, $customHiddenColumns)) {
173
                    continue;
174
                }
175
176
                if (in_array($name, $foreignKeysName)) {
177
                    continue;
178
                }
179
180
                $columns[$name] = $column;
181
            }
182
        }
183
184 8
        return $columns;
185
    }
186
187
    /**
188
     * @return \Illuminate\Support\Collection
189
     */
190 10
    public function getRelations()
191
    {
192 10
        $configRelations = $this->getConfigValue('relations');
193
194 10
        $relations = collect();
195
196 10
        if (! empty($configRelations)) {
197 10
            foreach ($configRelations as $relationName => $configRelation) {
198 10
                if (is_int($relationName)) {
199
                    $relationName = $configRelation;
200
                }
201
202 10
                $config = [];
203 10
                if ($configRelation !== $relationName) {
204 10
                    if (! is_array($configRelation)) {
205
                        $config['type'] = $configRelation;
206
                    } else {
207 10
                        $config = $configRelation;
208
                    }
209 10
                }
210
211
                /** @var Relation $relation */
212 10
                $relation = $this->relationFactory->setModel($this->instance)
213 10
                    ->setConfig($config)
214 10
                    ->get($relationName);
215
216 10
                $secondaryRelations = $relation->getSecondaryRelations();
217
218
219 10
                if (! $secondaryRelations->isEmpty()) {
220 1
                    $relations->put(
221 1
                        $relationName,
222 1
                        collect(['relation' => $relation, 'secondaryRelations' => $secondaryRelations])
223 1
                    );
224 1
                } else {
225 9
                    $relations->put($relationName, $relation);
226
                }
227
228 10
            }
229 10
        }
230
231 10
        return $relations;
232
    }
233
234
    /**
235
     * @param string|null $arrayKey
236
     * @return array
237
     * @throws AbstractorException
238
     */
239 2 View Code Duplication
    public function getListFields($arrayKey = 'main')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
240
    {
241 2
        $columns = $this->getColumns('list');
242
243 2
        $fieldsPresentation = $this->getConfigValue('fields_presentation') ? : [];
244
245 2
        $fields = array();
246 2
        foreach ($columns as $name => $column) {
247 2
            $presentation = null;
248 2
            if (array_key_exists($name, $fieldsPresentation)) {
249 2
                $presentation = $fieldsPresentation[$name];
250 2
            }
251
252
            $config = [
253 2
                'name'         => $name,
254 2
                'presentation' => $presentation,
255 2
                'form_type'    => null,
256 2
                'validation'   => null,
257
                'functions'    => null
258 2
            ];
259
260 2
            $fields[$arrayKey][] = $this->fieldFactory
261 2
                ->setColumn($column)
262 2
                ->setConfig($config)
263 2
                ->get();
264 2
        }
265
266 2
        return $fields;
267
    }
268
269
    /**
270
     * @param string|null $arrayKey
271
     * @return array
272
     * @throws AbstractorException
273
     */
274 2 View Code Duplication
    public function getDetailFields($arrayKey = 'main')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
275
    {
276 2
        $columns = $this->getColumns('detail');
277
278 2
        $fieldsPresentation = $this->getConfigValue('fields_presentation') ? : [];
279
280 2
        $fields = array();
281 2
        foreach ($columns as $name => $column) {
282 2
            $presentation = null;
283 2
            if (array_key_exists($name, $fieldsPresentation)) {
284 2
                $presentation = $fieldsPresentation[$name];
285 2
            }
286
287
            $config = [
288 2
                'name'         => $name,
289 2
                'presentation' => $presentation,
290 2
                'form_type'    => null,
291 2
                'validation'   => null,
292
                'functions'    => null
293 2
            ];
294
295 2
            $fields[$arrayKey][] = $this->fieldFactory
296 2
                ->setColumn($column)
297 2
                ->setConfig($config)
298 2
                ->get();
299 2
        }
300
301 2
        return $fields;
302
    }
303
304
    /**
305
     * @param bool|null $withForeignKeys
306
     * @param string|null $arrayKey
307
     * @return array
308
     * @throws AbstractorException
309
     */
310 4
    public function getEditFields($withForeignKeys = false, $arrayKey = 'main')
311
    {
312 4
        $columns = $this->getColumns('edit', $withForeignKeys);
0 ignored issues
show
Bug introduced by
It seems like $withForeignKeys defined by parameter $withForeignKeys on line 310 can also be of type null; however, Anavel\Crud\Abstractor\E...ent\Model::getColumns() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
313
314 4
        $this->readConfig('edit');
315
316 4
        $fields = array();
317 4
        foreach ($columns as $name => $column) {
318 4
            if (! in_array($name, $this->getReadOnlyColumns())) {
319 4
                $presentation = null;
320 4
                if (array_key_exists($name, $this->fieldsPresentation)) {
321
                    $presentation = $this->fieldsPresentation[$name];
322
                }
323
324
                $config = [
325 4
                    'name'         => $name,
326 4
                    'presentation' => $presentation,
327 4
                    'form_type'    => null,
328 4
                    'validation'   => null,
329
                    'functions'    => null
330 4
                ];
331
332 4
                $config = $this->setConfig($config, $name);
333
334 4
                $field = $this->fieldFactory
335 4
                    ->setColumn($column)
336 4
                    ->setConfig($config)
337 4
                    ->get();
338
339 4
                if (! empty($this->instance) && ! empty($this->instance->getAttribute($name))) {
340
                    $field->setValue($this->instance->getAttribute($name));
341
                }
342
343 4
                $fields[$arrayKey][$name] = $field;
344
345 4 View Code Duplication
                if (! empty($config['form_type']) && $config['form_type'] === 'file') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
346 4
                    $field = $this->fieldFactory
347 4
                        ->setColumn($column)
348 4
                        ->setConfig([
349 4
                            'name'         => $name . '__delete',
350 4
                            'presentation' => null,
351 4
                            'form_type'    => 'checkbox',
352 4
                            'no_validate'  => true,
353
                            'functions'    => null
354 4
                        ])
355 4
                        ->get();
356 4
                    $fields[$arrayKey][$name . '__delete'] = $field;
357 4
                }
358 4
            }
359 4
        }
360
361 4
        return $fields;
362
    }
363
364 4
    protected function getReadOnlyColumns()
365
    {
366 4
        $columns = [LaravelModel::CREATED_AT, LaravelModel::UPDATED_AT];
367
368 4
        $columns[] = $this->dbal->getModel()->getKeyName();
369
370 4
        return $columns;
371
    }
372
373
    /**
374
     * @param string $action
375
     * @return ElementInterface
376
     */
377 2
    public function getForm($action)
378
    {
379 2
        $this->generator->setModelFields($this->getEditFields());
380 2
        $this->generator->setRelatedModelFields($this->getRelations());
381
382 2
        return $this->generator->getForm($action);
383
    }
384
385
    /**
386
     * @param array $requestForm
0 ignored issues
show
Documentation introduced by
There is no parameter named $requestForm. Did you maybe mean $request?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
387
     * @return mixed
388
     */
389 1
    public function persist(Request $request)
390
    {
391
        /** @var \ANavallaSuiza\Laravel\Database\Contracts\Manager\ModelManager $modelManager */
392 1
        $modelManager = App::make('ANavallaSuiza\Laravel\Database\Contracts\Manager\ModelManager');
393 1
        if (! empty($this->instance)) {
394
            $item = $this->instance;
395
        } else {
396 1
            $item = $modelManager->getModelInstance($this->getModel());
397
        }
398
399
400 1
        $fields = $this->getEditFields(true);
401 1
        if (empty($fields['main']) && $this->getRelations()->isEmpty()) {
402
            return;
403
        }
404
405 1
        if (! empty($fields['main'])) {
406 1
            $skip = null;
407 1
            foreach ($fields['main'] as $key => $field) {
408
                /** @var FieldContract $field */
409 1
                if ($skip === $key) {
410
                    $skip = null;
411
                    continue;
412
                }
413 1
                $fieldName = $field->getName();
414 1
                $requestValue = $request->input("main.{$fieldName}");
415
416 1
                if (get_class($field->getFormField()) === \FormManager\Fields\Checkbox::class) {
417
                    if (empty($requestValue)) {
418
                        // Unchecked checkboxes are not sent, so we force setting them to false
419
                        $item->setAttribute(
420
                            $fieldName,
421
                            $field->applyFunctions(null)
422
                        );
423
                    } else {
424
                        $requestValue = true;
425
                    }
426
                }
427
428 1
                if (get_class($field->getFormField()) === \FormManager\Fields\File::class) {
429
                    $handleResult = $this->handleField($request, $item, $fields['main'], 'main', $fieldName);
430
                    if (! empty($handleResult['skip'])) {
431
                        $skip = $handleResult['skip'];
432
                    }
433
                    if (! empty($handleResult['requestValue'])) {
434
                        $requestValue = $handleResult['requestValue'];
435
                    }
436
                }
437
438
439 1
                if (! $field->saveIfEmpty() && empty($requestValue)) {
440
                    continue;
441
                }
442
443 1
                if (! empty($requestValue) || (empty($requestValue) && ! empty($item->getAttribute($fieldName)))) {
444 1
                    $item->setAttribute(
445 1
                        $fieldName,
446 1
                        $field->applyFunctions($requestValue)
447 1
                    );
448 1
                }
449 1
            }
450 1
        }
451
452 1
        $item->save();
453
454 1
        $this->setInstance($item);
455
456
457 1
        if (! empty($relations = $this->getRelations())) {
458 1
            foreach ($relations as $relationKey => $relation) {
459 1
                if ($relation instanceof Collection) {
460
                    $input = $request->input($relationKey);
461
                    $relation->get('relation')->persist($input, $request);
462
                } else {
463 1
                    $relation->persist($request->input($relationKey), $request);
464
                }
465 1
            }
466 1
        }
467
468 1
        return $item;
469
    }
470
471
    /**
472
     * @return array
473
     */
474 1
    public function getValidationRules()
475
    {
476 1
        return $this->generator->getValidationRules();
477
    }
478
479
    public function getFieldValue($item, $fieldName)
480
    {
481
        $value = null;
0 ignored issues
show
Unused Code introduced by
$value is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
482
483
        if (strpos($fieldName, '.')) {
484
            $customColumnRelation = explode('.', $fieldName);
485
486
            $relation = $item->{$customColumnRelation[0]};
487
            if (empty($relation)) {
488
                return null;
489
            }
490
491
            if ($relation instanceof \Illuminate\Support\Collection) {
492
                $relations = $this->getRelations();
493
494
                $relationAbstractor = $relations->get($customColumnRelation[0]);
495
496
                if ($relationAbstractor instanceof \Anavel\Crud\Abstractor\Eloquent\Relation\Translation) {
497
                    $value = $item->getAttribute($customColumnRelation[1]);
498
                } else {
499
                    $value = $relation->implode($customColumnRelation[1], ', ');
500
                }
501
            } else {
502
                $value = $relation->getAttribute($customColumnRelation[1]);
503
            }
504
        } else {
505
            $value = $item->getAttribute($fieldName);
506
        }
507
508
        return $value;
509
    }
510
}
511