Skip to content

Commit

Permalink
Merge pull request #945 from spiral/feature/scaffolder-instructions
Browse files Browse the repository at this point in the history
Introducing Instructions Feature in Spiral Framework's Scaffold Generator
  • Loading branch information
butschster authored Jul 12, 2023
2 parents a309d2c + 7a6e32b commit 0e4b58e
Show file tree
Hide file tree
Showing 16 changed files with 300 additions and 34 deletions.
21 changes: 18 additions & 3 deletions src/Scaffolder/src/Command/AbstractCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
namespace Spiral\Scaffolder\Command;

use Psr\Container\ContainerInterface;
use Spiral\Boot\DirectoriesInterface;
use Spiral\Console\Command;
use Spiral\Core\FactoryInterface;
use Spiral\Files\FilesInterface;
use Spiral\Reactor\Writer;
use Spiral\Scaffolder\Config\ScaffolderConfig;
use Spiral\Scaffolder\Declaration\DeclarationInterface;
use Spiral\Scaffolder\Declaration\HasInstructions;

abstract class AbstractCommand extends Command
{
Expand All @@ -19,6 +21,7 @@ public function __construct(
protected FilesInterface $files,
ContainerInterface $container,
private readonly FactoryInterface $factory,
private readonly DirectoriesInterface $dirs,
) {
$this->setContainer($container);

Expand Down Expand Up @@ -53,13 +56,16 @@ protected function writeDeclaration(DeclarationInterface $declaration): void
(string)$this->argument('name'),
$this->getNamespace(),
);
$filename = $this->files->normalizePath($filename);

$rootDirectory = $this->dirs->get('root');

$className = $declaration->getClass()->getName();
$relativeFilename = \str_replace($rootDirectory, '', $filename);

if ($this->files->exists($filename)) {
$this->writeln(
\sprintf("<fg=red>Unable to create '<comment>%s</comment>' declaration, ", $className)
. \sprintf("file '<comment>%s</comment>' already exists.</fg=red>", $filename),
. \sprintf("file '<comment>%s</comment>' already exists.</fg=red>", $relativeFilename),
);

return;
Expand All @@ -70,8 +76,17 @@ protected function writeDeclaration(DeclarationInterface $declaration): void

$this->writeln(
\sprintf("Declaration of '<info>%s</info>' ", $className)
. \sprintf("has been successfully written into '<comment>%s</comment>'.", $filename),
. \sprintf("has been successfully written into '<comment>%s</comment>'.", $relativeFilename),
);

if ($declaration instanceof HasInstructions && \count($declaration->getInstructions()) > 0) {
$this->newLine();
$this->writeln('<fg=green>Next steps:</fg=green>');

foreach ($declaration->getInstructions() as $i => $instruction) {
$this->writeln(\sprintf('%d. %s', (string)(++$i), $instruction));
}
}
}

protected function getNamespace(): ?string
Expand Down
14 changes: 13 additions & 1 deletion src/Scaffolder/src/Declaration/BootloaderDeclaration.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
use Nette\PhpGenerator\Literal;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Boot\BootloadManager\Methods;
use Spiral\Boot\KernelInterface;
use Spiral\Bootloader\DomainBootloader;
use Spiral\Core\CoreInterface;
use Spiral\Scaffolder\Config\ScaffolderConfig;

class BootloaderDeclaration extends AbstractDeclaration
class BootloaderDeclaration extends AbstractDeclaration implements HasInstructions
{
public const TYPE = 'bootloader';

public function __construct(
private readonly KernelInterface $kernel,
ScaffolderConfig $config,
string $name,
?string $comment = null,
Expand Down Expand Up @@ -52,4 +54,14 @@ public function declare(): void
$this->class->addMethod(Methods::INIT->value)->setReturnType('void');
$this->class->addMethod(Methods::BOOT->value)->setReturnType('void');
}

public function getInstructions(): array
{
$kernelClass = (new \ReflectionClass($this->kernel))->getName();

return [
\sprintf('Don\'t forget to add your bootloader to the bootloader\'s list in \'<comment>%s</comment>\' class', $kernelClass),
'Read more about bootloaders in the documentation: https://spiral.dev/docs/framework-bootloaders',
];
}
}
10 changes: 9 additions & 1 deletion src/Scaffolder/src/Declaration/CommandDeclaration.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use Spiral\Console\Command;
use Spiral\Scaffolder\Config\ScaffolderConfig;

class CommandDeclaration extends AbstractDeclaration
final class CommandDeclaration extends AbstractDeclaration implements HasInstructions
{
public const TYPE = 'command';

Expand Down Expand Up @@ -87,4 +87,12 @@ public function declare(): void
PHP,
);
}

public function getInstructions(): array
{
return [
\sprintf('Use the following command to run your command: \'<comment>php app.php %s</comment>\'', $this->alias),
'Read more about user Commands in the documentation: https://spiral.dev/docs/console-commands',
];
}
}
30 changes: 23 additions & 7 deletions src/Scaffolder/src/Declaration/ConfigDeclaration.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@

use Cocur\Slugify\SlugifyInterface;
use Doctrine\Inflector\Rules\English\InflectorFactory;
use Nette\PhpGenerator\Literal;
use Nette\PhpGenerator\Dumper;
use Nette\PhpGenerator\Literal;
use Spiral\Boot\DirectoriesInterface;
use Spiral\Core\InjectableConfig;
use Spiral\Files\FilesInterface;
use Spiral\Reactor\FileDeclaration;
Expand All @@ -17,13 +18,15 @@

use function Spiral\Scaffolder\defineArrayType;

class ConfigDeclaration extends AbstractDeclaration
class ConfigDeclaration extends AbstractDeclaration implements HasInstructions
{
public const TYPE = 'config';
private bool $reverse = false;

public function __construct(
ScaffolderConfig $config,
protected readonly FilesInterface $files,
protected readonly DirectoriesInterface $dirs,
protected readonly SlugifyInterface $slugify,
protected readonly ConfigDeclaration\TypeAnnotations $typeAnnotations,
protected readonly ConfigDeclaration\TypeHints $typeHints,
Expand All @@ -38,6 +41,7 @@ public function __construct(

public function create(bool $reverse, string $configName): void
{
$this->reverse = $reverse;
$this->class->addConstant('CONFIG', $configName)->setPublic();

$filename = $this->makeConfigFilename($configName);
Expand All @@ -50,10 +54,8 @@ public function create(bool $reverse, string $configName): void
$this->declareGetters($defaultsFromFile);

$this->class->getProperty('config')->setValue($this->defaultValues->get($defaultsFromFile));
} else {
if (!$this->files->exists($filename)) {
$this->touchConfigFile($filename);
}
} elseif (!$this->files->exists($filename)) {
$this->touchConfigFile($filename);
}
}

Expand All @@ -74,12 +76,26 @@ public function declare(): void
->setValue([])
->setComment(
<<<'DOC'
Default values for the config.
Default values for the config.
Will be merged with application config in runtime.
DOC,
);
}

public function getInstructions(): array
{
$configFile = $this->makeConfigFilename(
$this->class->getConstant('CONFIG')->getValue()
);

$configFile = \str_replace($this->dirs->get('root'), '', $configFile);

return [
\sprintf('You can now add your config values to the \'<comment>%s</comment>\' file.', $configFile),
'Read more about Config Objects in the documentation: https://spiral.dev/docs/framework-config',
];
}

private function makeConfigFilename(string $filename): string
{
return \sprintf('%s%s.php', $this->directory, $filename);
Expand Down
9 changes: 8 additions & 1 deletion src/Scaffolder/src/Declaration/ControllerDeclaration.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
/**
* Declares controller.
*/
class ControllerDeclaration extends AbstractDeclaration
class ControllerDeclaration extends AbstractDeclaration implements HasInstructions
{
public const TYPE = 'controller';

Expand All @@ -39,4 +39,11 @@ public function declare(): void
$this->namespace->addUse(Route::class);
$this->namespace->addUse(ResponseInterface::class);
}

public function getInstructions(): array
{
return [
'Read more about Controllers in the documentation: https://spiral.dev/docs/http-routing',
];
}
}
23 changes: 20 additions & 3 deletions src/Scaffolder/src/Declaration/FilterDeclaration.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Spiral\Scaffolder\Declaration;

use Nette\PhpGenerator\Property;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\UploadedFileInterface;
use Psr\Http\Message\UriInterface;
use Spiral\Filters\Attribute\Input;
Expand All @@ -16,18 +17,26 @@
use Spiral\Validation\Config\ValidationConfig;
use Spiral\Validation\Exception\ValidationException;

class FilterDeclaration extends AbstractDeclaration
class FilterDeclaration extends AbstractDeclaration implements HasInstructions
{
public const TYPE = 'filter';
private readonly ?ValidationConfig $validationConfig;

public function __construct(
private readonly ValidationConfig $validationConfig,
ContainerInterface $container,
ScaffolderConfig $config,
string $name,
?string $comment = null,
?string $namespace = null,
) {
parent::__construct($config, $name, $comment, $namespace);

try {
$this->validationConfig = $container->get(ValidationConfig::class);
} catch (\Throwable $e) {
// Validation is not configured
$this->validationConfig = null;
}
}

public function declare(): void
Expand All @@ -40,7 +49,7 @@ public function declare(): void

public function addFilterDefinition(): void
{
$validation = $this->validationConfig->getDefaultValidator();
$validation = $this->validationConfig?->getDefaultValidator();
if ($validation === null) {
throw new ValidationException(
'Default Validator is not configured. Read more at https://spiral.dev/docs/validation-factory'
Expand Down Expand Up @@ -86,6 +95,14 @@ public function addProperty(string $property): void
}
}

public function getInstructions(): array
{
return [
'Read more about Filter Objects in the documentation: https://spiral.dev/docs/filters-filter',
'Read more about Filter validation handling here: https://spiral.dev/docs/filters-filter#handle-validation-errors',
];
}

private function parseProperty(string $property): Property
{
$declaredType = null;
Expand Down
13 changes: 13 additions & 0 deletions src/Scaffolder/src/Declaration/HasInstructions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Spiral\Scaffolder\Declaration;

interface HasInstructions
{
/**
* @return non-empty-string[]
*/
public function getInstructions(): array;
}
9 changes: 8 additions & 1 deletion src/Scaffolder/src/Declaration/JobHandlerDeclaration.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use Spiral\Queue\JobHandler;

class JobHandlerDeclaration extends AbstractDeclaration
class JobHandlerDeclaration extends AbstractDeclaration implements HasInstructions
{
public const TYPE = 'jobHandler';

Expand All @@ -30,4 +30,11 @@ public function declare(): void
$method->addParameter('headers')
->setType('array');
}

public function getInstructions(): array
{
return [
'Read more about Job handlers in the documentation: https://spiral.dev/docs/queue-jobs',
];
}
}
10 changes: 9 additions & 1 deletion src/Scaffolder/src/Declaration/MiddlewareDeclaration.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
/**
* Middleware declaration.
*/
class MiddlewareDeclaration extends AbstractDeclaration
class MiddlewareDeclaration extends AbstractDeclaration implements HasInstructions
{
public const TYPE = 'middleware';

Expand All @@ -37,4 +37,12 @@ public function declare(): void
->addParameter('handler')
->setType(RequestHandlerInterface::class);
}

public function getInstructions(): array
{
return [
'Don\'t forget to activate a middleware in the \'<comment>App\Application\Bootloader\RoutesBootloader</comment>\'',
'Read more about Middleware in the documentation: https://spiral.dev/docs/http-middleware',
];
}
}
27 changes: 26 additions & 1 deletion src/Scaffolder/tests/Command/BootloaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Spiral\Core\CoreInterface;
use Throwable;

class BootloaderTest extends AbstractCommandTestCase
final class BootloaderTest extends AbstractCommandTestCase
{
/**
* @throws ReflectionException
Expand Down Expand Up @@ -69,6 +69,7 @@ public function testScaffoldWithCustomNamespace(): void
'App/Custom/Bootloader/SampleBootloader.php',
\str_replace('\\', '/', $reflection->getFileName())
);

$this->assertStringContainsString('App\Custom\Bootloader', $content);
}

Expand Down Expand Up @@ -99,4 +100,28 @@ public function testScaffoldForDomainBootloader(): void
CoreInterface::class => ['Spiral\Tests\Scaffolder\App\Bootloader\SampleDomainBootloader', 'domainCore'],
], $reflection->getConstant('SINGLETONS'));
}

public function testShowInstructionAfterInstallation(): void
{
$this->className = $class = '\\Spiral\\Tests\\Scaffolder\\App\\Bootloader\\SampleBootloader';

$result = $this->console()->run('create:bootloader', [
'name' => 'sample',
'--comment' => 'Sample Bootloader'
]);

$output = $result->getOutput()->fetch();

$this->assertSame(
<<<OUTPUT
Declaration of 'SampleBootloader' has been successfully written into 'Bootloader/SampleBootloader.php'.
Next steps:
1. Don't forget to add your bootloader to the bootloader's list in 'Spiral\Tests\Scaffolder\App\TestApp' class
2. Read more about bootloaders in the documentation: https://spiral.dev/docs/framework-bootloaders
OUTPUT,
$output
);
}
}
Loading

0 comments on commit 0e4b58e

Please sign in to comment.