Completed
Push — master ( 5fe226...5c1a79 )
by Adrian
22:45 queued 18:50
created

MiniCrud   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 321
Duplicated Lines 8.1 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 69.04%

Importance

Changes 0
Metric Value
dl 26
loc 321
ccs 136
cts 197
cp 0.6904
rs 6.5957
c 0
b 0
f 0
wmc 56
lcom 1
cbo 9

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getResults() 0 8 2
A setup() 0 4 1
C getEditFields() 0 63 15
C getEditFieldsBase() 14 75 7
F persist() 0 89 20
A setKeys() 0 4 1
A skipField() 12 12 4
A getDisplayType() 0 4 1
B addSecondaryRelationFields() 0 19 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like MiniCrud often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MiniCrud, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Anavel\Crud\Abstractor\Eloquent\Relation;
4
5
use Anavel\Crud\Abstractor\Eloquent\Relation\Traits\CheckRelationCompatibility;
6
use Anavel\Crud\Abstractor\Eloquent\Traits\HandleFiles;
7
use Anavel\Crud\Contracts\Abstractor\Field;
8
use Anavel\Crud\Contracts\Abstractor\Relation as RelationContract;
9
use Illuminate\Database\Eloquent\Model;
10
use Illuminate\Http\Request;
11
use Illuminate\Support\Collection;
12
13
class MiniCrud extends Relation
14
{
15
    use CheckRelationCompatibility;
16
    use HandleFiles;
17
18
    /** @var \ANavallaSuiza\Laravel\Database\Contracts\Dbal\AbstractionLayer $dbal */
19
    protected $dbal;
20
21
    /** @var Collection */
22
    protected $results;
23
24
    protected $compatibleEloquentRelations = [
25
        'Illuminate\Database\Eloquent\Relations\HasMany',
26
    ];
27
28
    /**
29
     * @return Collection
30
     */
31 4
    protected function getResults()
32 1
    {
33 4
        if (empty($this->results)) {
34 4
            return $this->results = $this->eloquentRelation->getResults();
35
        }
36
37
        return $this->results;
38
    }
39
40 8
    public function setup()
41
    {
42 8
        $this->checkRelationCompatibility();
43 6
    }
44
45
    /**
46
     * @return array
47
     */
48 2
    public function getEditFields($arrayKey = null)
49
    {
50 2
        $fields = [];
51 2
        if (empty($arrayKey)) {
52 2
            $arrayKey = $this->name;
53 2
        }
54
55 2
        $fieldsBase = $this->getEditFieldsBase();
56
57
58
        /** @var Collection $results */
59 2
        $results = $this->getResults();
60
61
62 2
        $results->put('emptyResult', '');
63 2
        if (!empty($fieldsBase)) {
64 2
            foreach ($results as $key => $result) {
65 2
                $tempFields = [];
66 2
                $index = $key === 'emptyResult' ? 0 : $result->id;
67
68 2
                foreach ($fieldsBase as $columnName => $fieldBase) {
69 2
                    $field = clone $fieldBase;
70 2
                    if ($this->skipField($columnName, $key)) {
71
                        continue;
72
                    }
73
74 2
                    if ($columnName != '__delete') {
75 2
                        if ($key !== 'emptyResult') {
76 2
                            $field->setValue($result->getAttribute($columnName));
77 2
                        }
78 2
                    } elseif ($key === 'emptyResult') {
79 2
                        continue;
80
                    }
81 2
                    $tempFields[$columnName] = $field;
82 2
                }
83
84
85 2
                $relationModel = $this->eloquentRelation->getRelated()->newInstance();
86 2
                if (!empty($result)) {
87 2
                    $relationModel = $result;
88 2
                }
89
90 2
                $this->modelAbstractor->setInstance($relationModel);
91 2
                $secondaryRelations = $this->getSecondaryRelations();
92
93 2
                if (!empty($secondaryRelations)) {
94 2
                    foreach ($secondaryRelations as $secondaryRelationKey => $secondaryRelation) {
95 2
                        foreach ($secondaryRelation->getEditFields($secondaryRelationKey) as $editGroupName => $editGroup) {
96
                            if ($secondaryRelation->getType() === 'Anavel\Crud\Abstractor\Eloquent\Relation\Select') {
97
                                $tempFields[$editGroup[key($editGroup)]->getName()] = $editGroup[key($editGroup)];
98
                            } else {
99
                                $tempFields[$editGroupName] = $editGroup;
100
                            }
101 2
                        }
102 2
                    }
103 2
                }
104
105 2
                $fields[$arrayKey][$index] = $tempFields;
106 2
            }
107 2
        }
108
109 2
        return $fields;
110
    }
111
112 4
    public function getEditFieldsBase()
113
    {
114 4
        $fields = [];
115 4
        $columns = $this->modelAbstractor->getColumns('edit');
116 4
        $this->readConfig('edit');
117
118 4
        if (!empty($columns)) {
119 4
            $readOnly = [Model::CREATED_AT, Model::UPDATED_AT];
120
121
            //Add field for model deletion
122
            $config = [
123 4
                'name'         => '__delete',
124 4
                'presentation' => 'Delete',
125 4
                'form_type'    => 'checkbox',
126 4
                'no_validate'  => true,
127 4
                'validation'   => null,
128 4
                'functions'    => null,
129 4
            ];
130
131
            /** @var Field $field */
132 4
            $field = $this->fieldFactory
133 4
                ->setColumn($columns[key($columns)]) //Set any column, we are not really using it
134 4
                ->setConfig($config)
135 4
                ->get();
136 4
            $fields['__delete'] = $field;
137
138
139 4
            foreach ($columns as $columnName => $column) {
140 4
                if (in_array($columnName, $readOnly, true)) {
141
                    continue;
142
                }
143
144 4
                $formType = null;
145 4
                if ($columnName === $this->eloquentRelation->getParent()->getKeyName()) {
146
                    $formType = 'hidden';
147
                }
148
149
                $config = [
150 4
                    'name'         => $columnName,
151 4
                    'presentation' => $this->name.' '.ucfirst(transcrud($columnName)),
152 4
                    'form_type'    => $formType,
153 4
                    'no_validate'  => true,
154 4
                    'validation'   => null,
155 4
                    'functions'    => null,
156 4
                ];
157
158 4
                $config = $this->setConfig($config, $columnName);
159
160
                /** @var Field $field */
161 4
                $field = $this->fieldFactory
162 4
                    ->setColumn($column)
163 4
                    ->setConfig($config)
164 4
                    ->get();
165
166 4
                $fields[$columnName] = $field;
167
168 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...
169
                    $field = $this->fieldFactory
170
                        ->setColumn($column)
171
                        ->setConfig([
172
                            'name'         => $columnName.'__delete',
173
                            'presentation' => null,
174
                            'form_type'    => 'checkbox',
175
                            'no_validate'  => true,
176
                            'validation'   => null,
177
                            'functions'    => null,
178
                        ])
179
                        ->get();
180
                    $fields[$columnName.'__delete'] = $field;
181
                }
182 4
            }
183 4
        }
184
185 4
        return $fields;
186
    }
187
188
    /**
189
     * @param array|null $relationArray
190
     *
191
     * @return mixed
192
     */
193 2
    public function persist(array $relationArray = null, Request $request)
194
    {
195 2
        if (!empty($relationArray)) {
196 2
            $keyName = $this->eloquentRelation->getParent()->getKeyName();
197 2
            $currentRelations = $this->getResults()->keyBy($keyName);
198
199 2
            $this->readConfig('edit');
200 2
            $fieldsBase = $this->getEditFieldsBase();
201
202 2
            foreach ($relationArray as $relationIndex => &$relation) {
203 2
                if (!empty($relation[$keyName])
204 2
                    && ($currentRelations->has($relation[$keyName]))
205 2
                ) {
206
                    $relationModel = $currentRelations->get($relation[$keyName]);
207
                } else {
208 2
                    $relationModel = $this->eloquentRelation->getRelated()->newInstance();
209
                }
210
211 2
                $this->modelAbstractor->setInstance($relationModel);
212 2
                $secondaryRelations = $this->getSecondaryRelations();
213
214
215 2
                $this->setKeys($relationModel);
216
217 2
                $shouldBeSkipped = true;
218 2
                $delayedRelations = collect();
219
220 2
                $skip = null;
221 2
                foreach ($fieldsBase as $fieldBaseKey => $field) {
222 2
                    $fieldName = $field->getName();
223
224 2
                    if (get_class($field->getFormField()) === \FormManager\Fields\File::class) {
225
                        $handleResult = $this->handleField($request, $relationModel, $fieldsBase,
226
                            $this->name.".$relationIndex", $fieldName, $this->modelAbstractor->mustDeleteFilesInFilesystem());
227
                        if (!empty($handleResult['skip'])) {
228
                            $skip = $handleResult['skip'];
229
                            unset($relationArray[$relationIndex][$skip]);
230
                        }
231
                        if (!empty($handleResult['requestValue'])) {
232
                            $relationArray[$relationIndex][$fieldName] = $handleResult['requestValue'];
233
                        }
234
                    }
235
236 2
                    if ($fieldName !== '__delete' && ($fieldName != $skip && (get_class($field->getFormField()) === \FormManager\Fields\Checkbox::class))) {
237
                        if (empty($relationArray[$relationIndex][$fieldName])) {
238
                            // Unchecked checkboxes are not sent, so we force setting them to false
239
                            $relationModel->setAttribute($fieldName, null);
240
                        } else {
241
                            $relationArray[$relationIndex][$fieldName] = true;
242
                        }
243
                    }
244 2
                }
245
246
247 2
                foreach ($relation as $fieldKey => $fieldValue) {
248 2
                    if ($secondaryRelations->has($fieldKey)) {
249 1
                        $delayedRelations->put($fieldKey, $fieldValue);
250 1
                        continue;
251
                    }
252
253
                    // This field can only come from existing models
254 2
                    if ($fieldKey === '__delete') {
255
                        $relationModel->delete();
256
                        $shouldBeSkipped = true;
257
                        break;
258
                    }
259
260 2
                    if ($shouldBeSkipped) {
261 2
                        $shouldBeSkipped = ($shouldBeSkipped === ($fieldValue === ''));
262 2
                    }
263
264 2
                    $relationModel->setAttribute($fieldKey, $fieldValue);
265 2
                }
266
267 2
                if (!$shouldBeSkipped) {
268 2
                    $relationModel->save();
269
270 2
                    if (!$delayedRelations->isEmpty()) {
271 1
                        foreach ($delayedRelations as $relationKey => $delayedRelation) {
272
                            /** @var RelationContract $secondaryRelation */
273 1
                            $secondaryRelation = $secondaryRelations->get($relationKey);
274
275 1
                            $secondaryRelation->persist($delayedRelation, $request);
276 1
                        }
277 1
                    }
278 2
                }
279 2
            }
280 2
        }
281 2
    }
282
283 1
    protected function setKeys(Model $relationModel)
284
    {
285 1
        $relationModel->setAttribute($this->eloquentRelation->getForeignKey(), $this->relatedModel->id);
286 1
    }
287
288 1 View Code Duplication
    protected function skipField($columnName, $key)
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...
289
    {
290 1
        if ($columnName === $this->eloquentRelation->getPlainForeignKey()) {
291
            return true;
292
        }
293
294 1
        if ($key === 'emptyResult' && ($columnName === $this->eloquentRelation->getParent()->getKeyName())) {
295
            return true;
296
        }
297
298 1
        return false;
299
    }
300
301
    /**
302
     * @return string
303
     */
304
    public function getDisplayType()
305
    {
306
        return self::DISPLAY_TYPE_TAB;
307
    }
308
309
    /**
310
     * @param array $fields
311
     *
312
     * @return array
313
     */
314
    public function addSecondaryRelationFields(array $fields)
315
    {
316
        $tempFields = [];
317
        foreach ($this->getSecondaryRelations() as $relationKey => $relation) {
318
            foreach ($relation->getEditFields($relationKey) as $editGroupName => $editGroup) {
319
                if ($relation->getType() === 'Anavel\Crud\Abstractor\Eloquent\Relation\Select') {
320
                    $tempFields[key($editGroup)] = $editGroup[key($editGroup)];
321
                } else {
322
                    $tempFields[$editGroupName] = $editGroup;
323
                }
324
            }
325
        }
326
        foreach ($fields[$this->name] as $groupKey => $mainFields) {
327
            $combinedFields = array_merge($mainFields, $tempFields);
328
            $fields[$this->name][$groupKey] = $combinedFields;
329
        }
330
331
        return $fields;
332
    }
333
}
334