Skip to content

Commit

Permalink
Merge pull request #17 from openforests-git/feature/MD/XL-8681-model-…
Browse files Browse the repository at this point in the history
…generator

feature/MD/XL-8681 model generator
  • Loading branch information
yalsicor authored Sep 17, 2024
2 parents a66d7ba + 1fd60b7 commit 099c582
Show file tree
Hide file tree
Showing 20 changed files with 2,591 additions and 840 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"laravel/framework": "^10.0",
"laravel/passport": "^11.0.0",
"nette/php-generator": "^4.1",
"ext-gettext": "*"
"ext-gettext": "*",
"nikic/php-parser": "^4.19"
},
"require-dev": {
"fakerphp/faker": "^1.19.1",
Expand Down
1,767 changes: 938 additions & 829 deletions composer.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/Generator/Commands/ControllerGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ protected function askCustomInputs(): void
param: 'stub',
label: 'Select the controller type:',
options: [
// add generic
'list' => 'List',
'find' => 'Find',
'create' => 'Create',
Expand Down
104 changes: 104 additions & 0 deletions src/Generator/Commands/CriteriaGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

namespace Apiato\Core\Generator\Commands;

use Apiato\Core\Generator\FileGeneratorCommand;
use Apiato\Core\Generator\Traits\HasTestTrait;

class CriteriaGenerator extends FileGeneratorCommand
{
use HasTestTrait;

public static function getCommandName(): string
{
return 'apiato:make:criteria';
}

public static function getCommandDescription(): string
{
return 'Create a Criteria file for a Container';
}

public static function getFileType(): string
{
return 'criteria';
}

protected static function getCustomCommandArguments(): array
{
return [
];
}

public function askCustomInputs(): void
{
}

protected function getFilePath(): string
{
return "$this->sectionName/$this->containerName/Data/Criterias/$this->fileName.php";
}

protected function getFileContent(): string
{
$file = new \Nette\PhpGenerator\PhpFile();
$namespace = $file->addNamespace('App\Containers\\' . $this->sectionName . '\\' . $this->containerName . '\Data\Criterias');

// imports
$parentCriteriaFullPath = 'App\Ship\Parents\Criterias\Criteria';
$namespace->addUse($parentCriteriaFullPath, 'ParentCriteria');
$repositoryInterface = 'Prettus\Repository\Contracts\RepositoryInterface';
$namespace->addUse($repositoryInterface);

// class
$class = $file->addNamespace($namespace)
->addClass($this->fileName)
->setExtends($parentCriteriaFullPath);

// apply method
$applyMethod = $class->addMethod('apply')
->setPublic()
->setReturnType('mixed')
->setBody('return $model;');
$applyMethod->addParameter('model');
$applyMethod->addParameter('repository')->setType($repositoryInterface);

// return the file
return $file;
}

protected function getTestPath(): string
{
return $this->sectionName . '/' . $this->containerName . '/Tests/Unit/Data/Criterias/' . $this->fileName . 'Test.php';
}

protected function getTestContent(): string
{
$file = new \Nette\PhpGenerator\PhpFile();
$namespace = $file->addNamespace('App\Containers\\' . $this->sectionName . '\\' . $this->containerName . '\Tests\Unit\Data\Criterias');

// imports
$parentUnitTestCaseFullPath = "App\Containers\AppSection\\$this->containerName\Tests\UnitTestCase";
$namespace->addUse($parentUnitTestCaseFullPath);
$criteriaFullPath = 'App\Containers\\' . $this->sectionName . '\\' . $this->containerName . '\Data\Criterias\\' . $this->fileName;
$namespace->addUse($criteriaFullPath);

// class
$class = $file->addNamespace($namespace)
->addClass($this->fileName . 'Test')
->setFinal()
->setExtends($parentUnitTestCaseFullPath);

$testMethod = $class->addMethod('testCriteria')->setPublic();
$testMethod->addBody("
\$criteria = app($this->fileName::class);
// Remove the following lines and add your logic and assertions
\$this->assertInstanceOf($this->fileName::class, \$criteria);
");
$testMethod->setReturnType('void');

// return the file
return $file;
}
}
119 changes: 119 additions & 0 deletions src/Generator/Commands/EndpointGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php

namespace Apiato\Core\Generator\Commands;

use Apiato\Core\Generator\CompositeGeneratorCommand;
use Symfony\Component\Console\Input\InputOption;

class EndpointGenerator extends CompositeGeneratorCommand
{
protected string $feature;
protected string $model;
protected string $ui;
protected string $stub;

public static function getCommandName(): string
{
return 'apiato:make:endpoint';
}

public static function getCommandDescription(): string
{
return 'Create an Endpoint for a Container';
}

protected static function getCustomCommandArguments(): array
{
return [
['feature', null, InputOption::VALUE_OPTIONAL, 'The feature name of the endpoint. (E.g. CreateUser, LinkPostToBlog, DeleteAllPosts, ...)'],
['model', null, InputOption::VALUE_OPTIONAL, 'The model this endpoint is for.'],
['ui', null, InputOption::VALUE_OPTIONAL, 'The UI of the endpoint. (API, WEB)'],
['stub', null, InputOption::VALUE_OPTIONAL, 'The stub file to load for this endpoint.'],
];
}

protected function askCustomInputs(): void
{
$this->feature = $this->checkParameterOrAskText(
param: 'feature',
label: 'Enter the feature name:',
hint: 'E.g. CreateUser, LinkPostToBlog, DeleteAllPosts, ...',
);
$this->model = $this->checkParameterOrAskTextSuggested(
param: 'model',
label: 'Enter the name of the Model:',
default: $this->containerName,
suggestions: $this->getModelsList(
section: $this->sectionName,
container: $this->containerName,
removeModelPostFix: true,
),
);
$this->ui = $this->checkParameterOrSelect(
param: 'ui',
label: 'Select the UI of the endpoint:',
options: ['API', 'WEB'],
default: 'API',
);
$this->stub = $this->checkParameterOrSelect(
param: 'stub',
label: 'Select the endpoint type:',
options: [
'generic' => 'Generic',
'list' => 'List',
'find' => 'Find',
'create' => 'Create',
'update' => 'Update',
'delete' => 'Delete',
],
default: 'find',
hint: 'Different types of endpoints have different default behaviors.',
);
}

protected function runGeneratorCommands(): void
{
$featureCamelCase = lcfirst($this->feature);
$featurePascalCase = ucfirst($this->feature);

$this->runGeneratorCommand(RouteGenerator::class, [
'--section' => $this->sectionName,
'--container' => $this->containerName,
'--file' => $featurePascalCase,
'--ui' => $this->ui,
'--controller' => $featurePascalCase . 'Controller',
'--test' => $this->test,
]);
$this->runGeneratorCommand(ActionGenerator::class, [
'--section' => $this->sectionName,
'--container' => $this->containerName,
'--file' => $featurePascalCase . 'Action',
'--model' => $this->model,
'--stub' => $this->stub,
'--ui' => $this->ui,
'--test' => $this->test,
]);
$this->runGeneratorCommand(ControllerGenerator::class, [
'--section' => $this->sectionName,
'--container' => $this->containerName,
'--file' => $featurePascalCase . 'Controller',
'--stub' => $this->stub,
'--test' => $this->test,
]);
$this->runGeneratorCommand(RequestGenerator::class, [
'--section' => $this->sectionName,
'--container' => $this->containerName,
'--file' => $featurePascalCase . 'Request',
'--model' => $this->model,
'--stub' => $this->stub,
'--test' => $this->test,
]);
$this->runGeneratorCommand(PolicyGenerator::class, [
'--section' => $this->sectionName,
'--container' => $this->containerName,
'--file' => $this->model . 'Policy',
'--method' => $featureCamelCase,
'--test' => $this->test,
]);
}
}
70 changes: 70 additions & 0 deletions src/Generator/Commands/EventListenerGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace Apiato\Core\Generator\Commands;

use Apiato\Core\Generator\FileGeneratorCommand;

// TODO: Make this command receive the event name as an argument
// when `EventGenerator` is implemented
class EventListenerGenerator extends FileGeneratorCommand
{
public static function getCommandName(): string
{
return 'apiato:make:listener';
}

public static function getCommandDescription(): string
{
return 'Create an Event Listener for a Container';
}

public static function getFileType(): string
{
return 'listener';
}

protected static function getCustomCommandArguments(): array
{
return [
];
}

protected function askCustomInputs(): void
{
}

protected function getFilePath(): string
{
return "$this->sectionName/$this->containerName/Listeners/$this->fileName.php";
}

protected function getFileContent(): string
{
$file = new \Nette\PhpGenerator\PhpFile();
$namespace = $file->addNamespace('App\Containers\\' . $this->sectionName . '\\' . $this->containerName . '\Listeners');

// imports
$parentJobFullPath = 'App\Ship\Parents\Listeners\Listener';
$namespace->addUse($parentJobFullPath, 'ParentListener');
$shouldQueueFullPath = 'Illuminate\Contracts\Queue\ShouldQueue';
$namespace->addUse($shouldQueueFullPath);

// class
$class = $file->addNamespace($namespace)
->addClass($this->fileName)
->setExtends($parentJobFullPath)
->addImplement($shouldQueueFullPath);

// constructor method
$constructorMethod = $class->addMethod('__construct')
->setPublic();

// handle method
$handleMethod = $class->addMethod('handle')
->setPublic()
->setReturnType('void')
->addParameter('event');

return $file;
}
}
67 changes: 67 additions & 0 deletions src/Generator/Commands/ExceptionGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace Apiato\Core\Generator\Commands;

use Apiato\Core\Generator\FileGeneratorCommand;
use Symfony\Component\HttpFoundation\Response;

class ExceptionGenerator extends FileGeneratorCommand
{
public static function getCommandName(): string
{
return 'apiato:make:exception';
}

public static function getCommandDescription(): string
{
return 'Create an Exception for a Container';
}

public static function getFileType(): string
{
return 'exception';
}

protected static function getCustomCommandArguments(): array
{
return [
];
}

protected function askCustomInputs(): void
{
}

protected function getFilePath(): string
{
return "$this->sectionName/$this->containerName/Exceptions/$this->fileName.php";
}

protected function getFileContent(): string
{
$file = new \Nette\PhpGenerator\PhpFile();
$namespace = $file->addNamespace('App\Containers\\' . $this->sectionName . '\\' . $this->containerName . '\Exceptions');

// imports
$parentExceptionFullPath = 'App\Ship\Parents\Exceptions\Exception';
$namespace->addUse($parentExceptionFullPath, 'ParentException');
$responseFullPath = 'Symfony\Component\HttpFoundation\Response';
$namespace->addUse($responseFullPath);

// class
$class = $file->addNamespace($namespace)
->addClass($this->fileName)
->setExtends($parentExceptionFullPath);

// properties
$class->addProperty('code')
->setVisibility('protected')
->setValue(Response::HTTP_BAD_REQUEST); // TODO: find a way to initialize it with `Response::HTTP_BAD_REQUEST` not 400

$class->addProperty('message')
->setVisibility('protected')
->setValue('Exception Default Message.');

return $file;
}
}
Loading

0 comments on commit 099c582

Please sign in to comment.