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

RonasIT / laravel-entity-generator / 14998084065

13 May 2025 01:36PM UTC coverage: 99.344% (+2.4%) from 96.94%
14998084065

Pull #144

github

web-flow
Merge eb31a5c6b into d191e02c5
Pull Request #144: tests:add tests for different package and project configs

909 of 915 relevant lines covered (99.34%)

5.57 hits per line

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

94.05
/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\Events\WarningEvent;
11
use RonasIT\Support\Exceptions\ClassNotExistsException;
12
use RonasIT\Support\Exceptions\IncorrectClassPathException;
13
use Throwable;
14
use ReflectionMethod;
15
use ReflectionClass;
16

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

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

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

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

50
        return $this;
15✔
51
    }
52

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

61
        return $this;
65✔
62
    }
63

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

72
        return $this;
22✔
73
    }
74

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

83
        foreach ($relations['belongsTo'] as $field) {
17✔
84
            $name = Str::snake($field) . '_id';
6✔
85

86
            $this->fields['integer-required'][] = $name;
6✔
87
        }
88

89
        return $this;
17✔
90
    }
91

92
    public function __construct()
93
    {
94
        $this->paths = config('entity-generator.paths');
68✔
95
    }
96

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

102
        if (Str::endsWith(Arr::last($pathParts), '.php')) {
30✔
103
            array_pop($pathParts);
×
104
        }
105

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

112
        $namespace = array_map(function (string $part) {
30✔
113
            return ucfirst($part);
30✔
114
        }, $pathParts);
30✔
115

116
        $fullPath = base_path($path);
30✔
117

118
        if (!file_exists($fullPath)) {
30✔
119
            mkdir($fullPath, 0777, true);
26✔
120
        }
121

122
        return implode('\\', $namespace);
30✔
123
    }
124

125
    protected function isFolderHasCorrectCase(string $folder, string $configPath): bool
126
    {
127
        $directory = Arr::get(self::LOVER_CASE_DIRECTORIES_MAP, $configPath);
30✔
128

129
        $firstFolderChar = substr($folder, 0, 1);
30✔
130

131
        return $folder === 'app' || (ucfirst($firstFolderChar) === $firstFolderChar) || Str::contains($directory, $folder);
30✔
132
    }
133

134
    abstract public function generate(): void;
135

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

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

142
        return file_exists($classPath);
29✔
143
    }
144

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

149
        if (Str::endsWith($entitiesPath, '.php')) {
26✔
150
            $pathParts = explode('/', $entitiesPath);
×
151
            array_pop($pathParts);
×
152
            $entitiesPath = implode('/', $pathParts);
×
153
        }
154

155
        if ($additionalEntityFolder) {
26✔
156
            $entitiesPath = $entitiesPath . "/{$additionalEntityFolder}";
6✔
157
        }
158

159
        $classPath = "{$entitiesPath}/{$name}.php";
26✔
160
        $tag = "<?php";
26✔
161

162
        if (!Str::contains($content, $tag)) {
26✔
163
            $content = "{$tag}\n\n{$content}";
26✔
164
        }
165

166
        if (!file_exists($entitiesPath)) {
26✔
167
            mkdir($entitiesPath, 0777, true);
8✔
168
        }
169

170
        return file_put_contents($classPath, $content);
26✔
171
    }
172

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

177
        $data['options'] = $this->crudOptions;
30✔
178

179
        return view($stubPath)->with($data)->render();
30✔
180
    }
181

182
    protected function getTableName($entityName, $delimiter = '_'): string
183
    {
184
        $entityName = Str::snake($entityName, $delimiter);
13✔
185

186
        return $this->getPluralName($entityName);
13✔
187
    }
188

189
    protected function getPluralName($entityName): string
190
    {
191
        return Str::plural($entityName);
16✔
192
    }
193

194
    protected function throwFailureException($exceptionClass, $failureMessage, $recommendedMessage): void
195
    {
196
        throw new $exceptionClass("{$failureMessage} {$recommendedMessage}");
18✔
197
    }
198

199
    protected function getRelatedModels(string $model, string $creatableClass): array
200
    {
201
        $modelClass = $this->getModelClass($model);
9✔
202

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

211
        $instance = new $modelClass();
7✔
212

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

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

217
        $relatedModels = [];
7✔
218

219
        DB::beginTransaction();
7✔
220

221
        foreach ($methods as $method) {
7✔
222
            try {
223
                $result = call_user_func([$instance, $method->getName()]);
7✔
224

225
                if (!$result instanceof BelongsTo) {
7✔
226
                    continue;
7✔
227
                }
228
            } catch (Throwable) {
6✔
229
                continue;
6✔
230
            }
231

232
            $relatedModels[] = class_basename(get_class($result->getRelated()));
4✔
233
        }
234

235
        DB::rollBack();
7✔
236

237
        return $relatedModels;
7✔
238
    }
239

240
    protected function getModelClass(string $model): string
241
    {
242
        $modelNamespace = $this->getOrCreateNamespace('models');
12✔
243

244
        return "{$modelNamespace}\\{$model}";
12✔
245
    }
246

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

251
        $stubPath = config($config);
52✔
252

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

256
            $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✔
257

258
            event(new WarningEvent($message));
22✔
259

260
            return false;
22✔
261
        }
262

263
        return true;
39✔
264
    }
265
}
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