Columns   F
last analyzed

Complexity

Total Complexity 61

Size/Duplication

Total Lines 284
Duplicated Lines 0.7 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 61
lcom 1
cbo 3
dl 2
loc 284
rs 3.52
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
B readColumnsFromTable() 2 62 8
F getVisibleColumns() 0 176 46
B setBelongsToRelations() 0 36 7

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 Columns 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 Columns, and based on these observations, apply Extract Interface, too.

1
<?php namespace Savannabits\JetstreamInertiaGenerator\Generators\Traits;
2
3
use Doctrine\DBAL\DriverManager;
4
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
5
use Illuminate\Database\Capsule\Manager;
6
use Illuminate\Database\DatabaseManager;
7
use Illuminate\Support\Collection;
8
use Illuminate\Support\Facades\Schema;
9
use Illuminate\Support\Str;
10
trait Columns {
11
12
    /**
13
     * @param $tableName
14
     * @return Collection
15
     */
16
    protected function readColumnsFromTable($tableName): Collection
17
    {
18
19
        // TODO how to process jsonb & json translatable columns? need to figure it out
20
21
        $indexes = collect(Schema::getConnection()->getDoctrineSchemaManager()->listTableIndexes($tableName));
0 ignored issues
show
Bug introduced by
The method getConnection() does not exist on Illuminate\Support\Facades\Schema. Did you maybe mean connection()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
22
        return collect(Schema::getColumnListing($tableName))->map(function($columnName) use ($tableName, $indexes) {
23
24
            //Checked unique index
25
            $columnUniqueIndexes = $indexes->filter(function($index) use ($columnName) {
26
                return in_array($columnName, $index->getColumns()) && ($index->isUnique() && !$index->isPrimary());
27
            });
28
            $columnPrimaryIndex = $indexes->filter(function($index) use ($columnName) {
29
                return in_array($columnName,$index->getColumns()) && $index->isPrimary();
30
            });
31
            $columnUniqueDeleteAtCondition = $columnUniqueIndexes->filter(function($index) {
32
                return $index->hasOption('where') && $index->getOption('where') == '(deleted_at IS NULL)';
33
            });
34
35
            // TODO add foreign key
36
37
            return [
38
                'name' => $columnName,
39
                'primary' => $columnPrimaryIndex->count() > 0,
40
                'label' => Str::title(str_replace("-"," ",Str::slug($columnName))),
41
                'type' => Schema::getColumnType($tableName, $columnName),
42
                'required' => boolval(Schema::getConnection()->getDoctrineColumn($tableName, $columnName)->getNotnull()),
0 ignored issues
show
Bug introduced by
The method getConnection() does not exist on Illuminate\Support\Facades\Schema. Did you maybe mean connection()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
43
                'unique' => $columnUniqueIndexes->count() > 0,
44
                'unique_indexes' => $columnUniqueIndexes,
45
                'unique_deleted_at_condition' => $columnUniqueDeleteAtCondition->count() > 0,
46
            ];
47
        })
48
            /*->sortByDesc(function ($item) {return $item['type']==='json';})
49
            ->sortByDesc(function ($item) {return $item['type']==='longText';})
50
            ->sortByDesc(function ($item) {return $item['type']==='time';})
51
            ->sortByDesc(function ($item) {return $item['type']==='tinyinteger';})
52
            ->sortByDesc(function ($item) {return $item['type']==='bigInteger';})
53
            ->sortByDesc(function ($item) {return $item['type']==='mediumInteger';})
54
            ->sortByDesc(function ($item) {return $item['type']==='integer';})
55
            ->sortByDesc(function ($item) {return $item['type']==='float';})
56
            ->sortByDesc(function ($item) {return $item['type']==='double';})
57
            ->sortByDesc(function ($item) {return $item['type']==='text';})
58
            ->sortByDesc(function ($item) {return $item['name']==='amount';})*/
59
            ->sortByDesc(function ($item) {return $item['type']==='boolean';})
60
            ->sortByDesc(function ($item) {return $item['type']==='datetime'&& !in_array($item['name'],['created_at','updated_at']);})
61
            ->sortByDesc(function ($item) {return $item['type']==='date';})
62
            ->sortByDesc(function ($item) {return $item['type']==='float';})
63
//            ->sortByDesc(function ($item) {return $item['type']==='double';})
64 View Code Duplication
            ->sortByDesc(function ($item) {return $item['type']==='text' && !in_array($item['name'],["description",'details','content','body','text']);})
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...
65
            ->sortByDesc(function ($item) {return $item['type']==='string' && !in_array($item['name'],["description",'details','content','body','text','middle_name','other_names','first_name','last_name','surname','title','headline','name','display_name','slug']);})
66 View Code Duplication
            ->sortByDesc(function ($item) {return in_array($item['name'],["description",'details','content','body','text']);})
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...
67
            ->sortByDesc(function ($item) {return in_array($item['name'],['middle_name','other_names']);})
68
            ->sortByDesc(function ($item) {return in_array($item['name'],['first_name','last_name','surname']);})
69
            ->sortByDesc(function ($item) {return in_array($item['name'],['headline']);})
70
            ->sortByDesc(function ($item) {return in_array($item['name'],['display_name']);})
71
            ->sortByDesc(function ($item) {return in_array($item['name'],['title']);})
72
            ->sortByDesc(function ($item) {return in_array($item['name'],['name']);})
73
            ->sortByDesc(function ($item) {return in_array($item['name'],['slug']);})
74
            ->sortByDesc(function ($item) {return in_array($item['name'],['id']);})
75
            ->values()
76
            ;
77
    }
78
79
    protected function getVisibleColumns($tableName, $modelVariableName): Collection
80
    {
81
        $relationships = $this->setBelongsToRelations();
82
        $columns = $this->readColumnsFromTable($tableName);
83
        $hasSoftDelete = ($columns->filter(function($column) {
84
                return $column['name'] == "deleted_at";
85
            })->count() > 0);
86
        $filtered = $columns->filter(function($column) {
87
            return !($column['name'] == "id" || $column['name'] == "created_at" || $column['name'] == "updated_at" || $column['name'] == "deleted_at" || $column['name'] == "remember_token");
88
        });
89
        return $filtered->map(function($column) use ($tableName, $hasSoftDelete, $modelVariableName,$filtered,$relationships){
90
            $serverStoreRules = collect([]);
91
            $serverUpdateRules = collect([]);
92
            $frontendRules = collect([]);
93
            if ($column['required']) {
94
                $serverStoreRules->push('\'required\'');
95
                $serverUpdateRules->push('\'sometimes\'');
96
                if($column['type'] != 'boolean' && $column['name'] != 'password') {
97
                    $frontendRules->push('required');
98
                }
99
            } else {
100
                $serverStoreRules->push('\'nullable\'');
101
                $serverUpdateRules->push('\'nullable\'');
102
            }
103
104
            if ($column['name'] == 'email') {
105
                $serverStoreRules->push('\'email\'');
106
                $serverUpdateRules->push('\'email\'');
107
                $frontendRules->push('email');
108
            }
109
110
            if ($column['name'] == 'password') {
111
                $serverStoreRules->push('\'confirmed\'');
112
                $serverUpdateRules->push('\'confirmed\'');
113
                $frontendRules->push('confirmed:password');
114
115
                $serverStoreRules->push('\'min:7\'');
116
                $serverUpdateRules->push('\'min:7\'');
117
                $frontendRules->push('min:7');
118
119
                $serverStoreRules->push('\'regex:/^.*(?=.{3,})(?=.*[a-zA-Z])(?=.*[0-9]).*$/\'');
120
                $serverUpdateRules->push('\'regex:/^.*(?=.{3,})(?=.*[a-zA-Z])(?=.*[0-9]).*$/\'');
121
                //TODO not working, need fixing
122
//                $frontendRules->push(''regex:/^.*(?=.{3,})(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!$#%]).*$/g'');
123
            }
124
125
            if($column['unique'] || $column['name'] == 'slug') {
126
                if($column['type'] == 'json') {
127
                    if (count($column['unique_indexes']) == 1) {
128
                        $storeRule = 'Rule::unique(\''.$tableName.'\', \''.$column['name'].'->\'.$locale)';
129
                        $updateRule = 'Rule::unique(\''.$tableName.'\', \''.$column['name'].'->\'.$locale)->ignore($this->'.$modelVariableName.'->getKey(), $this->'.$modelVariableName.'->getKeyName())';
130
                        if($hasSoftDelete && $column['unique_deleted_at_condition']) {
131
                            $storeRule .= '->whereNull(\'deleted_at\')';
132
                            $updateRule .= '->whereNull(\'deleted_at\')';
133
                        }
134
                        $cols = $column['unique_indexes'][0]->getColumns();
135
                        if (count($cols) > 1) {
136
                            $otherCols = collect($cols)->reject(function ($col) use ($column) { return $col ===$column['name'];});
137
                            foreach ($otherCols as $otherCol) {
138
                                $storeRule .= '->where('.$otherCol.',$this->'.$otherCol.')';
139
                                $updateRule .= '->where('.$otherCol.',$this->'.$otherCol.')';
140
                            }
141
                        }
142
                        $serverStoreRules->push($storeRule);
143
                        $serverUpdateRules->push($updateRule);
144
                    }
145
                } else {
146
                    if (count($column['unique_indexes']) == 1) {
147
                        $storeRule = 'Rule::unique(\''.$tableName.'\', \''.$column['name'].'\')';
148
                        $updateRule = 'Rule::unique(\''.$tableName.'\', \''.$column['name'].'\')';
149
                        if($hasSoftDelete && $column['unique_deleted_at_condition']) {
150
                            $storeRule .= '->whereNull(\'deleted_at\')';
151
                            $updateRule .= '->whereNull(\'deleted_at\')';
152
                        }
153
                        $cols = $column['unique_indexes']->first()->getColumns();
154
                        if (count($cols) > 1) {
155
                            $otherCols = collect($cols)->reject(function ($col) use ($column) { return $col ===$column['name'];});
156
                            foreach ($otherCols as $otherCol) {
157
                                if ($filtered->keyBy("name")->has($otherCol)) {
158
                                    $otherRel = $otherCol;
159
                                    $otherKey = '';
160
                                } elseif ($relationships->keyBy("name")->has($otherCol)) {
161
                                    $otherRel = $relationships->get($otherCol)['relationship_variable'];
162
                                    $ownerKey = $relationships->get($otherCol)["owner_key"];
163
                                    $otherKey = $ownerKey;
164
                                } else {
165
                                    $otherRel = null;
166
                                    $otherKey = null;
167
                                }
168
                                if ($otherRel) {
169
                                    if ($otherKey) {
170
                                        $where = 'collect($this->'.$otherRel.')->get(\''.$otherKey.'\')';
171
                                    } else {
172
                                        $where = $otherRel;
173
                                    }
174
                                    $storeRule .= '->where("'.$otherCol.'", $this->'.$where.')';
175
                                    $updateRule .= '->where("'.$otherCol.'",$this->'.$where.')';
176
177
                                }
178
                            }
179
                        }
180
                        $updateRule .= '->ignore($this->'.$modelVariableName.'->getKey(), $this->'.$modelVariableName.'->getKeyName())';
181
                        $serverStoreRules->push($storeRule);
182
                        $serverUpdateRules->push($updateRule);
183
                    }
184
                }
185
            }
186
187
            switch ($column['type']) {
188
                case 'datetime':
189
                    $serverStoreRules->push('\'date\'');
190
                    $serverUpdateRules->push('\'date\'');
191
                    $frontendRules->push('date_format:yyyy-MM-dd HH:mm:ss');
192
                    break;
193
                case 'date':
194
                    $serverStoreRules->push('\'date\'');
195
                    $serverUpdateRules->push('\'date\'');
196
                    $frontendRules->push('date_format:yyyy-MM-dd');
197
                    break;
198
                case 'time':
199
                    $serverStoreRules->push('\'date_format:H:i:s\'');
200
                    $serverUpdateRules->push('\'date_format:H:i:s\'');
201
                    $frontendRules->push('date_format:HH:mm:ss');
202
                    break;
203
204
                case 'tinyInteger':
205
                case 'smallInteger':
206
                case 'mediumInteger':
207
                case 'bigInteger':
208
                case 'bigint':
209
                case 'unsignedInteger':
210
                case 'unsignedTinyInteger':
211
                case 'unsignedSmallInteger':
212
                case 'unsignedMediumInteger':
213
                case 'unsignedBigInteger':
214
                case 'integer':
215
                    $serverStoreRules->push('\'integer\'');
216
                    $serverUpdateRules->push('\'integer\'');
217
                    $frontendRules->push('integer');
218
                    break;
219
220
                case 'boolean':
221
                    $serverStoreRules->push('\'boolean\'');
222
                    $serverUpdateRules->push('\'boolean\'');
223
                    $frontendRules->push('');
224
                    break;
225
                case 'float':
226
                    $serverStoreRules->push('\'numeric\'');
227
                    $serverUpdateRules->push('\'numeric\'');
228
                    $frontendRules->push('decimal');
229
                    break;
230
                case 'decimal':
231
                    $serverStoreRules->push('\'numeric\'');
232
                    $serverUpdateRules->push('\'numeric\'');
233
                    $frontendRules->push('decimal'); // FIXME?? I'm not sure about this one
234
                    break;
235
                case 'text':
236
                case 'string':
237
                    $serverStoreRules->push('\'string\'');
238
                    $serverUpdateRules->push('\'string\'');
239
                    break;
240
                default:
241
                    $serverStoreRules->push('\'string\'');
242
                    $serverUpdateRules->push('\'string\'');
243
            }
244
245
            return [
246
                'name' => $column['name'],
247
                'type' => $column['type'],
248
                'label' => Str::title(str_replace("-"," ",Str::slug($column["name"]))),
249
                'serverStoreRules' => $serverStoreRules->toArray(),
250
                'serverUpdateRules' => $serverUpdateRules->toArray(),
251
                'frontendRules' => $frontendRules->toArray(),
252
            ];
253
        });
254
    }
255
256
    protected function setBelongsToRelations(): Collection
257
    {
258
        $relationships = collect(app('db')->connection()->getDoctrineSchemaManager()->listTableForeignKeys($this->tableName))->map(function($fk) {
0 ignored issues
show
Bug introduced by
The property tableName does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
259
            /**@var ForeignKeyConstraint $fk*/
260
            $columns = $this->readColumnsFromTable($fk->getForeignTableName())->filter(function($column) {
261
                return in_array($column["name"],["name", "display_name","title"]) || in_array($column["type"],["string"]);
262
            })->pluck("name");
263
            $labelColumn = $columns->filter(function($column){return in_array($column, [
264
                    'name','display_name', 'title'
265
                ]);
266
            })->first();
267
            if (!$labelColumn) $labelColumn = $columns->filter(function($column){return $column==='title';})->first();
268
            if (!$labelColumn) $labelColumn = $columns->filter(function($column){return $column==='name';})->first();
269
            if (!$labelColumn) $labelColumn = $columns->first();
270
            if (!$labelColumn) $labelColumn = "id";
271
            $functionName = collect($fk->getColumns())->first();
272
            if (str_contains($functionName,"_id")) {$functionName = Str::singular(str_replace("_id","",$functionName));} else {
273
                $functionName =Str::singular($functionName)."_model";
274
            }
275
            $functionName = Str::camel($functionName);
276
            $relatedTitle = Str::title(str_replace("_"," ",Str::snake(Str::studly($functionName))));
277
            return [
278
                "function_name" => $functionName,
279
                "related_table" => $fk->getForeignTableName(),
280
                "related_route_name" => Str::slug(Str::pluralStudly($fk->getForeignTableName())),
281
                "related_model" => "\\$this->modelNamespace\\". Str::studly(Str::singular($fk->getForeignTableName())).'::class',
0 ignored issues
show
Bug introduced by
The property modelNamespace does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
282
                "related_model_title" => $relatedTitle,
283
                "label_column" => $labelColumn,
284
                "relationship_variable" => Str::snake($functionName),
285
                "foreign_key" => collect($fk->getColumns())->first(),
286
                "owner_key" => collect($fk->getForeignColumns())->first(),
287
            ];
288
        })->keyBy('foreign_key');
289
        $this->relations["belongsTo"] = $relationships;
0 ignored issues
show
Bug introduced by
The property relations does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
290
        return $relationships;
291
    }
292
293
}
294