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

RonasIT / laravel-entity-generator / 16722956627

04 Aug 2025 12:19PM UTC coverage: 99.893% (-0.1%) from 100.0%
16722956627

Pull #170

github

web-flow
Merge 9025227d3 into 3b878ef3f
Pull Request #170: feat: Generate property annotations for Model

25 of 26 new or added lines in 1 file covered. (96.15%)

1 existing line in 1 file now uncovered.

930 of 931 relevant lines covered (99.89%)

11.37 hits per line

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

98.95
/src/Generators/ModelGenerator.php
1
<?php
2

3
namespace RonasIT\Support\Generators;
4

5
use Illuminate\Support\Arr;
6
use Illuminate\Support\Str;
7
use RonasIT\Support\Exceptions\ClassAlreadyExistsException;
8
use RonasIT\Support\Exceptions\ClassNotExistsException;
9
use RonasIT\Support\Events\SuccessCreateMessage;
10
use RonasIT\Support\Exceptions\UnknownFieldTypeException;
11

12
class ModelGenerator extends EntityGenerator
13
{
14
    protected const array PLURAL_NUMBER_REQUIRED = [
15
        'belongsToMany',
16
        'hasMany',
17
    ];
18

19
    public function generate(): void
20
    {
21
        if ($this->classExists('models', $this->model)) {
18✔
22
            $this->throwFailureException(
2✔
23
                exceptionClass: ClassAlreadyExistsException::class,
2✔
24
                failureMessage: "Cannot create {$this->model} Model cause {$this->model} Model already exists.",
2✔
25
                recommendedMessage: "Remove {$this->model} Model.",
2✔
26
            );
2✔
27
        }
28

29
        if ($this->isStubExists('model') && (!$this->hasRelations() || $this->isStubExists('relation', 'model'))) {
16✔
30
            $this->prepareRelatedModels();
12✔
31
            $modelContent = $this->getNewModelContent();
10✔
32

33
            $this->saveClass('models', $this->model, $modelContent);
10✔
34

35
            event(new SuccessCreateMessage("Created a new Model: {$this->model}"));
10✔
36
        }
37
    }
38

39
    protected function hasRelations(): bool
40
    {
41
        return !collect($this->relations)->every(fn ($relation) => empty($relation));
14✔
42
    }
43

44
    protected function getNewModelContent(): string
45
    {
46
        return $this->getStub('model', [
10✔
47
            'entity' => $this->model,
10✔
48
            'fields' => Arr::collapse($this->fields),
10✔
49
            'relations' => $this->prepareRelations(),
10✔
50
            'casts' => $this->getCasts($this->fields),
10✔
51
            'namespace' => $this->getOrCreateNamespace('models'),
10✔
52
            'anotationProperties' => $this->generateAnnotationProperties($this->fields),
10✔
53
        ]);
10✔
54
    }
55

56
    public function prepareRelatedModels(): void
57
    {
58
        $types = [
12✔
59
            'hasMany' => 'belongsTo',
12✔
60
            'hasOne' => 'belongsTo',
12✔
61
            'belongsTo' => 'hasOne',
12✔
62
            'belongsToMany' => 'belongsToMany',
12✔
63
        ];
12✔
64

65
        foreach ($this->relations as $type => $relationsByType) {
12✔
66
            foreach ($relationsByType as $relation) {
10✔
67
                if (!$this->classExists('models', $relation)) {
4✔
68
                    $this->throwFailureException(
2✔
69
                        exceptionClass: ClassNotExistsException::class,
2✔
70
                        failureMessage: "Cannot create {$this->model} Model cause relation model {$relation} does not exist.",
2✔
71
                        recommendedMessage: "Create the {$relation} Model by himself or run command 'php artisan make:entity {$relation} --only-model'.",
2✔
72
                    );
2✔
73
                }
74

75
                $content = $this->getModelContent($relation);
2✔
76

77
                $newRelation = $this->getStub('relation', [
2✔
78
                    'name' => $this->getRelationName($this->model, $types[$type]),
2✔
79
                    'type' => $types[$type],
2✔
80
                    'entity' => $this->model,
2✔
81
                ]);
2✔
82

83
                $fixedContent = preg_replace('/\}$/', "\n    {$newRelation}\n}", $content);
2✔
84

85
                $this->saveClass('models', $relation, $fixedContent);
2✔
86
            }
87
        }
88
    }
89

90
    public function getModelContent(string $model): string
91
    {
92
        $modelPath = base_path("{$this->paths['models']}/{$model}.php");
2✔
93

94
        return file_get_contents($modelPath);
2✔
95
    }
96

97
    public function prepareRelations(): array
98
    {
99
        $result = [];
10✔
100

101
        foreach ($this->relations as $type => $relations) {
10✔
102
            foreach ($relations as $relation) {
8✔
103
                if (!empty($relation)) {
2✔
104
                    $result[] = [
2✔
105
                        'name' => $this->getRelationName($relation, $type),
2✔
106
                        'type' => $type,
2✔
107
                        'entity' => $relation,
2✔
108
                    ];
2✔
109
                }
110
            }
111
        }
112

113
        return $result;
10✔
114
    }
115

116
    protected function getCasts(array $fields): array
117
    {
118
        $casts = [
10✔
119
            'boolean-required' => 'boolean',
10✔
120
            'boolean' => 'boolean',
10✔
121
            'json' => 'array'
10✔
122
        ];
10✔
123

124
        $result = [];
10✔
125

126
        foreach ($fields as $fieldType => $names) {
10✔
127
            if (!array_key_exists($fieldType, $casts)) {
6✔
128
                continue;
6✔
129
            }
130

131
            foreach ($names as $name) {
6✔
132
                $result[$name] = $casts[$fieldType];
2✔
133
            }
134
        }
135

136
        return $result;
10✔
137
    }
138

139
    private function getRelationName(string $relation, string $type): string
140
    {
141
        $relationName = Str::snake($relation);
2✔
142

143
        if (in_array($type, self::PLURAL_NUMBER_REQUIRED)) {
2✔
144
            $relationName = Str::plural($relationName);
2✔
145
        }
146

147
        return $relationName;
2✔
148
    }
149

150
    protected function generateAnnotationProperties(array $fields): array
151
    {
152
        $result = [];
10✔
153

154
        foreach ($fields as $typeName => $fieldNames) {
10✔
155
            foreach ($fieldNames as $fieldName) {
6✔
156
                $result[] = $this->getPropertyLine($fieldName, $typeName);
2✔
157
            }
158
        }
159

160
        return $result;
10✔
161
    }
162

163
    protected function getPropertyLine(string $fieldName, string $typeName): string
164
    {
165
        if ($this->isJson($typeName)) {
2✔
166
            return $this->getProperty($fieldName, $typeName);
2✔
167
        }
168

169
        if ($this->isRequired($typeName)) {
2✔
170
            return $this->getProperty($fieldName, $typeName);
2✔
171
        }
172

173
        return $this->getProperty($fieldName, $typeName, true);
2✔
174
    }
175
    
176
    protected function getProperty(string $fieldName, string $typeName, bool $isNullable = false): string
177
    {
178
        $typeNames = [
2✔
179
            'integer' => 'int',
2✔
180
            'float' => 'float',
2✔
181
            'string' => 'string',
2✔
182
            'boolean' => 'bool',
2✔
183
            'timestamp' => 'Carbon',
2✔
184
            'json' => 'array',
2✔
185
        ];
2✔
186

187
        $type = $typeNames[Str::before($typeName, '-')];
2✔
188

189
        if ($isNullable) {
2✔
190
            $type .= '|null';
2✔
191
        }
192

193
        return "* @property {$type} {$fieldName}";
2✔
194
    }
195

196
    protected function isJson(string $typeName): bool
197
    {
198
        return $typeName === 'json';
2✔
199
    }
200

201
    protected function isRequired(string $typeName): bool
202
    {
203
        return Str::afterLast($typeName, '-') === 'required';
2✔
204
    }
205

206
    protected function isNullable(string $typeName): bool
207
    {
NEW
UNCOV
208
        return !strpos($typeName, '-');
×
209
    }
210
}
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