Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create module:make-class command #1802

Merged
merged 24 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
dcb4c48
Create make:class command
solomon-ochepa Apr 1, 2024
a1a82d6
Updated .gitignore
dcblogdev Apr 17, 2024
e8a736b
[misc] restored config data position
solomon-ochepa Apr 25, 2024
945fd2e
[optimize] Check $this->argumentName is not null before adding extra …
solomon-ochepa Apr 25, 2024
bc97a64
[complete] Update class
solomon-ochepa Apr 25, 2024
910cc15
[fix] Add $argumentName = "name"
solomon-ochepa Apr 26, 2024
4f701a7
Revert changes to config
solomon-ochepa Apr 26, 2024
2ce3880
Create clean_path() method - PathNamespace
solomon-ochepa May 4, 2024
5f276ee
Create app_path() method
solomon-ochepa May 4, 2024
dafd777
Apply app_path() method to ClassCommand class
solomon-ochepa May 4, 2024
3f1fcf8
Remove studly_path() and path_namespace()
solomon-ochepa May 4, 2024
f0ac74a
Update ClassCommand
solomon-ochepa May 4, 2024
190cf20
Update PathNamespace
solomon-ochepa May 4, 2024
bb7af6c
Create path() method in GeneratorCommand
solomon-ochepa May 4, 2024
95347cf
Improve getDestinationFilePath() using the newly path() method
solomon-ochepa May 4, 2024
fdcbb42
Merge master config
solomon-ochepa May 4, 2024
871696f
Fix issue with namespace including class name.
solomon-ochepa May 4, 2024
740caa3
Avoid double suffix
solomon-ochepa May 4, 2024
2eb2353
Impliment invokable feature and move stubs to base path.
solomon-ochepa May 6, 2024
2db7125
Optimize calling of module instance
solomon-ochepa May 6, 2024
a6ddf15
updated invokable to invoke
dcblogdev May 12, 2024
f2a08f7
added tests for make:class
dcblogdev May 12, 2024
7a00ac9
added make:class command to ConsoleServiceProvider
dcblogdev May 12, 2024
50291d1
updated make:class to use suffix instead of plain
dcblogdev May 12, 2024
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ coverage
.phpunit.result.cache
.idea
.php-cs-fixer.cache
**/.DS_Store
1 change: 1 addition & 0 deletions config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
'actions' => ['path' => 'app/Actions', 'generate' => false],
'casts' => ['path' => 'app/Casts', 'generate' => false],
'channels' => ['path' => 'app/Broadcasting', 'generate' => false],
'class' => ['path' => 'app/Classes', 'generate' => false],
'command' => ['path' => 'app/Console', 'generate' => false],
'component-class' => ['path' => 'app/View/Components', 'generate' => false],
'emails' => ['path' => 'app/Emails', 'generate' => false],
Expand Down
91 changes: 91 additions & 0 deletions src/Commands/Make/ClassMakeCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

namespace Nwidart\Modules\Commands\Make;

use Illuminate\Support\Str;
use Nwidart\Modules\Support\Config\GenerateConfigReader;
use Nwidart\Modules\Support\Stub;
use Nwidart\Modules\Traits\ModuleCommandTrait;

class ClassMakeCommand extends GeneratorCommand
{
use ModuleCommandTrait;

/**
* The name and signature of the console command.
*/
protected $signature = 'module:make-class
{--t|type=class : The type of class, e.g. class, service, repository, contract, etc.}
{--s|suffix : Create the class without the type suffix}
{--i|invokable : Generate a single method, invokable class}
{--f|force : Create the class even if the class already exists}
{name : The name of the class}
{module : The targeted module}';

/**
* The console command description.
*/
protected $description = 'Create a new class';

protected $argumentName = 'name';

public function getTemplateContents(): string
{
return (new Stub($this->stub(), [
'NAMESPACE' => $this->getClassNamespace($this->module()),
'CLASS' => $this->typeClass(),
]))->render();
}

public function stub(): string
{
return $this->option('invokable') ? '/class-invoke.stub' : '/class.stub';
}

public function getDestinationFilePath(): string
{
$path = $this->laravel['modules']->getModulePath($this->getModuleName());

$filePath = GenerateConfigReader::read('class')->getPath() ?? config('modules.paths.app_folder') . 'Classes';

return $this->typePath($path . $filePath . '/' . $this->getFileName() . '.php');
}

protected function getFileName(): string
{
$file = Str::studly($this->argument('name'));

if ($this->option('suffix') === true) {
$names = [Str::plural($this->type()), Str::singular($this->type())];
$file = Str::of($file)->remove($names, false);
$file .= Str::of($this->type())->studly();
}

return $file;
}

/**
* Get the type of class e.g. class, service, repository, etc.
*/
protected function type(): string
{
return Str::of($this->option('type'))->remove('=')->singular();
}

protected function typePath(string $path): string
{
return ($this->type() === 'class') ? $path : Str::of($path)->replaceLast('Classes', Str::of($this->type())->plural()->studly());
}

public function typeClass(): string
{
return Str::of($this->getFileName())->basename()->studly();
}

public function getDefaultNamespace(): string
{
$type = $this->type();

return config("modules.paths.generator.{$type}.namespace", 'Classes');
}
}
7 changes: 6 additions & 1 deletion src/Commands/Make/GeneratorCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Console\Command;
use Nwidart\Modules\Exceptions\FileAlreadyExistException;
use Nwidart\Modules\Generators\FileGenerator;
use Nwidart\Modules\Module;
use Nwidart\Modules\Traits\PathNamespace;

abstract class GeneratorCommand extends Command
Expand Down Expand Up @@ -50,7 +51,6 @@ public function handle(): int
$overwriteFile = $this->hasOption('force') ? $this->option('force') : false;
(new FileGenerator($path, $contents))->withFileOverwrite($overwriteFile)->generate();
});

} catch (FileAlreadyExistException $e) {
$this->components->error("File : {$path} already exists.");

Expand Down Expand Up @@ -93,4 +93,9 @@ public function getClassNamespace($module)

return $this->module_namespace($module->getStudlyName(), $this->getDefaultNamespace() . ($path_namespace ? '\\' . $path_namespace : ''));
}

public function module(?string $name = null): Module
{
return $this->laravel['modules']->findOrFail($name ?? $this->getModuleName());
}
}
11 changes: 11 additions & 0 deletions src/Commands/stubs/class-invoke.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace $NAMESPACE$;

class $CLASS$
{
public function __invoke()
{
//
}
}
11 changes: 11 additions & 0 deletions src/Commands/stubs/class.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace $NAMESPACE$;

class $CLASS$
{
public function __construct()
{
//
}
}
1 change: 1 addition & 0 deletions src/Providers/ConsoleServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public static function defaultCommands(): Collection
Commands\Make\ActionMakeCommand::class,
Commands\Make\CastMakeCommand::class,
Commands\Make\ChannelMakeCommand::class,
Commands\Make\ClassMakeCommand::class,
Commands\Make\CommandMakeCommand::class,
Commands\Make\ComponentClassMakeCommand::class,
Commands\Make\ComponentViewMakeCommand::class,
Expand Down
33 changes: 25 additions & 8 deletions src/Traits/PathNamespace.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,17 @@ trait PathNamespace
/**
* Get a well-formatted StudlyCase representation of path components.
*/
public function studly_path(string $path, $directory_separator = '/'): string
public function studly_path(string $path, $ds = '/'): string
{
return collect(explode($directory_separator, Str::of($path)
->replace("{$directory_separator}{$directory_separator}", $directory_separator)->trim($directory_separator)))
->map(fn ($path) => Str::studly($path))
->implode($directory_separator);
return collect(explode($ds, $this->clean_path($path, $ds)))->map(fn ($path) => Str::studly($path))->implode($ds);
}

/**
* Get a well-formatted StudlyCase namespace.
*/
public function studly_namespace(string $namespace, $directory_separator = '\\'): string
public function studly_namespace(string $namespace, $ds = '\\'): string
{
return $this->studly_path($namespace, $directory_separator);
return $this->studly_path($namespace, $ds);
}

/**
Expand All @@ -36,11 +33,31 @@ public function path_namespace(string $path): string
/**
* Get a well-formatted StudlyCase namespace for a module, with an optional additional path.
*/
public function module_namespace(string $module, string $path = null): string
public function module_namespace(string $module, ?string $path = null): string
{
$module_namespace = config('modules.namespace', $this->path_namespace(config('modules.paths.modules'))) . '\\' . ($module);
$module_namespace .= strlen($path) ? '\\' . $this->path_namespace($path) : '';

return $this->studly_namespace($module_namespace);
}

/**
* Clean path
*/
public function clean_path(string $path, $ds = '/'): string
{
return Str::of($path)->explode($ds)->reject(empty($path))->implode($ds);
}

/**
* Get the app path basename.
*/
public function app_path(?string $path = null): string
{
$config_path = config('modules.paths.app_folder');
$app_path = strlen($config_path) ? trim($config_path, '/') : 'app';
$app_path .= strlen($path) ? '/' . $path : '';

return $this->clean_path($app_path);
}
}
105 changes: 105 additions & 0 deletions tests/Commands/ClassMakeCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

namespace Nwidart\Modules\Tests\Commands;

use Nwidart\Modules\Contracts\RepositoryInterface;
use Nwidart\Modules\Tests\BaseTestCase;
use Spatie\Snapshots\MatchesSnapshots;

class ClassMakeCommandTest extends BaseTestCase
{
use MatchesSnapshots;

/**
* @var \Illuminate\Filesystem\Filesystem
*/
private $finder;
/**
* @var string
*/
private $modulePath;

public function setUp(): void
{
parent::setUp();
$this->finder = $this->app['files'];
$this->createModule();
$this->modulePath = $this->getModuleAppPath();

}

public function tearDown(): void
{
$this->app[RepositoryInterface::class]->delete('Blog');
parent::tearDown();
}

public function test_it_generates_a_new_class()
{
$code = $this->artisan('module:make-class', ['name' => 'Demo', 'module' => 'Blog']);

$this->assertTrue(is_file($this->modulePath . '/Classes/Demo.php'));
$this->assertSame(0, $code);
}

public function test_it_generates_a_new_class_can_override_with_force_option()
{
$this->artisan('module:make-class', ['name' => 'Demo', 'module' => 'Blog']);
$code = $this->artisan('module:make-class', ['name' => 'Demo', 'module' => 'Blog', '--force' => true]);

$this->assertTrue(is_file($this->modulePath . '/Classes/Demo.php'));
$this->assertSame(0, $code);
}

public function test_it_generates_a_new_class_can_use_invoke_option()
{
$code = $this->artisan('module:make-class', ['name' => 'Demo', 'module' => 'Blog', '--invokable' => true]);

$this->assertTrue(is_file($this->modulePath . '/Classes/Demo.php'));
$this->assertSame(0, $code);
}

public function test_it_generates_a_new_class_can_use_suffix_option()
{
$code = $this->artisan('module:make-class', ['name' => 'Demo', 'module' => 'Blog', '--suffix' => true]);

$this->assertTrue(is_file($this->modulePath . '/Classes/DemoClass.php'));
$this->assertSame(0, $code);
}

public function test_it_generates_a_new_class_use_type_option()
{
$code = $this->artisan('module:make-class', ['name' => 'Demo', 'module' => 'Blog', '--type' => 'contract']);

$this->assertTrue(is_file($this->modulePath . '/Contracts/Demo.php'));
$this->assertSame(0, $code);
}

public function test_it_generated_correct_file_with_content()
{
$code = $this->artisan('module:make-class', ['name' => 'Demo', 'module' => 'Blog']);

$file = $this->finder->get($this->modulePath . '/Classes/Demo.php');

$this->assertMatchesSnapshot($file);
$this->assertSame(0, $code);
}

public function test_it_can_generate_a_class_in_sub_namespace_in_correct_folder()
{
$code = $this->artisan('module:make-class', ['name' => 'Api\\Demo', 'module' => 'Blog']);

$this->assertTrue(is_file($this->modulePath . '/Classes/Api/Demo.php'));
$this->assertSame(0, $code);
}

public function test_it_can_generate_a_class_in_sub_namespace_with_correct_generated_file()
{
$code = $this->artisan('module:make-class', ['name' => 'Api\\Demo', 'module' => 'Blog']);

$file = $this->finder->get($this->modulePath . '/Classes/Api/Demo.php');

$this->assertMatchesSnapshot($file);
$this->assertSame(0, $code);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Modules\Blog\Classes\Api;

class Api\Demo
{
public function __construct()
{
//
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Modules\Blog\Classes;

class Demo
{
public function __construct()
{
//
}
}