Skip to content

Commit dd1a2cd

Browse files
Add GeneratePackageCommand
1 parent c34b0ce commit dd1a2cd

File tree

3 files changed

+292
-0
lines changed

3 files changed

+292
-0
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
<?php
2+
3+
namespace PlinCode\LaravelCleanArchitecture\Commands;
4+
5+
use Illuminate\Console\Command;
6+
use Illuminate\Filesystem\Filesystem;
7+
use Illuminate\Support\Str;
8+
9+
class GeneratePackageCommand extends Command
10+
{
11+
protected $signature = 'clean-arch:generate-package {name : The name of the package}
12+
{vendor : The vendor name}
13+
{--path= : Custom path for the package}
14+
{--force : Overwrite existing files}';
15+
16+
protected $description = 'Generate a new Laravel package with Clean Architecture structure';
17+
18+
protected Filesystem $files;
19+
20+
public function __construct(Filesystem $files)
21+
{
22+
parent::__construct();
23+
$this->files = $files;
24+
}
25+
26+
public function handle(): int
27+
{
28+
$packageName = $this->argument('name');
29+
$vendor = $this->argument('vendor');
30+
$customPath = $this->option('path');
31+
$force = $this->option('force');
32+
33+
$this->info("🚀 Generating package: {$vendor}/{$packageName}");
34+
35+
$path = $customPath ?: base_path("packages/{$vendor}/{$packageName}");
36+
$studlyName = Str::studly($packageName);
37+
$namespace = Str::studly($vendor) . '\\' . $studlyName;
38+
39+
$this->createPackageStructure($path, $packageName, $studlyName, $namespace, $vendor);
40+
41+
$this->info("✅ Package {$vendor}/{$packageName} generated successfully!");
42+
$this->info("Package location: {$path}");
43+
$this->newLine();
44+
$this->info('Next steps:');
45+
$this->info('1. Add the package to your composer.json repositories');
46+
$this->info("2. Run: composer require {$vendor}/{$packageName}");
47+
48+
return self::SUCCESS;
49+
}
50+
51+
protected function createPackageStructure(string $path, string $packageName, string $studlyName, string $namespace, string $vendor): void
52+
{
53+
// Create directories
54+
$directories = [
55+
'src',
56+
'src/Commands',
57+
'stubs',
58+
'config',
59+
'tests',
60+
'tests/Feature',
61+
'tests/Unit',
62+
];
63+
64+
foreach ($directories as $directory) {
65+
$fullPath = "{$path}/{$directory}";
66+
if (! $this->files->isDirectory($fullPath)) {
67+
$this->files->makeDirectory($fullPath, 0755, true);
68+
$this->info("Created directory: {$directory}");
69+
}
70+
}
71+
72+
// Create files
73+
$this->createPackageComposer($path, $packageName, $studlyName, $namespace, $vendor);
74+
$this->createPackageServiceProvider($path, $studlyName, $namespace);
75+
$this->createPackageModel($path, $studlyName, $namespace);
76+
$this->createPackageService($path, $studlyName, $namespace);
77+
$this->createPackageReadme($path, $packageName, $studlyName, $vendor);
78+
}
79+
80+
protected function createPackageComposer(string $path, string $packageName, string $studlyName, string $namespace, string $vendor): void
81+
{
82+
$content = json_encode([
83+
'name' => "{$vendor}/{$packageName}",
84+
'description' => "Laravel package for {$studlyName}",
85+
'type' => 'library',
86+
'license' => 'MIT',
87+
'authors' => [
88+
[
89+
'name' => 'Your Name',
90+
'email' => 'your.email@example.com',
91+
],
92+
],
93+
'require' => [
94+
'php' => '^8.3',
95+
'illuminate/support' => '^11.0|^12.0',
96+
'illuminate/console' => '^11.0|^12.0',
97+
'illuminate/filesystem' => '^11.0|^12.0',
98+
],
99+
'autoload' => [
100+
'psr-4' => [
101+
str_replace('\\', '\\\\', $namespace) . '\\' => 'src/',
102+
],
103+
],
104+
'autoload-dev' => [
105+
'psr-4' => [
106+
str_replace('\\', '\\\\', $namespace) . '\\Tests\\' => 'tests/',
107+
],
108+
],
109+
'extra' => [
110+
'laravel' => [
111+
'providers' => [
112+
str_replace('\\', '\\\\', $namespace) . '\\' . $studlyName . 'ServiceProvider',
113+
],
114+
],
115+
],
116+
'minimum-stability' => 'stable',
117+
'prefer-stable' => true,
118+
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
119+
120+
$this->files->put("{$path}/composer.json", $content);
121+
$this->info('Created: composer.json');
122+
}
123+
124+
protected function createPackageServiceProvider(string $path, string $studlyName, string $namespace): void
125+
{
126+
$stub = $this->getStub('package-service-provider');
127+
$content = str_replace(
128+
['{{Namespace}}', '{{StudlyName}}'],
129+
[$namespace, $studlyName],
130+
$stub
131+
);
132+
133+
$this->files->put("{$path}/src/{$studlyName}ServiceProvider.php", $content);
134+
$this->info("Created: src/{$studlyName}ServiceProvider.php");
135+
}
136+
137+
protected function createPackageModel(string $path, string $studlyName, string $namespace): void
138+
{
139+
$stub = $this->getStub('package-model');
140+
$content = str_replace(
141+
['{{Namespace}}', '{{StudlyName}}'],
142+
[$namespace, $studlyName],
143+
$stub
144+
);
145+
146+
$this->files->put("{$path}/src/{$studlyName}.php", $content);
147+
$this->info("Created: src/{$studlyName}.php");
148+
}
149+
150+
protected function createPackageService(string $path, string $studlyName, string $namespace): void
151+
{
152+
$stub = $this->getStub('package-service');
153+
$content = str_replace(
154+
['{{Namespace}}', '{{StudlyName}}'],
155+
[$namespace, $studlyName],
156+
$stub
157+
);
158+
159+
$this->files->put("{$path}/src/{$studlyName}Service.php", $content);
160+
$this->info("Created: src/{$studlyName}Service.php");
161+
}
162+
163+
protected function createPackageReadme(string $path, string $packageName, string $studlyName, string $vendor): void
164+
{
165+
$stub = $this->getStub('package-readme');
166+
$content = str_replace(
167+
['{{PackageName}}', '{{StudlyName}}', '{{VendorName}}'],
168+
[$packageName, $studlyName, $vendor],
169+
$stub
170+
);
171+
172+
$this->files->put("{$path}/README.md", $content);
173+
$this->info('Created: README.md');
174+
}
175+
176+
protected function getStub(string $stub): string
177+
{
178+
$stubPath = __DIR__ . "/../../stubs/{$stub}.stub";
179+
180+
if (! $this->files->exists($stubPath)) {
181+
throw new \Exception("Stub file not found: {$stubPath}");
182+
}
183+
184+
return $this->files->get($stubPath);
185+
}
186+
}

tests/Unit/CommandsTest.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
use Illuminate\Console\Command;
4+
use Illuminate\Support\Facades\Artisan;
5+
6+
describe('Commands', function () {
7+
it('can execute install command without errors', function () {
8+
$exitCode = Artisan::call('clean-arch:install', ['--help' => true]);
9+
10+
expect($exitCode)->toBe(0);
11+
});
12+
13+
it('shows help for make domain command', function () {
14+
$exitCode = Artisan::call('clean-arch:make-domain', ['--help' => true]);
15+
16+
expect($exitCode)->toBe(0);
17+
});
18+
19+
it('shows help for make action command', function () {
20+
$exitCode = Artisan::call('clean-arch:make-action', ['--help' => true]);
21+
22+
expect($exitCode)->toBe(0);
23+
});
24+
25+
it('shows help for make service command', function () {
26+
$exitCode = Artisan::call('clean-arch:make-service', ['--help' => true]);
27+
28+
expect($exitCode)->toBe(0);
29+
});
30+
31+
it('shows help for make controller command', function () {
32+
$exitCode = Artisan::call('clean-arch:make-controller', ['--help' => true]);
33+
34+
expect($exitCode)->toBe(0);
35+
});
36+
37+
it('shows help for generate package command', function () {
38+
$exitCode = Artisan::call('clean-arch:generate-package', ['--help' => true]);
39+
40+
expect($exitCode)->toBe(0);
41+
});
42+
});
43+
44+
describe('Command Registration', function () {
45+
it('registers all commands in artisan', function () {
46+
$commands = Artisan::all();
47+
48+
expect($commands)
49+
->toHaveKey('clean-arch:install')
50+
->toHaveKey('clean-arch:make-domain')
51+
->toHaveKey('clean-arch:make-action')
52+
->toHaveKey('clean-arch:make-service')
53+
->toHaveKey('clean-arch:make-controller')
54+
->toHaveKey('clean-arch:generate-package');
55+
});
56+
57+
it('has command instances that extend laravel command class', function () {
58+
$commands = Artisan::all();
59+
60+
expect($commands['clean-arch:install'])
61+
->toBeInstanceOf(Command::class);
62+
});
63+
});

tests/Unit/ServiceProviderTest.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
use Illuminate\Support\Facades\Artisan;
4+
use PlinCode\LaravelCleanArchitecture\CleanArchitectureServiceProvider;
5+
6+
describe('Service Provider', function () {
7+
it('registers install command', function () {
8+
expect(Artisan::all())
9+
->toHaveKey('clean-arch:install');
10+
});
11+
12+
it('registers make domain command', function () {
13+
expect(Artisan::all())
14+
->toHaveKey('clean-arch:make-domain');
15+
});
16+
17+
it('registers make action command', function () {
18+
expect(Artisan::all())
19+
->toHaveKey('clean-arch:make-action');
20+
});
21+
22+
it('registers make service command', function () {
23+
expect(Artisan::all())
24+
->toHaveKey('clean-arch:make-service');
25+
});
26+
27+
it('registers make controller command', function () {
28+
expect(Artisan::all())
29+
->toHaveKey('clean-arch:make-controller');
30+
});
31+
32+
it('registers generate package command', function () {
33+
expect(Artisan::all())
34+
->toHaveKey('clean-arch:generate-package');
35+
});
36+
37+
it('is registered in the application', function () {
38+
$providers = $this->app->getLoadedProviders();
39+
40+
expect($providers)
41+
->toHaveKey(CleanArchitectureServiceProvider::class);
42+
});
43+
});

0 commit comments

Comments
 (0)