FakeRecordGeneratorTask   F
last analyzed

Complexity

Total Complexity 60

Size/Duplication

Total Lines 240
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 162
c 1
b 0
f 0
dl 0
loc 240
rs 3.6
wmc 60

4 Methods

Rating   Name   Duplication   Size   Complexity  
F run() 0 130 25
A isEnabled() 0 3 1
D getRandomValueFromType() 0 53 30
A createMembersFromApi() 0 36 4

How to fix   Complexity   

Complex Class

Complex classes like FakeRecordGeneratorTask 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.

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

1
<?php
2
3
namespace LeKoala\DevToolkit\Tasks;
4
5
use \Exception;
0 ignored issues
show
Bug introduced by
The type \Exception was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use LeKoala\DevToolkit\BuildTaskTools;
7
use SilverStripe\Assets\Image;
8
use SilverStripe\Dev\BuildTask;
9
use SilverStripe\Security\Member;
10
use SilverStripe\Control\Director;
11
use SilverStripe\ORM\FieldType\DBInt;
12
use SilverStripe\ORM\FieldType\DBDate;
13
use SilverStripe\ORM\FieldType\DBEnum;
14
use SilverStripe\ORM\FieldType\DBText;
15
use LeKoala\DevToolkit\FakeDataProvider;
16
use SilverStripe\ORM\FieldType\DBBoolean;
17
use SilverStripe\ORM\FieldType\DBVarchar;
18
use SilverStripe\ORM\FieldType\DBCurrency;
19
use SilverStripe\ORM\FieldType\DBHTMLText;
20
21
class FakeRecordGeneratorTask extends BuildTask
22
{
23
    use BuildTaskTools;
24
25
    protected $title = "Fake Record Generator";
26
    protected $description = 'Generate fake records for a given class';
27
    private static $segment = 'FakeRecordGeneratorTask';
28
29
    public function run($request)
30
    {
31
        $this->request = $request;
32
33
        $list = $this->getValidDataObjects();
34
        $this->addOption("model", "Which model to generate", null, $list);
35
        $this->addOption("how_many", "How many records to generate", 20);
36
        $this->addOption("member_from_api", "Use https://randomuser.me to generate members", true);
37
        $this->addOption("clear_existing", "Clear existing data", false);
38
39
        $options = $this->askOptions();
40
41
        $model = $options['model'];
42
        $how_many = $options['how_many'];
43
        $member_from_api = $options['member_from_api'];
44
        $clear_existing = $options['clear_existing'];
45
46
        if ($model) {
47
            $sing = singleton($model);
0 ignored issues
show
Unused Code introduced by
The assignment to $sing is dead and can be removed.
Loading history...
48
49
            if ($clear_existing) {
50
                $this->message("Clearing existing data", "warning");
51
                $cleared = 0;
52
                foreach ($model::get() as $rec) {
53
                    $rec->delete();
54
                    $cleared++;
55
                }
56
                $this->message("Cleared $cleared records");
57
            }
58
59
            if ($model == Member::class && $member_from_api) {
60
                $this->createMembersFromApi($how_many);
61
            } else {
62
                for ($i = 0; $i < $how_many; $i++) {
63
                    $this->message("Generating record $i");
64
65
                    try {
66
                        $rec = $model::create();
67
68
                        $fields = $rec->getCMSFields();
69
70
                        // Fill according to type
71
                        $db = $model::config()->db;
72
                        $owns = $model::config()->owns;
73
                        $has_one = $model::config()->has_one;
74
                        $has_many = $model::config()->has_many;
0 ignored issues
show
Unused Code introduced by
The assignment to $has_many is dead and can be removed.
Loading history...
75
                        $many_many = $model::config()->many_many;
76
77
                        foreach ($db as $name => $type) {
78
                            $rec->$name = $this->getRandomValueFromType($type, $name, $rec);
79
80
                            $field = $fields->dataFieldByName($name);
81
                            if (!$field) {
82
                                continue;
83
                            }
84
85
                            // For dropdown fields and the likes, we might use the getSource thing
86
                            if ($field->hasMethod('getSource')) {
87
                                $source = $field->getSource();
88
                                if (is_array($source)) {
89
                                    $source = array_keys($source);
90
                                    $value = $source[array_rand($source)];
91
92
                                    // Use save into to ensure consistency
93
                                    $field->setValue($value);
94
                                    $field->saveInto($rec);
95
                                }
96
                            }
97
                        }
98
99
                        $hasFillFake = $rec->hasMethod('fillFake');
100
101
                        // Only populate relations for record without fillFake
102
                        if (!$hasFillFake) {
103
                            foreach ($has_one as $name => $class) {
104
                                $rel = null;
105
                                $nameID = $name . 'ID';
106
                                $isOwned = isset($owns[$name]) ? true : false;
107
108
                                if ($isOwned) {
109
                                    if ($class == Image::class) {
110
                                        $rel = FakeDataProvider::ownImage($rec, $name);
111
                                    }
112
                                } else {
113
                                    if ($class == Image::class) {
114
                                        $rel = FakeDataProvider::image();
115
                                    } else {
116
                                        $rel = FakeDataProvider::record($class);
117
                                    }
118
                                }
119
120
                                if ($rel) {
121
                                    $rec->$nameID = $rel->ID;
122
                                }
123
                            }
124
                            foreach ($many_many as $name => $class) {
125
                                if (is_array($class)) {
126
                                    continue;
127
                                }
128
129
                                $rel = null;
130
                                if ($isOwned) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $isOwned does not seem to be defined for all execution paths leading up to this point.
Loading history...
131
                                    if ($class == Image::class) {
132
                                        $rel = FakeDataProvider::ownImage($rec);
133
                                    }
134
                                } else {
135
                                    $rel = FakeDataProvider::record($class);
136
                                }
137
                                if ($rel) {
138
                                    $rec->$name()->add($rel->ID);
139
                                }
140
                            }
141
                        }
142
143
                        $id = $rec->write();
0 ignored issues
show
Unused Code introduced by
The assignment to $id is dead and can be removed.
Loading history...
144
145
                        if ($hasFillFake) {
146
                            $rec->fillFake();
147
                        }
148
149
                        $id = $rec->write();
150
151
                        $this->message("New record with id $id", "created");
152
                    } catch (Exception $ex) {
153
                        $this->message($ex->getMessage(), "error");
154
                    }
155
                }
156
            }
157
        } else {
158
            $this->message("Implement 'fillFake' method to create your own fakes");
159
        }
160
    }
161
162
    protected function getRandomValueFromType($type, $name, $record)
163
    {
164
        $type = explode('(', $type);
165
        switch ($type[0]) {
166
            case 'Varchar':
167
            case DBVarchar::class:
168
                $length = 50;
0 ignored issues
show
Unused Code introduced by
The assignment to $length is dead and can be removed.
Loading history...
169
                if (count($type) > 1) {
170
                    $length = (int) $type[1];
171
                }
172
                if ($name == 'CountryCode' || $name == 'Nationality') {
173
                    return FakeDataProvider::countryCode();
174
                } elseif ($name == 'PostalCode' || $name == 'Postcode') {
175
                    $addr = FakeDataProvider::address();
176
                    return $addr['Postcode'];
177
                } elseif ($name == 'Locality' || $name == 'City') {
178
                    $addr = FakeDataProvider::address();
179
                    return $addr['City'];
180
                } elseif ($name == 'URLSegment' || $name == 'Slug') {
181
                    return null; // let autogeneration happen
182
                }
183
                return FakeDataProvider::words(3, 7);
184
            case 'Date':
185
            case 'DateTime':
186
            case DBDate::class:
187
                return FakeDataProvider::date(strtotime('-1 year'), strtotime('+1 year'));
188
            case 'Boolean':
189
            case DBBoolean::class:
190
                return FakeDataProvider::boolean();
191
            case 'Enum':
192
            case 'NiceEnum':
193
            case DBEnum::class:
194
                /* @var $enum Enum */
195
                $enum = $record->dbObject($name);
196
                return FakeDataProvider::pick(array_values($enum->enumValues()));
197
            case 'Int':
198
            case DBInt::class:
199
                return rand(1, 10);
200
            case 'Currency':
201
            case DBCurrency::class:
202
                return FakeDataProvider::fprand(20, 100, 2);
203
            case 'HTMLText':
204
            case DBHTMLText::class:
205
                return FakeDataProvider::paragraphs(3, 7);
206
            case 'Text':
207
            case DBText::class:
208
                return FakeDataProvider::sentences(3, 7);
209
            default:
210
                $dbObject = $record->dbObject($name);
211
                if ($dbObject && $dbObject->hasMethod('fillFake')) {
212
                    return $dbObject->fillFake();
213
                }
214
                return null;
215
        }
216
    }
217
218
    protected function createMembersFromApi($how_many)
219
    {
220
        $data = FakeDataProvider::randomUser(['result' => $how_many]);
221
        foreach ($data as $res) {
222
            try {
223
                $rec = Member::create();
224
                $rec->Gender = $res['gender'];
225
                $rec->FirstName = ucwords($res['name']['first']);
226
                $rec->Surname = ucwords($res['name']['last']);
227
                $rec->Salutation = ucwords($res['name']['title']);
228
                $rec->Address = $res['location']['street'];
229
                $rec->Locality = $res['location']['city'];
230
                $rec->PostalCode = $res['location']['postcode'];
231
                $rec->BirthDate = $res['dob'];
232
                $rec->Created = $res['registered'];
233
                $rec->Phone = $res['phone'];
234
                $rec->Cell = $res['cell'];
235
                $rec->Nationality = $res['nat'];
236
                $rec->Email = $res['email'];
237
238
                $image_data = file_get_contents($res['picture']['large']);
239
                $image = FakeDataProvider::storeFakeImage($image_data, basename($res['picture']['large']), 'Avatars');
240
                $rec->AvatarID = $image->ID;
241
242
                $id = $rec->write();
0 ignored issues
show
Unused Code introduced by
The assignment to $id is dead and can be removed.
Loading history...
243
244
                $rec->changePassword($res['login']['password']);
245
246
                if ($rec->hasMethod('fillFake')) {
247
                    $rec->fillFake();
248
                }
249
                $id = $rec->write();
250
251
                $this->message("New record with id $id", "created");
252
            } catch (Exception $ex) {
253
                $this->message($ex->getMessage(), "error");
254
            }
255
        }
256
    }
257
258
    public function isEnabled()
259
    {
260
        return Director::isDev();
261
    }
262
}
263