• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

RonasIT / laravel-entity-generator / 12026977149

26 Nov 2024 08:49AM UTC coverage: 39.655% (+0.1%) from 39.537%
12026977149

Pull #108

github

web-flow
Merge ddd257d48 into 8497bb4ef
Pull Request #108: feat: skip generation for invalid stubs

33 of 75 new or added lines in 15 files covered. (44.0%)

23 existing lines in 2 files now uncovered.

391 of 986 relevant lines covered (39.66%)

1.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Generators/FactoryGenerator.php
1
<?php
2

3
namespace RonasIT\Support\Generators;
4

5
use Faker\Generator as Faker;
6
use Illuminate\Support\Arr;
7
use Illuminate\Support\Str;
8
use RonasIT\Support\Exceptions\ModelFactoryNotFound;
9
use RonasIT\Support\Exceptions\ClassNotExistsException;
10
use RonasIT\Support\Exceptions\ModelFactoryNotFoundedException;
11
use RonasIT\Support\Exceptions\ClassAlreadyExistsException;
12
use RonasIT\Support\Events\SuccessCreateMessage;
13
use Exception;
14

15
class FactoryGenerator extends EntityGenerator
16
{
17
    const array FAKERS_METHODS = [
18
        'integer' => 'randomNumber()',
19
        'boolean' => 'boolean',
20
        'string' => 'word',
21
        'float' => 'randomFloat(2, 0, 10000)',
22
        'timestamp' => 'dateTime',
23
    ];
24

25
    const array CUSTOM_METHODS = [
26
        'json' => '[]',
27
    ];
28

29
    protected function generateSeparateClass(): string
30
    {
31
        if (!$this->classExists('models', $this->model)) {
×
32
            $this->throwFailureException(
×
33
                ClassNotExistsException::class,
×
34
                "Cannot create {$this->model}Factory cause {$this->model} Model does not exists.",
×
35
                "Create a {$this->model} Model by himself or run command 'php artisan make:entity {$this->model} --only-model'."
×
36
            );
×
37
        }
38

39
        if ($this->classExists('factory', "{$this->model}Factory")) {
×
40
            $this->throwFailureException(
×
41
                ClassAlreadyExistsException::class,
×
42
                "Cannot create {$this->model}Factory cause {$this->model}Factory already exists.",
×
43
                "Remove {$this->model}Factory."
×
44
            );
×
45
        }
46

47
        $factoryContent = $this->getStub('factory', [
×
48
            'namespace' => $this->getOrCreateNamespace('factory'),
×
49
            'entity' => $this->model,
×
50
            'fields' => $this->prepareFields()
×
51
        ]);
×
52

53
        $this->saveClass('factory', "{$this->model}Factory", $factoryContent);
×
54

55
        return "Created a new Factory: {$this->model}Factory";
×
56
    }
57

58
    protected function generateToGenericClass(): string
59
    {
NEW
60
        $stubPath = config("entity-generator.stubs.legacy_factory");
×
61

NEW
62
        $content = view($stubPath)->with([
×
NEW
63
            'entity' => $this->model,
×
NEW
64
            'fields' => $this->prepareFields(),
×
NEW
65
            'modelsNamespace' => $this->getOrCreateNamespace('models')
×
NEW
66
        ])->render();
×
67

NEW
68
        $content = "\n\n" . $content;
×
69

NEW
70
        file_put_contents($this->paths['factory'], $content, FILE_APPEND);
×
71

NEW
72
        $this->prepareRelatedFactories();
×
73

NEW
74
        return "Created a new Test factory for {$this->model} model in '{$this->paths['factory']}'";
×
75
    }
76

77
    public function generate(): void
78
    {
NEW
79
        $isActualVersion = (version_compare(app()->version(), '8', '>='));
×
80

NEW
81
        if ($isActualVersion && $this->checkStubExists('factory')) {
×
NEW
82
            event(new SuccessCreateMessage($this->generateSeparateClass()));
×
NEW
83
        } else if (!$isActualVersion) {
×
NEW
84
            if (!file_exists($this->paths['factory']) && $this->checkStubExists('legacy_empty_factory')) {
×
NEW
85
                $this->prepareEmptyFactory();
×
86
            }
87

NEW
88
            if (!$this->checkExistModelFactory() && $this->checkExistRelatedModelsFactories()) {
×
NEW
89
                if (!$this->checkStubExists('legacy_factory')) {
×
NEW
90
                    return;
×
91
                }
92

NEW
93
                $createMessage = $this->generateToGenericClass();
×
94
            } else {
NEW
95
                $createMessage = "Factory for {$this->model} model has already created, so new factory not necessary create.";
×
96
            }
97

NEW
98
            event(new SuccessCreateMessage($createMessage));
×
99
        }
100
    }
101

102
    protected function prepareEmptyFactory(): void
103
    {
104
        $stubPath = config('entity-generator.stubs.legacy_empty_factory');
×
105
        $content = "<?php \n\n" . view($stubPath, [
×
106
            'modelsNamespace' => $this->getOrCreateNamespace('models')
×
107
        ])->render();
×
108

109
        list($basePath, $databaseFactoryDir) = extract_last_part(config('entity-generator.paths.factory'), '/');
×
110

111
        if (!is_dir($databaseFactoryDir)) {
×
112
            mkdir($databaseFactoryDir);
×
113
        }
114

115
        file_put_contents($this->paths['factory'], $content);
×
116
    }
117

118
    protected function checkExistRelatedModelsFactories(): bool
119
    {
120
        $modelFactoryContent = file_get_contents($this->paths['factory']);
×
121
        $relatedModels = $this->getRelatedModels($this->model);
×
122
        $modelNamespace = $this->getOrCreateNamespace('models');
×
123

124
        foreach ($relatedModels as $relatedModel) {
×
125
            $relatedFactoryClass = "{$modelNamespace}\\$relatedModel::class";
×
126
            $existModelFactory = strpos($modelFactoryContent, $relatedFactoryClass);
×
127

128
            if (!$existModelFactory) {
×
129
                $this->throwFailureException(
×
130
                    ModelFactoryNotFoundedException::class,
×
131
                    "Not found $relatedModel factory for $relatedModel model in '{$this->paths['factory']}",
×
132
                    "Please declare a factory for $relatedModel model on '{$this->paths['factory']}' path and run your command with option '--only-tests'."
×
133
                );
×
134
            }
135
        }
136

137
        return true;
×
138
    }
139

140
    protected static function getFakerMethod($field): string
141
    {
142
        if (Arr::has(self::FAKERS_METHODS, $field['type'])) {
×
143
            return "\$faker->" . self::FAKERS_METHODS[$field['type']];
×
144
        }
145

146
        return self::getCustomMethod($field);
×
147
    }
148

149
    protected static function getCustomMethod($field): string
150
    {
151
        if (Arr::has(self::CUSTOM_METHODS, $field['type'])) {
×
152
            return self::CUSTOM_METHODS[$field['type']];
×
153
        }
154

155
        $message = $field['type'] . 'not found in CUSTOM_METHODS variable CUSTOM_METHODS = ' . self::CUSTOM_METHODS;
×
156
        throw new Exception($message);
×
157
    }
158

159
    protected function prepareRelatedFactories(): void
160
    {
161
        $relations = array_merge(
×
162
            $this->relations['hasOne'],
×
163
            $this->relations['hasMany']
×
164
        );
×
165

166
        foreach ($relations as $relation) {
×
167
            $modelFactoryContent = file_get_contents($this->paths['factory']);
×
168

169
            if (!Str::contains($modelFactoryContent, $this->getModelClass($relation))) {
×
170
                $this->throwFailureException(
×
171
                    ModelFactoryNotFound::class,
×
172
                    "Model factory for mode {$relation} not found.",
×
173
                    "Please create it and after thar you can run this command with flag '--only-tests'."
×
174
                );
×
175
            }
176

177
            $matches = [];
×
178

179
            preg_match($this->getFactoryPattern($relation), $modelFactoryContent, $matches);
×
180

181
            foreach ($matches as $match) {
×
182
                $field = Str::snake($this->model) . '_id';
×
183

184
                $newField = "\n        \"{$field}\" => 1,";
×
185

186
                $modelFactoryContent = str_replace($match, $match . $newField, $modelFactoryContent);
×
187
            }
188

189
            file_put_contents($this->paths['factory'], $modelFactoryContent);
×
190
        }
191
    }
192

193
    public static function getFactoryFieldsContent($field): string
194
    {
195
        /** @var Faker $faker */
196
        $faker = app(Faker::class);
×
197

198
        if (preg_match('/_id$/', $field['name']) || ($field['name'] == 'id')) {
×
199
            return 1;
×
200
        }
201

202
        if (property_exists($faker, $field['name'])) {
×
203
            return "\$faker-\>{$field['name']}";
×
204
        }
205

206
        if (method_exists($faker, $field['name'])) {
×
207
            return "\$faker-\>{$field['name']}()";
×
208
        }
209

210
        return self::getFakerMethod($field);
×
211
    }
212

213
    protected function checkExistModelFactory(): int
214
    {
215
        $modelFactoryContent = file_get_contents($this->paths['factory']);
×
216
        $modelNamespace = $this->getOrCreateNamespace('models');
×
217
        $factoryClass = "{$modelNamespace}\\$this->model::class";
×
218

219
        return strpos($modelFactoryContent, $factoryClass);
×
220
    }
221

222
    protected function prepareFields(): array
223
    {
224
        $result = [];
×
225

226
        foreach ($this->fields as $type => $fields) {
×
227
            foreach ($fields as $field) {
×
228
                $explodedType = explode('-', $type);
×
229

230
                $result[] = [
×
231
                    'name' => $field,
×
232
                    'type' => head($explodedType)
×
233
                ];
×
234
            }
235
        }
236

237
        return $result;
×
238
    }
239

240
    protected function getFactoryPattern($model): string
241
    {
242
        $modelNamespace = "App\\\\Models\\\\" . $model;
×
243
        $return = "return \\[";
×
244

245
        return "/{$modelNamespace}.*{$return}/sU";
×
246
    }
247

248
    protected function getModelClass($model): string
249
    {
250
        $modelNamespace = $this->getOrCreateNamespace('models');
×
251

252
        return "{$modelNamespace}\\{$model}";
×
253
    }
254

255
    protected function getRelatedModels($model)
256
    {
257
        $content = $this->getModelClassContent($model);
×
258

259
        preg_match_all('/(?<=belongsTo\().*(?=::class)/', $content, $matches);
×
260

261
        return head($matches);
×
262
    }
263

264
    protected function getModelClassContent($model): string
265
    {
266
        $path = base_path("{$this->paths['models']}/{$model}.php");
×
267

268
        if (!$this->classExists('models', $model)) {
×
269
            $this->throwFailureException(
×
270
                ClassNotExistsException::class,
×
271
                "Cannot create {$model} Model cause {$model} Model does not exists.",
×
272
                "Create a {$model} Model by himself or run command 'php artisan make:entity {$model} --only-model'."
×
273
            );
×
274
        }
275

276
        return file_get_contents($path);
×
277
    }
278
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc