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

RonasIT / laravel-entity-generator / 15085218998

17 May 2025 12:31PM UTC coverage: 99.351% (+0.007%) from 99.344%
15085218998

Pull #151

github

web-flow
Merge c607623da into 6e80bef88
Pull Request #151: 150 implement relations dto

15 of 15 new or added lines in 6 files covered. (100.0%)

4 existing lines in 1 file now uncovered.

918 of 924 relevant lines covered (99.35%)

5.69 hits per line

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

94.12
/src/Generators/EntityGenerator.php
1
<?php
2

3
namespace RonasIT\Support\Generators;
4

5
use Illuminate\Database\Eloquent\Relations\BelongsTo;
6
use Illuminate\Filesystem\Filesystem;
7
use Illuminate\Support\Arr;
8
use Illuminate\Support\Facades\DB;
9
use Illuminate\Support\Str;
10
use RonasIT\Support\DTO\RelationsDTO;
11
use RonasIT\Support\Events\WarningEvent;
12
use RonasIT\Support\Exceptions\ClassNotExistsException;
13
use RonasIT\Support\Exceptions\IncorrectClassPathException;
14
use Throwable;
15
use ReflectionMethod;
16
use ReflectionClass;
17

18
/**
19
 * @property Filesystem $fs
20
 */
21
abstract class EntityGenerator
22
{
23
    const AVAILABLE_FIELDS = [
24
        'integer', 'integer-required', 'string-required', 'string', 'float-required', 'float',
25
        'boolean-required', 'boolean', 'timestamp-required', 'timestamp', 'json'
26
    ];
27

28
    const LOVER_CASE_DIRECTORIES_MAP = [
29
        'migrations' => 'database/migrations',
30
        'factories' => 'database/factories',
31
        'seeders' => 'database/seeders',
32
        'database_seeder' => 'database/seeders',
33
        'tests' => 'tests',
34
        'routes' => 'routes',
35
    ];
36

37
    protected $paths = [];
38
    protected $model;
39
    protected $fields;
40
    protected $relations = [];
41
    protected $crudOptions;
42

43
    /**
44
     * @param array $crudOptions
45
     * @return $this
46
     */
47
    public function setCrudOptions($crudOptions)
48
    {
49
        $this->crudOptions = $crudOptions;
15✔
50

51
        return $this;
15✔
52
    }
53

54
    /**
55
     * @param string $model
56
     * @return $this
57
     */
58
    public function setModel($model)
59
    {
60
        $this->model = Str::studly($model);
65✔
61

62
        return $this;
65✔
63
    }
64

65
    /**
66
     * @param array $fields
67
     * @return $this
68
     */
69
    public function setFields($fields)
70
    {
71
        $this->fields = $fields;
22✔
72

73
        return $this;
22✔
74
    }
75

76
    /**
77
     * @param array $relations
78
     * @return $this
79
     */
80
    public function setRelations(RelationsDTO $relations)
81
    {
82
        $relations = $relations->toArray();
17✔
83

84
        $this->relations = $relations;
17✔
85

86
        foreach ($relations['belongsTo'] as $field) {
17✔
87
            $name = Str::snake($field) . '_id';
6✔
88

89
            $this->fields['integer-required'][] = $name;
6✔
90
        }
91

92
        return $this;
17✔
93
    }
94

95
    public function __construct()
96
    {
97
        $this->paths = config('entity-generator.paths');
68✔
98
    }
99

100
    protected function getOrCreateNamespace(string $configPath): string
101
    {
102
        $path = $this->paths[$configPath];
30✔
103
        $pathParts = explode('/', $path);
30✔
104

105
        if (Str::endsWith(Arr::last($pathParts), '.php')) {
30✔
UNCOV
106
            array_pop($pathParts);
×
107
        }
108

109
        foreach ($pathParts as $part) {
30✔
110
            if (!$this->isFolderHasCorrectCase($part, $configPath)) {
30✔
UNCOV
111
                throw new IncorrectClassPathException("Incorrect path to {$configPath}, {$part} folder must start with a capital letter, please specify the path according to the PSR.");
×
112
            }
113
        }
114

115
        $namespace = array_map(function (string $part) {
30✔
116
            return ucfirst($part);
30✔
117
        }, $pathParts);
30✔
118

119
        $fullPath = base_path($path);
30✔
120

121
        if (!file_exists($fullPath)) {
30✔
122
            mkdir($fullPath, 0777, true);
26✔
123
        }
124

125
        return implode('\\', $namespace);
30✔
126
    }
127

128
    protected function isFolderHasCorrectCase(string $folder, string $configPath): bool
129
    {
130
        $directory = Arr::get(self::LOVER_CASE_DIRECTORIES_MAP, $configPath);
30✔
131

132
        $firstFolderChar = substr($folder, 0, 1);
30✔
133

134
        return $folder === 'app' || (ucfirst($firstFolderChar) === $firstFolderChar) || Str::contains($directory, $folder);
30✔
135
    }
136

137
    abstract public function generate(): void;
138

139
    protected function classExists($path, $name): bool
140
    {
141
        $entitiesPath = $this->paths[$path];
29✔
142

143
        $classPath = base_path("{$entitiesPath}/{$name}.php");
29✔
144

145
        return file_exists($classPath);
29✔
146
    }
147

148
    protected function saveClass($path, $name, $content, $additionalEntityFolder = false): string
149
    {
150
        $entitiesPath = base_path($this->paths[$path]);
26✔
151

152
        if (Str::endsWith($entitiesPath, '.php')) {
26✔
153
            $pathParts = explode('/', $entitiesPath);
×
UNCOV
154
            array_pop($pathParts);
×
UNCOV
155
            $entitiesPath = implode('/', $pathParts);
×
156
        }
157

158
        if ($additionalEntityFolder) {
26✔
159
            $entitiesPath = $entitiesPath . "/{$additionalEntityFolder}";
6✔
160
        }
161

162
        $classPath = "{$entitiesPath}/{$name}.php";
26✔
163
        $tag = "<?php";
26✔
164

165
        if (!Str::contains($content, $tag)) {
26✔
166
            $content = "{$tag}\n\n{$content}";
26✔
167
        }
168

169
        if (!file_exists($entitiesPath)) {
26✔
170
            mkdir($entitiesPath, 0777, true);
8✔
171
        }
172

173
        return file_put_contents($classPath, $content);
26✔
174
    }
175

176
    protected function getStub($stub, $data = []): string
177
    {
178
        $stubPath = config("entity-generator.stubs.{$stub}");
30✔
179

180
        $data['options'] = $this->crudOptions;
30✔
181

182
        return view($stubPath)->with($data)->render();
30✔
183
    }
184

185
    protected function getTableName($entityName, $delimiter = '_'): string
186
    {
187
        $entityName = Str::snake($entityName, $delimiter);
13✔
188

189
        return $this->getPluralName($entityName);
13✔
190
    }
191

192
    protected function getPluralName($entityName): string
193
    {
194
        return Str::plural($entityName);
16✔
195
    }
196

197
    protected function throwFailureException($exceptionClass, $failureMessage, $recommendedMessage): void
198
    {
199
        throw new $exceptionClass("{$failureMessage} {$recommendedMessage}");
18✔
200
    }
201

202
    protected function getRelatedModels(string $model, string $creatableClass): array
203
    {
204
        $modelClass = $this->getModelClass($model);
9✔
205

206
        if (!class_exists($modelClass)) {
9✔
207
            $this->throwFailureException(
2✔
208
                exceptionClass: ClassNotExistsException::class,
2✔
209
                failureMessage: "Cannot create {$creatableClass} cause {$model} Model does not exists.",
2✔
210
                recommendedMessage: "Create a {$model} Model by himself or run command 'php artisan make:entity {$model} --only-model'.",
2✔
211
            );
2✔
212
        }
213

214
        $instance = new $modelClass();
7✔
215

216
        $publicMethods = (new ReflectionClass($modelClass))->getMethods(ReflectionMethod::IS_PUBLIC);
7✔
217

218
        $methods = array_filter($publicMethods, fn ($method) => $method->class === $modelClass && !$method->getParameters());
7✔
219

220
        $relatedModels = [];
7✔
221

222
        DB::beginTransaction();
7✔
223

224
        foreach ($methods as $method) {
7✔
225
            try {
226
                $result = call_user_func([$instance, $method->getName()]);
7✔
227

228
                if (!$result instanceof BelongsTo) {
7✔
229
                    continue;
7✔
230
                }
231
            } catch (Throwable) {
6✔
232
                continue;
6✔
233
            }
234

235
            $relatedModels[] = class_basename(get_class($result->getRelated()));
4✔
236
        }
237

238
        DB::rollBack();
7✔
239

240
        return $relatedModels;
7✔
241
    }
242

243
    protected function getModelClass(string $model): string
244
    {
245
        $modelNamespace = $this->getOrCreateNamespace('models');
12✔
246

247
        return "{$modelNamespace}\\{$model}";
12✔
248
    }
249

250
    protected function isStubExists(string $stubName, ?string $generationType = null): bool
251
    {
252
        $config = "entity-generator.stubs.{$stubName}";
52✔
253

254
        $stubPath = config($config);
52✔
255

256
        if (!view()->exists($stubPath)) {
52✔
257
            $generationType ??= Str::replace('_', ' ', $stubName);
22✔
258

259
            $message = "Generation of {$generationType} has been skipped cause the view {$stubPath} from the config {$config} is not exists. Please check that config has the correct view name value.";
22✔
260

261
            event(new WarningEvent($message));
22✔
262

263
            return false;
22✔
264
        }
265

266
        return true;
39✔
267
    }
268
}
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

© 2026 Coveralls, Inc