Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 42 additions & 4 deletions src/Generators/ModelGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,18 @@ protected function hasRelations(): bool

protected function getNewModelContent(): string
{
$relations = $this->prepareRelations();

return $this->getStub('model', [
'entity' => $this->model,
'fields' => Arr::collapse($this->fields),
'relations' => $this->prepareRelations(),
'relations' => $relations,
'casts' => $this->getCasts($this->fields),
'namespace' => $this->generateNamespace($this->paths['models'], $this->modelSubFolder),
'importRelations' => $this->getImportedRelations(),
'anotationProperties' => $this->generateAnnotationProperties($this->fields),
'annotationProperties' => $this->generateAnnotationProperties($this->fields, $relations),
'hasCarbonField' => !empty($this->fields['timestamp']) || !empty($this->fields['timestamp-required']),
'hasCollectionType' => !empty($this->relations->hasMany) || !empty($this->relations->belongsToMany),
]);
}

Expand Down Expand Up @@ -76,14 +79,19 @@ public function prepareRelatedModels(): void
$this->insertImport($content, $namespace);
}

$relationName = $this->getRelationName($this->model, $types[$type]);

$newRelation = $this->getStub('relation', [
'name' => $this->getRelationName($this->model, $types[$type]),
'name' => $relationName,
'type' => $types[$type],
'entity' => $this->model,
]);

// TODO: use ronasit/larabuilder instead regexp
$fixedContent = preg_replace('/\}$/', "\n {$newRelation}\n}", $content);

$this->insertPropertyAnnotation($fixedContent, $this->getRelationType($this->model, $types[$type]), $relationName);

$this->saveClass('models', $relation, $fixedContent);
}
}
Expand All @@ -101,6 +109,7 @@ protected function insertImport(string &$classContent, string $import): void
$import = "use {$import};";

if (!Str::contains($classContent, $import)) {
// TODO: use ronasit/larabuilder instead regexp
$classContent = preg_replace('/(namespace\s+[^;]+;\s*)/', "$1{$import}\n", $classContent, 1);
}
}
Expand Down Expand Up @@ -190,7 +199,7 @@ protected function generateClassNamespace(string $className, ?string $folder = n
return "{$path}\\{$psrPath}";
}

protected function generateAnnotationProperties(array $fields): array
protected function generateAnnotationProperties(array $fields, array $relations): array
{
$result = [];

Expand All @@ -200,6 +209,10 @@ protected function generateAnnotationProperties(array $fields): array
}
}

foreach ($relations as $relation) {
$result[$relation['name']] = $this->getRelationType($relation['entity'], $relation['type']);
}

return $result;
}

Expand Down Expand Up @@ -239,4 +252,29 @@ protected function isRequired(string $typeName): bool
{
return Str::endsWith($typeName, 'required');
}

protected function getRelationType(string $model, string $relation): string
{
if (in_array($relation, self::PLURAL_NUMBER_REQUIRED)) {
return "Collection<{$model}>";
}

return "{$model}|null";
}

protected function insertPropertyAnnotation(string &$content, string $propertyDataType, string $propertyName): void
{
$annotation = "* @property {$propertyDataType} \${$propertyName}";

// TODO: use ronasit/larabuilder instead regexp
if (!Str::contains($content, '/**')) {
$content = preg_replace('/^\s*class[\s\S]+?\{/m', "\n/**\n {$annotation}\n */$0", $content);
} else {
$content = preg_replace('/\*\//m', "{$annotation}\n $0", $content);
}

if (Str::contains($propertyDataType, 'Collection')) {
$this->insertImport($content, 'Illuminate\Database\Eloquent\Collection');
}
}
}
9 changes: 6 additions & 3 deletions stubs/model.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
@if($hasCarbonField)
use Carbon\Carbon;
@endif
@if($hasCollectionType)
use Illuminate\Database\Eloquent\Collection;
@endif

@if(!empty($anotationProperties))
@if(!empty($annotationProperties))
/**
@foreach($anotationProperties as $key => $value)
* @property {{ $value }} ${{ $key }}
@foreach($annotationProperties as $key => $value)
* @property {!! $value !!} ${{ $key }}
Copy link

Copilot AI Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Using unescaped output {!! !!} for the property value could be a security concern if the value contains malicious content. However, since this is a code generator and values come from controlled sources (the generator itself), this is acceptable. Consider adding a comment explaining why unescaped output is necessary here (to preserve type syntax like Collection<Model>).

Copilot uses AI. Check for mistakes.
@endforeach
*/
@else
Expand Down
24 changes: 22 additions & 2 deletions tests/ModelGeneratorTest.php
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public function setUp(): void
{
parent::setUp();

$this->mockFilesystem();
$this->mockDefaultFilesystem();
}

public function testModelAlreadyExists()
Expand All @@ -41,7 +41,9 @@ className: ResourceAlreadyExistsException::class,

public function testRelationModelMissing()
{
$this->mockFileSystemWithoutCommentModel();
$this->mockFilesystem([
'User.php' => file_get_contents(getcwd() . '/tests/Support/Models/WelcomeBonus.php'),
]);

$this->assertExceptionThrew(
className: ClassNotExistsException::class,
Expand Down Expand Up @@ -247,4 +249,22 @@ className: WarningEvent::class,
message: 'Generation of model has been skipped cause the view incorrect_stub from the config entity-generator.stubs.relation is not exists. Please check that config has the correct view name value.',
);
}

public function testAddPropertyAnnotationToRelatedModel()
{
app(ModelGenerator::class)
->setModel('Category')
->setFields([])
->setRelations(new RelationsDTO(
belongsToMany: ['User'],
))
->generate();

$this->assertGeneratedFileEquals('related_model_with_property.php', 'app/Models/User.php');

$this->assertEventPushed(
className: SuccessCreateMessage::class,
message: 'Created a new Model: Category',
);
}
}
15 changes: 6 additions & 9 deletions tests/Support/Model/ModelMockTrait.php
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,24 @@ trait ModelMockTrait
{
use GeneratorMockTrait;


public function mockFileSystemWithoutCommentModel(): void
public function mockDefaultFilesystem(): void
{
$fileSystemMock = new FileSystemMock();

$fileSystemMock->models = [
'Comment.php' => file_get_contents(getcwd() . '/tests/Support/Models/WelcomeBonus.php'),
'User.php' => file_get_contents(getcwd() . '/tests/Support/Models/WelcomeBonus.php'),
'Forum/Author.php' => file_get_contents(getcwd() . '/tests/Support/Models/WelcomeBonus.php'),
];

$fileSystemMock->setStructure();
}

public function mockFilesystem(): void
public function mockFilesystem(array $models): void
{
$fileSystemMock = new FileSystemMock;
$fileSystemMock = new FileSystemMock();

$fileSystemMock->models = [
'Comment.php' => file_get_contents(getcwd() . '/tests/Support/Models/WelcomeBonus.php'),
'User.php' => file_get_contents(getcwd() . '/tests/Support/Models/WelcomeBonus.php'),
'Forum/Author.php' => file_get_contents(getcwd() . '/tests/Support/Models/WelcomeBonus.php'),
];
$fileSystemMock->models = $models;

$fileSystemMock->setStructure();
}
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/ModelGeneratorTest/comment_relation_model.php
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
use Illuminate\Database\Eloquent\Model;
use RonasIT\Support\Traits\ModelTrait;

/**
* @property Post|null $post
*/
class WelcomeBonus extends Model
{
use ModelTrait;
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/ModelGeneratorTest/new_model.php
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Database\Eloquent\Model;
use RonasIT\Support\Traits\ModelTrait;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;

/**
* @property int|null $priority
Expand All @@ -20,6 +21,8 @@
* @property Carbon|null $updated_at
* @property Carbon $published_at
* @property array $meta
* @property Comment|null $comment
* @property Collection<User> $users
*/
class Post extends Model
{
Expand Down
3 changes: 2 additions & 1 deletion tests/fixtures/ModelGeneratorTest/new_model_with_many_relations.php
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
use Illuminate\Database\Eloquent\Model;
use RonasIT\Support\Traits\ModelTrait;
use App\Models\User;
use Illuminate\Database\Eloquent\Collection;

//TODO: add @property annotation for each model's field
/**
* @property Collection<User> $users
*/
class Post extends Model
{
Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/ModelGeneratorTest/new_model_with_subfolers_relations.php
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
use Illuminate\Database\Eloquent\Model;
use RonasIT\Support\Traits\ModelTrait;
use App\Models\Forum\Author;
use Illuminate\Database\Eloquent\Collection;

/**
* @property string $title
* @property Collection<Author> $authors
*/
class Post extends Model
{
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/ModelGeneratorTest/new_subfolders_model.php
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Models\Comment;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;

/**
* @property int|null $priority
Expand All @@ -22,6 +23,8 @@
* @property Carbon|null $updated_at
* @property Carbon $published_at
* @property array $meta
* @property Comment|null $comment
* @property Collection<User> $users
*/
class Post extends Model
{
Expand Down
34 changes: 34 additions & 0 deletions tests/fixtures/ModelGeneratorTest/related_model_with_property.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace RonasIT\Support\Tests\Support\Models;

use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use RonasIT\Support\Traits\ModelTrait;

/**
* @property Collection<Category> $categories
*/
class WelcomeBonus extends Model
{
use ModelTrait;

public function getConnectionName(): string
{
return 'pgsql';
}

protected $fillable = [
'title',
'name',
];

public function some_relation()
{
}

public function categories()
{
return $this->belongsToMany(Category::class);
}
}