Skip to content

Commit add5e25

Browse files
committed
Feat: added different commands
1 parent 0cea353 commit add5e25

29 files changed

+2065
-26
lines changed

src/Commands/CreateModuleCommand.php

+379
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,379 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CreativeCrafts\ModuleDesignCommands\Commands;
6+
7+
use Illuminate\Console\Command;
8+
use Illuminate\Contracts\Filesystem\FileNotFoundException;
9+
use Illuminate\Support\Facades\File;
10+
11+
use function Laravel\Prompts\error;
12+
use function Laravel\Prompts\info;
13+
use function Laravel\Prompts\outro;
14+
use function Laravel\Prompts\text;
15+
16+
class CreateModuleCommand extends Command
17+
{
18+
/**
19+
* The name and signature of the console command.
20+
*
21+
* @var string
22+
*/
23+
protected $signature = 'make:module';
24+
25+
/**
26+
* The console command description.
27+
*
28+
* @var string
29+
*/
30+
protected $description = 'Create a new module directory structure';
31+
32+
/**
33+
* Execute the console command.
34+
*
35+
* @throws FileNotFoundException
36+
*/
37+
public function handle(): int
38+
{
39+
$basePath = 'modules';
40+
$moduleName = text(
41+
label: 'What is the name of the module?',
42+
required: 'Module name is required.'
43+
);
44+
45+
info('Check if module exist ....');
46+
$modulePath = $basePath.'/'.ucfirst($moduleName);
47+
if (File::exists($modulePath)) {
48+
error('Module already exists!');
49+
50+
return 1;
51+
}
52+
info('Module does not exist. Creating module main directory ....');
53+
File::makeDirectory($modulePath, 0777, true, true);
54+
55+
$codeDirectoryNames = [
56+
'config',
57+
'routes',
58+
'database',
59+
'domain',
60+
'app',
61+
'tests',
62+
];
63+
64+
info('Creating the necessary files for each directories ....');
65+
foreach ($codeDirectoryNames as $directory) {
66+
File::makeDirectory($modulePath.'/'.$directory, 0777, true, true);
67+
}
68+
69+
info('Creating the module config file ....');
70+
$configFilePath = $modulePath.'/config/config.php';
71+
$configStubContent = file_get_contents($this->getConfigStub());
72+
if ($configStubContent === false) {
73+
error('Unable to read the config stub file.');
74+
75+
return 1;
76+
}
77+
File::put($configFilePath, $configStubContent);
78+
79+
info('Setting up the web route files ....');
80+
$webRoutePath = $modulePath.'/routes/web.php';
81+
$webRouteStubContent = file_get_contents($this->getRouteWebStub());
82+
if ($webRouteStubContent === false) {
83+
error('Unable to read the web route stub file.');
84+
85+
return 1;
86+
}
87+
File::put($webRoutePath, $webRouteStubContent);
88+
89+
info('Setting up the api route files ....');
90+
$apiRoutePath = $modulePath.'/routes/api.php';
91+
$apiRouteStubContent = file_get_contents($this->getRouteApiStub());
92+
if ($apiRouteStubContent === false) {
93+
error('Unable to read the api route stub file.');
94+
95+
return 1;
96+
}
97+
File::put($apiRoutePath, $apiRouteStubContent);
98+
99+
info('Creating the module database directory ....');
100+
$databasePath = $modulePath.'/database';
101+
$databaseDirectoryNames = [
102+
'factories',
103+
'migrations',
104+
'seeders',
105+
];
106+
foreach ($databaseDirectoryNames as $dbDirectories) {
107+
File::makeDirectory($databasePath.'/'.$dbDirectories, 0777, true, true);
108+
}
109+
110+
info('Creating the domain directory....');
111+
$domainPath = $modulePath.'/domain';
112+
$domainDirectoryNames = [
113+
'Actions',
114+
'Aggregates',
115+
'Collections',
116+
'Contracts',
117+
'DataFactories',
118+
'DataTransferObjects',
119+
'Queries',
120+
'QueryBuilders',
121+
];
122+
foreach ($domainDirectoryNames as $domainDirectories) {
123+
File::makeDirectory($domainPath.'/'.$domainDirectories, 0777, true, true);
124+
}
125+
126+
info('Creating the module test directory ....');
127+
$testPath = $modulePath.'/tests';
128+
$testDirectoryNames = [
129+
'ArchTest',
130+
'Feature',
131+
'Unit',
132+
];
133+
foreach ($testDirectoryNames as $testDirectories) {
134+
File::makeDirectory($testPath.'/'.$testDirectories, 0777, true, true);
135+
}
136+
137+
$includeProcessManagerDirectory = text(
138+
label: 'Do you want to include a process manager directory? yes/no',
139+
required: 'Required.'
140+
);
141+
142+
if ($includeProcessManagerDirectory === 'yes') {
143+
info('Creating the process manager directory....');
144+
$processManagerPath = $domainPath.'/Processes';
145+
File::makeDirectory($processManagerPath, 0777, true, true);
146+
} else {
147+
info('Skipping Process manager directory...');
148+
}
149+
150+
info('Creating the module source directory ....');
151+
$srcPath = $modulePath.'/app';
152+
info('Adding module app path to autoload in composer.json ....');
153+
154+
if (app()->environment() === 'development') {
155+
$composerFilePath = app()->basePath().'/composer.json';
156+
$composerFileContent = File::get($composerFilePath);
157+
// Parse the JSON into an array
158+
/** @var array<int, string> $composerArray */
159+
$composerArray = json_decode($composerFileContent, true);
160+
// Get the PSR-4 autoload section from the array
161+
$psr4Autoload = fluent($composerArray)->scope('autoload.psr-4')->toArray();
162+
// app path
163+
$path = 'modules/'.ucfirst($moduleName).'/app';
164+
$key = 'Modules\\'.ucfirst($moduleName).'\\App\\';
165+
// Add the path to the PSR-4 autoload section
166+
$psr4Autoload[$key] = $path;
167+
// domain path
168+
$domainPath = 'modules/'.ucfirst($moduleName).'/domain';
169+
$domainKey = 'Modules\\'.ucfirst($moduleName).'\\Domain\\';
170+
// Add the path to the PSR-4 autoload section
171+
$psr4Autoload[$domainKey] = $domainPath;
172+
// Overwrite the PSR-4 autoload section in the array with the updated array
173+
$composerArray['autoload']['psr-4'] = $psr4Autoload;
174+
// Convert the updated array back to JSON and write it to the composer.json file
175+
/** @var string $newComposerContents */
176+
$newComposerContents = json_encode($composerArray, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
177+
File::put($composerFilePath, $newComposerContents);
178+
179+
info('Running composer dump-autoload ....');
180+
exec('composer dump-autoload');
181+
}
182+
183+
$srcDirectoryNames = [
184+
'Exceptions',
185+
'Http',
186+
'Models',
187+
'Providers',
188+
];
189+
foreach ($srcDirectoryNames as $srcDirectories) {
190+
File::makeDirectory($srcPath.'/'.$srcDirectories, 0777, true, true);
191+
}
192+
193+
$includeEventDirectory = text(
194+
label: 'Do you want to include an events directory? yes/no',
195+
required: 'Required.'
196+
);
197+
198+
if ($includeEventDirectory === 'yes') {
199+
info('Creating the module event directory ....');
200+
$eventPath = $srcPath.'/Events';
201+
File::makeDirectory($eventPath, 0777, true, true);
202+
} else {
203+
info('Skipping Events directory...');
204+
}
205+
206+
$includeListenersDirectory = text(
207+
label: 'Do you want to include an event listeners directory? yes/no',
208+
required: 'Required.'
209+
);
210+
211+
if ($includeListenersDirectory === 'yes') {
212+
info('Creating the module event listeners directory ....');
213+
$listenersPath = $srcPath.'/Listeners';
214+
File::makeDirectory($listenersPath, 0777, true, true);
215+
} else {
216+
info('Skipping event Listeners directory...');
217+
}
218+
219+
info('Creating the module Http directory ....');
220+
$httpPath = $srcPath.'/Http';
221+
$httpDirectoryNames = [
222+
'Controllers',
223+
'Requests',
224+
'Resources',
225+
];
226+
foreach ($httpDirectoryNames as $httpDirectories) {
227+
File::makeDirectory($httpPath.'/'.$httpDirectories, 0777, true, true);
228+
}
229+
230+
$includeMiddlewareDirectory = text(
231+
label: 'Do you want to include a middleware directory? yes/no',
232+
required: 'Required.'
233+
);
234+
235+
if ($includeMiddlewareDirectory === 'yes') {
236+
info('Creating the module middleware directory ....');
237+
$middlewarePath = $httpPath.'/Middleware';
238+
File::makeDirectory($middlewarePath, 0777, true, true);
239+
} else {
240+
info('Skipping Middleware directory...');
241+
}
242+
243+
$providerNamespace = $this->getNamespace($moduleName, 'Providers');
244+
245+
info('Setting up the route service provider ....');
246+
$routeServiceProviderPath = $srcPath.'/Providers/RouteServiceProvider.php';
247+
$routeServiceProviderStubContent = file_get_contents($this->getRouteServiceProviderStub());
248+
if ($routeServiceProviderStubContent === false) {
249+
error('Unable to read the route service provider stub file.');
250+
251+
return 1;
252+
}
253+
$this->configureStub(
254+
$routeServiceProviderStubContent,
255+
$providerNamespace,
256+
'RouteServiceProvider',
257+
$routeServiceProviderPath,
258+
strtolower($moduleName)
259+
);
260+
261+
info('Setting up the event service provider ....');
262+
$eventServiceProviderPath = $srcPath.'/Providers/EventServiceProvider.php';
263+
$eventServiceProviderStubContent = file_get_contents($this->getEventServiceProviderStub());
264+
if ($eventServiceProviderStubContent === false) {
265+
error('Unable to read the event service provider stub file.');
266+
267+
return 1;
268+
}
269+
$this->configureStub(
270+
$eventServiceProviderStubContent,
271+
$providerNamespace,
272+
'EventServiceProvider',
273+
$eventServiceProviderPath,
274+
strtolower($moduleName)
275+
);
276+
277+
info('Setting up the module service provider ....');
278+
$moduleServiceProviderPath = $srcPath.'/Providers/'.$moduleName.'ServiceProvider.php';
279+
$moduleServiceProviderStubContent = file_get_contents($this->getModuleServiceProviderStub());
280+
if ($moduleServiceProviderStubContent === false) {
281+
error('Unable to read the module service provider stub file.');
282+
283+
return 1;
284+
}
285+
$this->configureStub(
286+
$moduleServiceProviderStubContent,
287+
$providerNamespace,
288+
$moduleName.'ServiceProvider',
289+
$moduleServiceProviderPath,
290+
strtolower($moduleName)
291+
);
292+
info('Registering the module service provider in the bootstrap app file ....');
293+
if (app()->environment() === 'development') {
294+
$providerNamespace = $this->getNamespace($moduleName, 'Providers');
295+
$appConfigPath = app()->basePath().'/bootstrap/app.php';
296+
$appConfigContent = File::get($appConfigPath);
297+
$newProvider = PHP_EOL.'\\'.$providerNamespace.'\\'.$moduleName.'ServiceProvider::class,';
298+
$needle = 'withProviders([';
299+
// Find the position where to insert the new provider
300+
$withProviderPosition = strrpos(
301+
$appConfigContent,
302+
$needle
303+
);
304+
if ($withProviderPosition !== false) {
305+
// Insert the new provider just before the last closing bracket
306+
$appConfigContent = substr_replace(
307+
$appConfigContent,
308+
$newProvider,
309+
$withProviderPosition + strlen($needle),
310+
0
311+
);
312+
File::put($appConfigPath, $appConfigContent);
313+
outro(
314+
'Registration of the module service provider in the bootstrap app file was successful. Please check it and format file.'
315+
);
316+
} else {
317+
error('Unable to find the position to insert the new provider.');
318+
319+
return 1;
320+
}
321+
}
322+
323+
outro('Module : ['.$moduleName.']'.' created successfully.');
324+
325+
return 0;
326+
}
327+
328+
protected function configureStub(string $stubContent, string $namespace, string $className, string $filePath, ?string $key): int
329+
{
330+
$stubContent = str_replace(
331+
['{{ namespace }}', '{{ class }}', '{{ key }}'],
332+
[$namespace, $className, $key],
333+
$stubContent,
334+
);
335+
File::put($filePath, $stubContent);
336+
337+
return 0;
338+
}
339+
340+
protected function getConfigStub(): string
341+
{
342+
return app()->basePath().'/stubs/module-config.stub';
343+
}
344+
345+
protected function getRouteServiceProviderStub(): string
346+
{
347+
return app()->basePath().'/stubs/module-route-service-provider.stub';
348+
}
349+
350+
protected function getEventServiceProviderStub(): string
351+
{
352+
return app()->basePath().'/stubs/module-event-service-provider.stub';
353+
}
354+
355+
protected function getModuleServiceProviderStub(): string
356+
{
357+
return app()->basePath().'/stubs/module-service-provider.stub';
358+
}
359+
360+
protected function getRouteWebStub(): string
361+
{
362+
return app()->basePath().'/stubs/module-route-web.stub';
363+
}
364+
365+
protected function getRouteApiStub(): string
366+
{
367+
return app()->basePath().'/stubs/module-route-api.stub';
368+
}
369+
370+
protected function getNamespace(string $rootNamespace, string $directoryName): string
371+
{
372+
// check if the root namespace is a nested directory
373+
if (str_contains($rootNamespace, '/')) {
374+
$rootNamespace = str_replace('/', '\\', $rootNamespace);
375+
}
376+
377+
return 'Modules\\'.ucfirst($rootNamespace).'\\App\\'.$directoryName;
378+
}
379+
}

0 commit comments

Comments
 (0)