Completed
Push — master ( b9a65d...e02d8c )
by
unknown
07:00
created

MiniCrud   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 285
Duplicated Lines 15.44 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 67.82%

Importance

Changes 15
Bugs 2 Features 0
Metric Value
wmc 52
c 15
b 2
f 0
lcom 1
cbo 9
dl 44
loc 285
ccs 118
cts 174
cp 0.6782
rs 7.9487

9 Methods

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