Skip to content

Commit

Permalink
Merge pull request #1802 from solomon-ochepa/module-make-class-command
Browse files Browse the repository at this point in the history
Create module:make-class command
  • Loading branch information
dcblogdev committed May 12, 2024
2 parents 4522002 + 50291d1 commit 7c6bda0
Show file tree
Hide file tree
Showing 11 changed files with 274 additions and 9 deletions.
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()
{
//
}
}

0 comments on commit 7c6bda0

Please sign in to comment.