├── .gitignore
├── LICENSE
├── composer.json
├── phpunit.xml
├── readme.md
├── src
├── Commands
│ ├── Custom
│ │ └── MakeModuleRepo.php
│ ├── Graphql
│ │ ├── MakeModuleGraphqlMutation.php
│ │ ├── MakeModuleGraphqlQuery.php
│ │ └── MakeModuleGraphqlType.php
│ ├── Laravel
│ │ ├── MakeModuleController.php
│ │ ├── MakeModuleEvent.php
│ │ ├── MakeModuleException.php
│ │ ├── MakeModuleJob.php
│ │ ├── MakeModuleListener.php
│ │ ├── MakeModuleMigration.php
│ │ ├── MakeModuleModel.php
│ │ ├── MakeModuleNotification.php
│ │ ├── MakeModulePolicy.php
│ │ ├── MakeModuleRequest.php
│ │ ├── MakeModuleResource.php
│ │ ├── MakeModuleRule.php
│ │ └── MakeModuleSeeder.php
│ └── MakeModule.php
├── Exceptions
│ └── File
│ │ ├── FileIsNotWritableException.php
│ │ └── FileNotExistException.php
├── Helpers
│ ├── ComposerParser.php
│ ├── File.php
│ └── ModuleHandler.php
├── LaravelModuleCreatorServiceProvider.php
├── Stubs
│ ├── Graphql
│ │ ├── mutation.stub
│ │ ├── query.stub
│ │ └── type.stub
│ ├── Providers
│ │ └── ServiceProvider.stub
│ ├── Repo.stub
│ ├── Routes.stub
│ └── composer.stub
└── Traits
│ ├── GraphqlCommand.php
│ └── RequireModule.php
└── tests
└── Unit
├── ComposerParserTest.php
├── FileTest.php
└── ModuleHandlerTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | .idea/
3 | composer.lock
4 | .phpunit.result.cache
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Hesam Mousavi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hesammousavi/laravel-module-creator",
3 | "description": "a tool for build your laravel modules",
4 | "license": "MIT",
5 | "homepage": "https://github.com/Hesammousavi/laravel-module-creator",
6 | "authors": [
7 | {
8 | "name": "Hesam Mousavi",
9 | "email": "hesammoousavi@gmail.com"
10 | }
11 | ],
12 | "keywords": ["Laravel Module" , "Laravel" , "Module Creator"],
13 | "scripts": {
14 | "test": "./vendor/bin/phpunit"
15 | },
16 | "autoload": {
17 | "psr-4": {
18 | "Hesammousavi\\LaravelModuleCreator\\": "src/"
19 | }
20 | },
21 | "autoload-dev": {
22 | "psr-4": {
23 | "Tests\\": "tests/"
24 | }
25 | },
26 | "extra": {
27 | "laravel": {
28 | "providers": [
29 | "Hesammousavi\\LaravelModuleCreator\\LaravelModuleCreatorServiceProvider"
30 | ]
31 | }
32 | },
33 | "require": {
34 | "illuminate/support": "^9.0 || ^10.0 || ^11.0 || ^12.0 ",
35 | "ext-json": "*"
36 | },
37 | "require-dev": {
38 | "roave/security-advisories": "dev-latest",
39 | "phpunit/phpunit": "^9.5"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | ./tests/Unit
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | Laravel package for Create Laravel Modules from a template
2 |
3 | # Requirements
4 | Laravel 9 or later
5 | PHP 8.0 or later
6 |
7 | # Install
8 |
9 | You can install the package via composer:
10 | ```bash
11 | composer require hesammousavi/laravel-module-creator
12 | ```
13 |
14 | # Usage
15 |
16 | first you must create your module
17 |
18 | ```bash
19 | php artisan m:make Roocket/User
20 | ```
21 |
22 | finally, run this code :
23 |
24 | ```
25 | composer update
26 | ```
27 |
28 | beautifully done
29 |
30 |
31 | # Your Module Commands
32 |
33 | you have different commands to do anything with your module
34 | ```bash
35 | php artisan
36 | ```
37 |
38 | you can see these commands for your usage
39 |
40 | ```
41 | module
42 | m:make create a new module to develop project
43 | m:make:controller Create a new controller class
44 | m:make:request Create a new request class
45 | m:make:resource Create a new resource class
46 | m:make:graphql-mutation Create a new Graphql Mutation class
47 | m:make:graphql-query Create a new Graphql Query class
48 | m:make:graphql-type Create a new Graphql Type class
49 | m:make:migration Create a new migration file
50 | m:make:model Create a new Eloquent model class
51 | m:make:repo Create a new repo class
52 | m:make:seeder Create a new Seeder class
53 | m:make:rule Create a new Rule Validation class
54 | m:make:event Create a new Event class
55 | m:make:listener Create a new Listener class
56 | ```
57 |
58 |
59 | you can build model for your module like
60 |
61 | ```bash
62 | php artisan m:make:model
63 | php artisan m:make:model Roocket/user User
64 | ```
65 |
--------------------------------------------------------------------------------
/src/Commands/Custom/MakeModuleRepo.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
54 |
55 | return base_path("modules/{$this->argument('module')}/src/Database/Repo") . '/' . str_replace('\\', '/', $name) . '.php';
56 | }
57 |
58 | protected function rootNamespace()
59 | {
60 | return str_replace('/', '\\', $this->argument('module')) . '\Database\Repo';
61 | }
62 |
63 | protected function getStub()
64 | {
65 | return __DIR__ . '/../../Stubs/Repo.stub';
66 | }
67 |
68 | /**
69 | * Get the console command arguments.
70 | *
71 | * @return array
72 | */
73 | protected function getArguments()
74 | {
75 | return [
76 | ['module', InputArgument::REQUIRED, 'the name of the module'],
77 | ['name', InputArgument::REQUIRED, 'The name of the class'],
78 | ];
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Commands/Graphql/MakeModuleGraphqlMutation.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
51 |
52 | return base_path("modules/{$this->argument('module')}/src/Graphql/Mutations") . '/' . str_replace('\\', '/', $name) . '.php';
53 | }
54 |
55 |
56 | protected function rootNamespace()
57 | {
58 | return str_replace('/', '\\', $this->argument('module')) . '\Graphql\Mutations';
59 | }
60 |
61 | protected function getStub()
62 | {
63 | return __DIR__ . '/../../Stubs/Graphql/mutation.stub';
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Commands/Graphql/MakeModuleGraphqlQuery.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
52 |
53 | return base_path("modules/{$this->argument('module')}/src/Graphql/Queries") . '/' . str_replace('\\', '/', $name) . '.php';
54 | }
55 |
56 |
57 | protected function rootNamespace()
58 | {
59 | return str_replace('/', '\\', $this->argument('module')) . '\Graphql\Queries';
60 | }
61 |
62 | protected function getStub()
63 | {
64 | return __DIR__ . '/../../Stubs/Graphql/query.stub';
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Commands/Graphql/MakeModuleGraphqlType.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
51 |
52 | return base_path("modules/{$this->argument('module')}/src/Graphql/Types") . '/' . str_replace('\\', '/', $name) . '.php';
53 | }
54 |
55 |
56 | protected function rootNamespace()
57 | {
58 | return str_replace('/', '\\', $this->argument('module')) . '\Graphql\Types';
59 | }
60 |
61 | protected function getStub()
62 | {
63 | return __DIR__ . '/../../Stubs/Graphql/type.stub';
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Commands/Laravel/MakeModuleController.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
54 |
55 | return base_path("modules/{$this->argument('module')}/src") . '/' . str_replace('\\', '/', $name) . '.php';
56 | }
57 |
58 |
59 | protected function rootNamespace()
60 | {
61 | return str_replace('/', '\\', $this->argument('module'));
62 | }
63 |
64 | /**
65 | * Generate the form requests for the given model and classes.
66 | *
67 | * @param string $modelClass
68 | * @param string $storeRequestClass
69 | * @param string $updateRequestClass
70 | * @return array
71 | */
72 | protected function generateFormRequests($modelClass, $storeRequestClass, $updateRequestClass)
73 | {
74 | $storeRequestClass = 'Store'.class_basename($modelClass).'Request';
75 |
76 | $this->call('m:make:request', [
77 | 'module' => $this->argument('module'),
78 | 'name' => $storeRequestClass,
79 | ]);
80 |
81 | $updateRequestClass = 'Update'.class_basename($modelClass).'Request';
82 |
83 | $this->call('m:make:request', [
84 | 'module' => $this->argument('module'),
85 | 'name' => $storeRequestClass,
86 | ]);
87 |
88 | return [$storeRequestClass, $updateRequestClass];
89 | }
90 |
91 | /**
92 | * Get the console command arguments.
93 | *
94 | * @return array
95 | */
96 | protected function getArguments()
97 | {
98 | return [
99 | ['module', InputArgument::REQUIRED, 'the name of the module'],
100 | ['name', InputArgument::REQUIRED, 'The name of the class'],
101 | ];
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/Commands/Laravel/MakeModuleEvent.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
53 |
54 | return base_path("modules/{$this->argument('module')}/src") . '/' . str_replace('\\', '/', $name) . '.php';
55 | }
56 |
57 |
58 | protected function rootNamespace()
59 | {
60 | return str_replace('/', '\\', $this->argument('module'));
61 | }
62 |
63 | /**
64 | * Get the console command arguments.
65 | *
66 | * @return array
67 | */
68 | protected function getArguments()
69 | {
70 | return [
71 | ['module', InputArgument::REQUIRED, 'the name of the module'],
72 | ['name', InputArgument::REQUIRED, 'The name of the class'],
73 | ];
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Commands/Laravel/MakeModuleException.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
54 |
55 | return base_path("modules/{$this->argument('module')}/src") . '/' . str_replace('\\', '/', $name) . '.php';
56 | }
57 |
58 |
59 | protected function rootNamespace()
60 | {
61 | return str_replace('/', '\\', $this->argument('module'));
62 | }
63 |
64 | /**
65 | * Get the console command arguments.
66 | *
67 | * @return array
68 | */
69 | protected function getArguments()
70 | {
71 | return [
72 | ['module', InputArgument::REQUIRED, 'the name of the module'],
73 | ['name', InputArgument::REQUIRED, 'The name of the class'],
74 | ];
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Commands/Laravel/MakeModuleJob.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
54 |
55 | return base_path("modules/{$this->argument('module')}/src") . '/' . str_replace('\\', '/', $name) . '.php';
56 | }
57 |
58 |
59 | protected function rootNamespace()
60 | {
61 | return str_replace('/', '\\', $this->argument('module'));
62 | }
63 |
64 | /**
65 | * Get the console command arguments.
66 | *
67 | * @return array
68 | */
69 | protected function getArguments()
70 | {
71 | return [
72 | ['module', InputArgument::REQUIRED, 'the name of the module'],
73 | ['name', InputArgument::REQUIRED, 'The name of the class'],
74 | ];
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Commands/Laravel/MakeModuleListener.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
53 |
54 | return base_path("modules/{$this->argument('module')}/src") . '/' . str_replace('\\', '/', $name) . '.php';
55 | }
56 |
57 |
58 | protected function rootNamespace()
59 | {
60 | return str_replace('/', '\\', $this->argument('module'));
61 | }
62 |
63 | /**
64 | * Get the console command arguments.
65 | *
66 | * @return array
67 | */
68 | protected function getArguments()
69 | {
70 | return [
71 | ['module', InputArgument::REQUIRED, 'the name of the module'],
72 | ['name', InputArgument::REQUIRED, 'The name of the class'],
73 | ];
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Commands/Laravel/MakeModuleMigration.php:
--------------------------------------------------------------------------------
1 | composer = $composer;
58 | $this->file = $filesystem;
59 | }
60 |
61 | public function handle()
62 | {
63 | $this->checkModuleExists();
64 |
65 | // It's possible for the developer to specify the tables to modify in this
66 | // schema operation. The developer may also specify if this table needs
67 | // to be freshly created so we can create the appropriate migrations.
68 | $name = Str::snake(trim($this->input->getArgument('name')));
69 |
70 | $table = $this->input->getOption('table');
71 |
72 | $create = $this->input->getOption('create') ?: false;
73 |
74 | // If no table was given as an option but a create option is given then we
75 | // will use the "create" option as the table name. This allows the devs
76 | // to pass a table name into this option as a short-cut for creating.
77 | if (! $table && is_string($create)) {
78 | $table = $create;
79 |
80 | $create = true;
81 | }
82 |
83 | // Next, we will attempt to guess the table name if this the migration has
84 | // "create" in the name. This will allow us to provide a convenient way
85 | // of creating migrations that create new tables for the application.
86 | if (! $table) {
87 | [$table, $create] = TableGuesser::guess($name);
88 | }
89 |
90 | // Now we are ready to write the migration out to disk. Once we've written
91 | // the migration out, we will dump-autoload for the entire framework to
92 | // make sure that the migrations are registered by the class loaders.
93 | $this->writeMigration($name, $table, $create);
94 |
95 | $this->composer->dumpAutoloads();
96 | }
97 |
98 | /**
99 | * Write the migration file to disk.
100 | *
101 | * @param string $name
102 | * @param string $table
103 | * @param bool $create
104 | * @print string
105 | */
106 | protected function writeMigration($name, $table, $create)
107 | {
108 | $creator = new MigrationCreator($this->file , '');
109 |
110 | $file = $creator->create(
111 | $name, $this->getMigrationPath(), $table, $create
112 | );
113 |
114 | $this->line("Created Migration: {$file}");
115 | }
116 |
117 | protected function getMigrationPath()
118 | {
119 | if (! is_null($targetPath = $this->input->getOption('path'))) {
120 | return $this->laravel->basePath().'/'.$targetPath;
121 | }
122 |
123 | return base_path("modules/{$this->argument('module')}/src/Database/Migrations");
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/src/Commands/Laravel/MakeModuleModel.php:
--------------------------------------------------------------------------------
1 | argument('name'))));
53 |
54 | if ($this->option('pivot')) {
55 | $table = Str::singular($table);
56 | }
57 |
58 | $this->call(MakeModuleMigration::class, [
59 | 'module' => $this->argument('module'),
60 | 'name' => "create_{$table}_table",
61 | '--create' => $table,
62 | ]);
63 | }
64 |
65 | /**
66 | * Create a controller for the model.
67 | *
68 | * @return void
69 | */
70 | protected function createController()
71 | {
72 | $controller = Str::studly(class_basename($this->argument('name')));
73 |
74 | $modelName = $this->qualifyClass($this->getNameInput());
75 |
76 | $this->call(MakeModuleController::class, array_filter([
77 | 'name' => "{$controller}Controller",
78 | 'module' => $this->argument('module'),
79 | '--model' => $this->option('resource') || $this->option('api') ? $modelName : null,
80 | '--api' => $this->option('api'),
81 | '--requests' => $this->option('requests') || $this->option('all'),
82 | ]));
83 | }
84 |
85 | /**
86 | * Get the destination class path.
87 | *
88 | * @param string $name
89 | * @return string
90 | */
91 | protected function getPath($name)
92 | {
93 | $name = Str::replaceFirst($this->rootNamespace(), '', $name);
94 |
95 | return base_path("modules/{$this->argument('module')}/src") . '/' . str_replace('\\', '/', $name) . '.php';
96 | }
97 |
98 |
99 | protected function rootNamespace()
100 | {
101 | return str_replace('/', '\\', $this->argument('module'));
102 | }
103 |
104 | /**
105 | * Get the console command arguments.
106 | *
107 | * @return array
108 | */
109 | protected function getArguments()
110 | {
111 | return [
112 | ['module', InputArgument::REQUIRED, 'the name of the module'],
113 | ['name', InputArgument::REQUIRED, 'The name of the class'],
114 | ];
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/Commands/Laravel/MakeModuleNotification.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
54 |
55 | return base_path("modules/{$this->argument('module')}/src") . '/' . str_replace('\\', '/', $name) . '.php';
56 | }
57 |
58 |
59 | protected function rootNamespace()
60 | {
61 | return str_replace('/', '\\', $this->argument('module'));
62 | }
63 |
64 | /**
65 | * Get the console command arguments.
66 | *
67 | * @return array
68 | */
69 | protected function getArguments()
70 | {
71 | return [
72 | ['module', InputArgument::REQUIRED, 'the name of the module'],
73 | ['name', InputArgument::REQUIRED, 'The name of the class'],
74 | ];
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Commands/Laravel/MakeModulePolicy.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
48 |
49 | return base_path("modules/{$this->argument('module')}/src") . '/' . str_replace('\\', '/', $name) . '.php';
50 | }
51 |
52 |
53 | protected function rootNamespace()
54 | {
55 | return str_replace('/', '\\', $this->argument('module'));
56 | }
57 |
58 | /**
59 | * Get the console command arguments.
60 | *
61 | * @return array
62 | */
63 | protected function getArguments()
64 | {
65 | return [
66 | ['module', InputArgument::REQUIRED, 'the name of the module'],
67 | ['name', InputArgument::REQUIRED, 'The name of the class'],
68 | ];
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Commands/Laravel/MakeModuleRequest.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
49 |
50 | return base_path("modules/{$this->argument('module')}/src") . '/' . str_replace('\\', '/', $name) . '.php';
51 | }
52 |
53 |
54 | protected function rootNamespace()
55 | {
56 | return str_replace('/', '\\', $this->argument('module'));
57 | }
58 |
59 | /**
60 | * Generate the form requests for the given model and classes.
61 | *
62 | * @param string $modelClass
63 | * @param string $storeRequestClass
64 | * @param string $updateRequestClass
65 | * @return array
66 | */
67 | protected function generateFormRequests($modelClass, $storeRequestClass, $updateRequestClass)
68 | {
69 | $storeRequestClass = 'Store'.class_basename($modelClass).'Request';
70 |
71 | $this->call('m:make:request', [
72 | 'module' => $this->argument('module'),
73 | 'name' => $storeRequestClass,
74 | ]);
75 |
76 | $updateRequestClass = 'Update'.class_basename($modelClass).'Request';
77 |
78 | $this->call('m:make:request', [
79 | 'module' => $this->argument('module'),
80 | 'name' => $storeRequestClass,
81 | ]);
82 |
83 | return [$storeRequestClass, $updateRequestClass];
84 | }
85 |
86 | /**
87 | * Get the console command arguments.
88 | *
89 | * @return array
90 | */
91 | protected function getArguments()
92 | {
93 | return [
94 | ['module', InputArgument::REQUIRED, 'the name of the module'],
95 | ['name', InputArgument::REQUIRED, 'The name of the class'],
96 | ];
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/Commands/Laravel/MakeModuleResource.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
49 |
50 | return base_path("modules/{$this->argument('module')}/src") . '/' . str_replace('\\', '/', $name) . '.php';
51 | }
52 |
53 |
54 | protected function rootNamespace()
55 | {
56 | return str_replace('/', '\\', $this->argument('module'));
57 | }
58 |
59 |
60 | /**
61 | * Get the console command arguments.
62 | *
63 | * @return array
64 | */
65 | protected function getArguments()
66 | {
67 | return [
68 | ['module', InputArgument::REQUIRED, 'the name of the module'],
69 | ['name', InputArgument::REQUIRED, 'The name of the class'],
70 | ];
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Commands/Laravel/MakeModuleRule.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
49 |
50 | return base_path("modules/{$this->argument('module')}/src") . '/' . str_replace('\\', '/', $name) . '.php';
51 | }
52 |
53 |
54 | protected function rootNamespace()
55 | {
56 | return str_replace('/', '\\', $this->argument('module'));
57 | }
58 |
59 | /**
60 | * Get the console command arguments.
61 | *
62 | * @return array
63 | */
64 | protected function getArguments()
65 | {
66 | return [
67 | ['module', InputArgument::REQUIRED, 'the name of the module'],
68 | ['name', InputArgument::REQUIRED, 'The name of the class'],
69 | ];
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/Commands/Laravel/MakeModuleSeeder.php:
--------------------------------------------------------------------------------
1 | rootNamespace(), '', $name);
52 |
53 | return base_path("modules/{$this->argument('module')}/src/Database/Seeders") . '/' . str_replace('\\', '/', $name) . '.php';
54 | }
55 |
56 |
57 | protected function rootNamespace()
58 | {
59 | return str_replace('/', '\\', $this->argument('module')) . '\Database\Seeders';
60 | }
61 |
62 | /**
63 | * Get the console command arguments.
64 | *
65 | * @return array
66 | */
67 | protected function getArguments()
68 | {
69 | return [
70 | ['module', InputArgument::REQUIRED, 'the name of the module'],
71 | ['name', InputArgument::REQUIRED, 'The name of the class'],
72 | ];
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Commands/MakeModule.php:
--------------------------------------------------------------------------------
1 | moduleName = $this->argument('name');
33 |
34 | $files = [
35 | 'ServiceProvider.php' => [
36 | 'name' => '{name}ServiceProvider',
37 | 'namespace' => 'Providers',
38 | 'stub' => __DIR__ . '/../Stubs/Providers/ServiceProvider.stub',
39 | 'rootPath' => "/src/Providers/",
40 | 'extensions' => 'php'
41 | ],
42 | 'routes.php' => [
43 | 'name' => 'routes',
44 | 'stub' => __DIR__ . '/../Stubs/Routes.stub',
45 | 'rootPath' => "/src/Routes/",
46 | 'extensions' => 'php'
47 | ],
48 | 'composer.json' => [
49 | 'name' => 'composer',
50 | 'stub' => __DIR__ . '/../Stubs/composer.stub',
51 | 'rootPath' => "/",
52 | 'extensions' => 'json',
53 | ]
54 | ];
55 |
56 | foreach ($files as $file => $meta) {
57 | $this->currentFile = $meta;
58 | parent::handle();
59 | }
60 |
61 | (new ModuleHandler($this->moduleName))->add();
62 | }
63 |
64 | public function getNameInput()
65 | {
66 | $name = substr( $this->argument('name'), strrpos( $this->argument('name'), '/') + 1);
67 | return str_replace('{name}' , $name , $this->currentFile['name']);
68 | }
69 |
70 | /**
71 | * Build the class with the given name.
72 | *
73 | * @param string $name
74 | * @return string
75 | *
76 | * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
77 | */
78 | protected function buildClass($name)
79 | {
80 | $stub = $this->files->get($this->getStub());
81 |
82 | return $this->replaceNamespace($stub, $name)
83 | ->replaceModuleNamesapce($stub , $name)
84 | ->replaceClass($stub, $name);
85 | }
86 |
87 |
88 | /**
89 | * Replace the class name for the given stub.
90 | *
91 | * @param string $stub
92 | * @param string $name
93 | * @return string
94 | */
95 | protected function replaceClass($stub, $name)
96 | {
97 | $class = substr( $name, strrpos( $name, '\\') + 1);
98 |
99 | return str_replace(['DummyClass', '{{ class }}', '{{class}}'], $class, $stub);
100 | }
101 |
102 | /**
103 | * Replace the class name for the given stub.
104 | *
105 | * @param string $stub
106 | * @param string $name
107 | * @return $this
108 | */
109 | protected function replaceModuleNamesapce(&$stub, $name)
110 | {
111 | $namespace = str_replace('\\' , '\\\\', $this->rootNamespace());
112 | $namespaceName = Str::lower(str_replace('\\', '/' , $this->rootNamespace()));
113 | $moduleName = Arr::last(explode('\\', $namespace));
114 |
115 | $searches = [
116 | ['{{ moduleNamespace }}' , '{{ moduleNamespaceName }}' , '{{ moduleName }}'] ,
117 | ['{{moduleNamespace}}' , '{{moduleNamespaceName}}' , '{{moduleName}}']
118 | ];
119 |
120 | foreach ($searches as $search ) {
121 | $stub = str_replace($search, [ $namespace , $namespaceName , $moduleName ], $stub);
122 | }
123 |
124 | return $this;
125 | }
126 |
127 |
128 | /**
129 | * Get the full namespace for a given class, without the class name.
130 | *
131 | * @param string $name
132 | * @return string
133 | */
134 | protected function getNamespace($name)
135 | {
136 | $rootNamespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
137 |
138 | if(isset($this->currentFile['namespace']))
139 | return "$rootNamespace\\{$this->currentFile['namespace']}";
140 |
141 | return $rootNamespace;
142 | }
143 |
144 |
145 | /**
146 | * Get the destination class path.
147 | *
148 | * @param string $name
149 | * @return string
150 | */
151 | protected function getPath($name)
152 | {
153 | return "{$this->getModulePath()}/{$this->getNameInput()}.{$this->currentFile['extensions']}";
154 | }
155 |
156 | protected function getStub()
157 | {
158 | return $this->currentFile['stub'];
159 | }
160 |
161 | protected function rootNamespace()
162 | {
163 | return str_replace('/' , '\\' , $this->moduleName);
164 | }
165 |
166 | protected function getModulePath()
167 | {
168 | return base_path("modules/{$this->moduleName}/{$this->currentFile['rootPath']}");
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/src/Exceptions/File/FileIsNotWritableException.php:
--------------------------------------------------------------------------------
1 | getFilePath());
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Exceptions/File/FileNotExistException.php:
--------------------------------------------------------------------------------
1 | getFilePath());
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Helpers/ComposerParser.php:
--------------------------------------------------------------------------------
1 | file = new File($composerFilePath);
12 | }
13 |
14 | public function getOption(string $key)
15 | {
16 | if(!array_key_exists($key, $this->decodeFile())) return null;
17 |
18 | return $this->decodeFile()[$key];
19 | }
20 |
21 | public function setOption(string $key, $value): void
22 | {
23 | $composerContent = $this->decodeFile();
24 |
25 | $composerContent[$key] = $value;
26 |
27 | $this->file->write(
28 | $this->encodeFile($composerContent)
29 | );
30 | }
31 |
32 | private function encodeFile($content): string
33 | {
34 | return json_encode($content, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
35 | }
36 |
37 | private function decodeFile()
38 | {
39 | return json_decode($this->file->read(), true);
40 | }
41 | }
--------------------------------------------------------------------------------
/src/Helpers/File.php:
--------------------------------------------------------------------------------
1 | filePath = $filePath;
18 |
19 | $this->checkFileExistence();
20 | }
21 |
22 | public function getFilePath(): string
23 | {
24 | return $this->filePath;
25 | }
26 |
27 | public function read(): string
28 | {
29 | $content = $this->readFile();
30 |
31 | return $content;
32 | }
33 |
34 | /**
35 | * @throws FileIsNotWritableException
36 | */
37 | public function write(string $content): void
38 | {
39 | $this->checkIsFileWritable();
40 | $this->writeFile($content);
41 | }
42 |
43 | private function readFile(): string
44 | {
45 | return file_get_contents($this->filePath);
46 | }
47 |
48 | private function writeFile(string $content): void
49 | {
50 | file_put_contents($this->filePath, $content);
51 | }
52 |
53 | /**
54 | * @throws FileIsNotWritableException
55 | */
56 | private function checkIsFileWritable(): void
57 | {
58 | if (!is_writable($this->filePath)) throw new FileIsNotWritableException($this);
59 | }
60 |
61 | /**
62 | * @throws FileNotExistException
63 | */
64 | private function checkFileExistence(): void
65 | {
66 | if (!file_exists($this->filePath)) throw new FileNotExistException($this);
67 | }
68 | }
--------------------------------------------------------------------------------
/src/Helpers/ModuleHandler.php:
--------------------------------------------------------------------------------
1 | moduleName = $moduleName;
13 | $this->composerFilePath = $composerFilePath;
14 | }
15 |
16 | public function add(): void
17 | {
18 | $this->addRequire();
19 | $this->addRepository();
20 | }
21 |
22 | private function addRequire()
23 | {
24 | $composerParser = new ComposerParser($this->composerFilePath);
25 | $require = $composerParser->getOption('require');
26 | $require[strtolower($this->moduleName)] = 'dev-main';
27 | $composerParser->setOption('require', $require);
28 | }
29 |
30 | private function addRepository()
31 | {
32 | $composerParser = new ComposerParser($this->composerFilePath);
33 | $repositories = $composerParser->getOption('repositories');
34 | if (!is_null($repositories)) {
35 | $repositories[] = $this->createRepository();
36 | } else {
37 | $repositories = [$this->createRepository()];
38 | }
39 | $composerParser->setOption('repositories', $repositories);
40 | }
41 |
42 | private function createRepository(): array
43 | {
44 | return [
45 | 'type' => 'path',
46 | 'url' => "modules/$this->moduleName",
47 | ];
48 | }
49 | }
--------------------------------------------------------------------------------
/src/LaravelModuleCreatorServiceProvider.php:
--------------------------------------------------------------------------------
1 | registerCommands();
29 | }
30 |
31 | public function registerCommands()
32 | {
33 | $commands = [
34 | MakeModule::class,
35 | MakeModuleMigration::class,
36 | MakeModuleModel::class,
37 | MakeModuleSeeder::class,
38 | MakeModuleController::class,
39 | MakeModuleRepo::class,
40 | MakeModuleResource::class,
41 | MakeModuleRequest::class,
42 | MakeModulePolicy::class,
43 | MakeModuleRule::class,
44 | MakeModuleEvent::class,
45 | MakeModuleListener::class,
46 | MakeModuleJob::class,
47 | MakeModuleNotification::class,
48 | MakeModuleException::class
49 | ];
50 |
51 | if(class_exists("Rebing\GraphQL\Support\Facades\GraphQL")) {
52 | $commands = array_merge($commands, [
53 | MakeModuleGraphqlType::class,
54 | MakeModuleGraphqlMutation::class,
55 | MakeModuleGraphqlQuery::class
56 | ]);
57 | }
58 |
59 | $this->commands($commands);
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/Stubs/Graphql/mutation.stub:
--------------------------------------------------------------------------------
1 | 'DummyGraphqlName',
17 | 'description' => 'A mutation'
18 | ];
19 |
20 | public function type(): Type
21 | {
22 | return Type::listOf(Type::string());
23 | }
24 |
25 | public function args(): array
26 | {
27 | return [
28 |
29 | ];
30 | }
31 |
32 | public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, Closure $getSelectFields)
33 | {
34 | $fields = $getSelectFields();
35 | $select = $fields->getSelect();
36 | $with = $fields->getRelations();
37 |
38 | return [];
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Stubs/Graphql/query.stub:
--------------------------------------------------------------------------------
1 | 'DummyGraphqlName',
17 | 'description' => 'A query'
18 | ];
19 |
20 | public function type(): Type
21 | {
22 | return Type::listOf(Type::string());
23 | }
24 |
25 | public function args(): array
26 | {
27 | return [
28 |
29 | ];
30 | }
31 |
32 | public function resolve($root, array $args, $context, ResolveInfo $resolveInfo, Closure $getSelectFields)
33 | {
34 | /** @var SelectFields $fields */
35 | $fields = $getSelectFields();
36 | $select = $fields->getSelect();
37 | $with = $fields->getRelations();
38 |
39 | return [
40 | 'The DummyGraphqlName works',
41 | ];
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Stubs/Graphql/type.stub:
--------------------------------------------------------------------------------
1 | 'DummyGraphqlName',
13 | 'description' => 'A type'
14 | ];
15 |
16 | public function fields(): array
17 | {
18 | return [
19 |
20 | ];
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Stubs/Providers/ServiceProvider.stub:
--------------------------------------------------------------------------------
1 | loadMigrationsFrom(__DIR__."/../Database/Migrations");
9 | $this->loadRoutesFrom(__DIR__.'/../Routes/routes.php');
10 | }
11 |
12 | public function boot()
13 | {
14 |
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Stubs/Repo.stub:
--------------------------------------------------------------------------------
1 | replaceGraphqlName($stub);
14 | }
15 |
16 | protected function replaceGraphqlName(string $stub): string
17 | {
18 | $graphqlName = $this->getNameInput();
19 | $graphqlName = \Safe\preg_replace('/Type$/', '', $graphqlName);
20 |
21 | return str_replace(
22 | 'DummyGraphqlName',
23 | $graphqlName,
24 | $stub
25 | );
26 | }
27 |
28 | /**
29 | * Get the console command arguments.
30 | *
31 | * @return array
32 | */
33 | protected function getArguments()
34 | {
35 | return [
36 | ['module', InputArgument::REQUIRED, 'the name of the module'],
37 | ['name', InputArgument::REQUIRED, 'The name of the class'],
38 | ];
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Traits/RequireModule.php:
--------------------------------------------------------------------------------
1 | isDirectory(base_path("modules/{$this->argument('module')}")))
10 | throw new \Exception("{$this->argument('module')} module not found");
11 | }
12 |
13 | public function handle()
14 | {
15 | $this->checkModuleExists();
16 |
17 | parent::handle();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tests/Unit/ComposerParserTest.php:
--------------------------------------------------------------------------------
1 | copyComposerFile();
17 | }
18 |
19 | private function copyComposerFile(): void
20 | {
21 | copy('composer.json', self::composerFileName);
22 | }
23 |
24 | public function testAddingOption(): void
25 | {
26 | $composerParser = new ComposerParser(self::composerFileName);
27 | $require = $composerParser->getOption('require');
28 | $require['test/test'] = 'dev-main';
29 | $composerParser->setOption(
30 | 'require',
31 | $require
32 | );
33 |
34 | $this->assertTrue(
35 | !is_null($composerParser->getOption('require'))
36 | && !is_null($composerParser->getOption('require')['illuminate/support'])
37 | && $composerParser->getOption('require')['test/test'] == 'dev-main'
38 | );
39 | }
40 |
41 | public function testSettingOptionSimple(): void
42 | {
43 | $composerParser = new ComposerParser(self::composerFileName);
44 | $composerParser->setOption('name', 'test/test');
45 | $name = $composerParser->getOption('name');
46 |
47 | $this->assertEquals('test/test', $name);
48 | }
49 |
50 | public function testSettingOptionArray(): void
51 | {
52 | $composerParser = new ComposerParser(self::composerFileName);
53 | $keywords = ['keyOne', 'keyTwo', 'keyThree'];
54 | $composerParser->setOption('keywords', $keywords);
55 | $resultKeywords = $composerParser->getOption('keywords');
56 |
57 | $this->assertEquals($keywords[0], $resultKeywords[0]);
58 | $this->assertEquals($keywords[1], $resultKeywords[1]);
59 | $this->assertEquals($keywords[2], $resultKeywords[2]);
60 | }
61 |
62 | public function testSettingOptionObject(): void
63 | {
64 | $composerParser = new ComposerParser(self::composerFileName);
65 | $composerParser->setOption('require', [
66 | 'test/test' => 'dev-main',
67 | ]);
68 | $require = $composerParser->getOption('require');
69 |
70 | $this->assertTrue(!is_null($require['test/test']) && $require['test/test'] == 'dev-main');
71 | }
72 |
73 | public function testSettingOptionComplex(): void
74 | {
75 | $composerParser = new ComposerParser(self::composerFileName);
76 | $composerParser->setOption('extra', [
77 | 'test' => [
78 | 'providers' => [
79 | 'one',
80 | 'two',
81 | 'three',
82 | ]
83 | ],
84 | ]);
85 | $extra = $composerParser->getOption('extra');
86 |
87 | $this->assertTrue($extra['test'] === ['providers' => ['one', 'two', 'three']]);
88 | }
89 |
90 | public function testGettingOption(): void
91 | {
92 | $composerParser = new ComposerParser(self::composerFileName);
93 | $name = $composerParser->getOption('name');
94 |
95 | $this->assertEquals('hesammousavi/laravel-module-creator', $name);
96 | }
97 |
98 | public function tearDown(): void
99 | {
100 | parent::tearDown();
101 |
102 | $this->deleteComposerFile();
103 | }
104 |
105 | private function deleteComposerFile()
106 | {
107 | unlink(self::composerFileName);
108 | }
109 | }
--------------------------------------------------------------------------------
/tests/Unit/FileTest.php:
--------------------------------------------------------------------------------
1 | createTestFile();
18 | }
19 |
20 | private function createTestFile()
21 | {
22 | $file = fopen(self::testFileName, 'w');
23 | fwrite($file, self::testFileContent);
24 | fclose($file);
25 | }
26 |
27 | public function testExceptionOnNotExistingFile()
28 | {
29 | $this->expectException(FileNotExistException::class);
30 |
31 | new File('dummy_name');
32 | }
33 |
34 | public function testReadFile()
35 | {
36 | $file = new File(self::testFileName);
37 | $content = $file->read();
38 |
39 | $this->assertTrue($content === self::testFileContent);
40 | }
41 |
42 | public function testWriteFile()
43 | {
44 | $file = new File(self::testFileName);
45 | $content = $file->read();
46 |
47 | $this->assertTrue($content === self::testFileContent);
48 |
49 | $newFileContent = 'new content';
50 | $file->write($newFileContent);
51 | $newContent = $file->read();
52 |
53 | $this->assertTrue($newContent == $newFileContent);
54 | }
55 |
56 | protected function tearDown(): void
57 | {
58 | parent::tearDown();
59 |
60 | $this->deleteTestFile();
61 | }
62 |
63 | private function deleteTestFile()
64 | {
65 | unlink(self::testFileName);
66 | }
67 | }
--------------------------------------------------------------------------------
/tests/Unit/ModuleHandlerTest.php:
--------------------------------------------------------------------------------
1 | copyComposerFile();
19 | }
20 |
21 | private function copyComposerFile(): void
22 | {
23 | copy('composer.json', self::composerFileName);
24 | }
25 |
26 | public function testAddingModule()
27 | {
28 | $moduleHandler = new ModuleHandler('Roocket/User', self::composerFileName);
29 | $moduleHandler->add();
30 |
31 | $composerParser = new ComposerParser(self::composerFileName);
32 | $require = $composerParser->getOption('require');
33 | $repositories = $composerParser->getOption('repositories');
34 |
35 | $this->assertTrue(!is_null($require['roocket/user']) && $require['roocket/user'] == 'dev-main');
36 | $this->assertTrue(!is_null($repositories));
37 |
38 | foreach ($repositories as $repository){
39 | if($repository['url'] == 'modules/Roocket/User'){
40 | return;
41 | }
42 | }
43 | throw new Exception('repository does not exist in composer file');
44 | }
45 |
46 | public function tearDown(): void
47 | {
48 | parent::tearDown();
49 |
50 | $this->deleteComposerFile();
51 | }
52 |
53 | private function deleteComposerFile()
54 | {
55 | unlink(self::composerFileName);
56 | }
57 | }
--------------------------------------------------------------------------------