├── .gitignore
├── LICENSE
├── README.md
├── clarc-laravel-plugin.iml
├── composer.json
├── phpunit.xml.dist
├── src
├── ClarcProvider.php
├── Code
│ └── ClarcModuleCodes.php
├── Commands
│ ├── ClarcCommand.php
│ ├── Initialize
│ │ └── ClarcInitializeCommand.php
│ ├── Make
│ │ ├── ClarcMakeCommand.php
│ │ ├── ClarcMakeCommandObjectCreatePresenter.php
│ │ └── ClarcMakeCommandProviderAppendUseCasePresenter.php
│ └── Shared
│ │ └── Menu
│ │ ├── ClarcCommandSharedMenu.php
│ │ └── ClarcCommandSharedMenuOption.php
├── Config
│ └── LaravelConfig.php
├── LaravelSourceFileBuilder
│ ├── LaravelControllerSourceFileBuilder.php
│ └── LaravelPresenterSourceFileBuilder.php
└── UseCases
│ ├── ClarcObject
│ └── Create
│ │ ├── ClarcObjectCreateInputData.php
│ │ ├── ClarcObjectCreateInputPort.php
│ │ ├── ClarcObjectCreateInteractor.php
│ │ ├── ClarcObjectCreateOutputData.php
│ │ ├── ClarcObjectCreateOutputPortInterface.php
│ │ └── ClarcObjectUseCaseCreateOutputCollector.php
│ └── ClarcProvider
│ └── AppendUseCase
│ ├── ClarcProviderAppendUseCaseInputData.php
│ ├── ClarcProviderAppendUseCaseInputPortInterface.php
│ ├── ClarcProviderAppendUseCaseInteractor.php
│ ├── ClarcProviderAppendUseCaseOutputData.php
│ ├── ClarcProviderAppendUseCaseOutputPortInterface.php
│ └── Scripts
│ ├── AppendUseCaseScriptInterface.php
│ └── UseCaseSettingScript.php
└── tests
├── BasicCodeGenerate
└── BasicCodeGenerateTest.php
├── ClarcObjectCreateInteractor
├── ClarcObjectCreateInteractorTest.php
└── TestClarcObjectCreatePresenter.php
├── ClarcProviderAppendUseCaseInteractor
├── ClarcProviderAppendUseCaseInteractorTest.php
└── TestClarcProviderAppendUseCaseUseCasePresenter.php
└── bootstrap.php
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 |
3 | composer.phar
4 | composer.lock
5 | /vendor/
6 | .phpunit.result.cache
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Masanobu Naruse
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # clarc-laravel-plugin
2 |
3 | クリーンアーキテクチャの実装例にしたがって、必要なモジュール群をスキャフォールディングするプラグインです。
4 |
5 | https://nrslib.com/phpcon-2019-proposal/
6 |
7 | 現在開発中です。
8 |
9 | # Commands
10 |
11 | ## clarc:init
12 |
13 | 初期化コマンドです。
14 | ClarcProvider と ClarcMiddleWare が作成されます。
15 | ClarcProvider を Provider として登録し、ClarcMiddleWare を MiddleWare として登録してください。
16 | ClarcProvider は後述の clarc:make 実行時にインジェクション対象を設定する箇所です。
17 |
18 | ## clarc:make
19 |
20 | クリーンアーキテクチャの実装例にしたがって、必要なモジュール群をスキャフォールディングします。
21 | 入力に従い下記データが生成されます。
22 |
23 | - Controller
24 | - InputData
25 | - InputPort
26 | - Interactor
27 | - OutputPort
28 | - OutputData
29 | - Presenter
30 | - ViewModel
31 |
--------------------------------------------------------------------------------
/clarc-laravel-plugin.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nrslib/clarc-laravel-plugin",
3 | "require": {
4 | "php": "^7.2",
5 | "nrslib/cfg": "0.*",
6 | "nrslib/clarc-php-core": "dev-master",
7 | "illuminate/support": "^6.2",
8 | "laravel/framework": "^6.2"
9 | },
10 | "require-dev": {
11 | "phpunit/phpunit": "^8.0"
12 | },
13 | "autoload": {
14 | "psr-4": {
15 | "nrslib\\ClarcLaravelPlugin\\": "src/"
16 | }
17 | },
18 | "autoload-dev": {
19 | "psr-4": {
20 | "nrslib\\ClarcLaravelPluginTests\\": "tests/"
21 | }
22 | },
23 | "extra": {
24 | "laravel": {
25 | "providers": [
26 | "nrslib\\ClarcLaravelPlugin\\ClarcProvider"
27 | ]
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 | tests
4 |
5 |
6 |
7 | src
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/ClarcProvider.php:
--------------------------------------------------------------------------------
1 | app->singleton('command.Clarc.make', function ($app) {
19 | return $app['nrslib\ClarcLaravelPlugin\Commands\Make\ClarcMakeCommand'];
20 | });
21 | $this->commands('command.Clarc.make');
22 |
23 | $this->app->singleton('command.Clarc.initialize', function ($app) {
24 | return $app['nrslib\ClarcLaravelPlugin\Commands\Initialize\ClarcInitializeCommand'];
25 | });
26 | $this->commands('command.Clarc.initialize');
27 | }
28 | }
--------------------------------------------------------------------------------
/src/Code/ClarcModuleCodes.php:
--------------------------------------------------------------------------------
1 | app->singleton(ClarcMiddleWare::class);
31 | $this->registerUseCases();
32 | }
33 |
34 | /**
35 | * register use cases
36 | */
37 | private function registerUseCases()
38 | {
39 | }
40 | }
41 | ';
42 |
43 | public static $debugProvider =
44 | '';
45 |
46 | public static $middleWare =
47 | 'router = $router;
76 | }
77 |
78 | /**
79 | * @param mixed $data
80 | * @see \Illuminate\Routing\Router::toResponse()
81 | */
82 | public function setData($data): void
83 | {
84 | $this->data = $data;
85 | }
86 |
87 | /**
88 | * Handle an incoming request.
89 | *
90 | * @param Request $request
91 | * @param \Closure $next
92 | * @return mixed
93 | */
94 | public function handle($request, Closure $next)
95 | {
96 | $response = $next($request);
97 | if ($this->data === null) {
98 | return $response;
99 | }
100 |
101 | return $this->router->prepareResponse($this->router->getCurrentRequest(), $this->data);
102 | }
103 | }
104 | ';
105 | }
--------------------------------------------------------------------------------
/src/Commands/ClarcCommand.php:
--------------------------------------------------------------------------------
1 | info('Welcome to Clarc');
14 | $this->separator();
15 | $this->info($text);
16 | $this->separator();
17 | }
18 |
19 | public function need(string $text)
20 | {
21 | while (true) {
22 | $result = $this->ask($text);
23 | if (!empty($result)) {
24 | return $result;
25 | }
26 | }
27 | }
28 |
29 | public function separator()
30 | {
31 | $this->line('---------------------------------------------------------------');
32 | }
33 |
34 | public function newline()
35 | {
36 | $this->line('');
37 | }
38 | }
--------------------------------------------------------------------------------
/src/Commands/Initialize/ClarcInitializeCommand.php:
--------------------------------------------------------------------------------
1 | title('Initialize clarc');
19 |
20 | $middlewareFilePath = LaravelConfig::DIR_MIDDLEWARE . 'ClarcMiddleWare.php';
21 | $this->info('Creating file ' . $middlewareFilePath);
22 | file_put_contents($middlewareFilePath, ClarcModuleCodes::$middleWare);
23 | $this->info('Wrote ' . realpath($middlewareFilePath));
24 | $this->newline();
25 |
26 | $clarcProviderFilePath = LaravelConfig::DIR_PROVIDER . 'ClarcProvider.php';
27 | $this->info('Creating file' . $clarcProviderFilePath);
28 | file_put_contents($clarcProviderFilePath, ClarcModuleCodes::$clarcProvider);
29 | $this->info('Wrote ' . realpath($clarcProviderFilePath));
30 | $this->newline();
31 |
32 | $this->info('Please append ClarcProvider to app.php');
33 | }
34 | }
--------------------------------------------------------------------------------
/src/Commands/Make/ClarcMakeCommand.php:
--------------------------------------------------------------------------------
1 | title('Make usecase');
35 |
36 | $this->info('Please input your controller name.');
37 | $controllerName = $this->need('controller name');
38 | $controllerName = ucfirst($controllerName);
39 |
40 | $this->info('Please input your action name.');
41 | $actionName = $this->need('action name.');
42 | $actionName = ucfirst($actionName);
43 |
44 | $schema = new UseCaseSchema($controllerName, $actionName);
45 | $objectCreatePresenter = new ClarcMakeCommandObjectCreatePresenter($this, $schema);
46 | $alreadyRegistered = !$this->checkFiles($controllerName, $actionName, $objectCreatePresenter);
47 | if ($alreadyRegistered) {
48 | $this->info('process ended.');
49 | return;
50 | }
51 |
52 | $this->info('Please select input data fields');
53 | $inputDataFields = $this->askFields();
54 | $this->info('Please select output data fields');
55 | $outputDataFields = $this->askFields();
56 |
57 | $this->createClarcObjects($objectCreatePresenter, $controllerName, $actionName, $inputDataFields, $outputDataFields);
58 | $this->registerDependency($controllerName, $actionName);
59 |
60 | $this->info('process ended.');
61 | }
62 |
63 | /**
64 | * @param ClarcMakeCommandObjectCreatePresenter $objectCreatePresenter
65 | * @param string $controllerName
66 | * @param string $actionName
67 | * @param TypeAndName[] $inputDataFields
68 | * @param TypeAndName[] $outputDataFields
69 | */
70 | private function createClarcObjects(ClarcMakeCommandObjectCreatePresenter $objectCreatePresenter, string $controllerName, string $actionName, array $inputDataFields, array $outputDataFields)
71 | {
72 | $objectCreateInteractor = new ClarcObjectCreateInteractor(
73 | $objectCreatePresenter,
74 | new ClassRenderer(),
75 | new InterfaceRenderer()
76 | );
77 | $currentControllerContent = $this->getCurrentControllerContent($controllerName);
78 | $objectCreateInputData = new ClarcObjectCreateInputData(
79 | $controllerName,
80 | $currentControllerContent,
81 | $actionName,
82 | $inputDataFields,
83 | $outputDataFields
84 | );
85 | $objectCreateInteractor->handle($objectCreateInputData);
86 | }
87 |
88 | /**
89 | * @param string $controllerName
90 | * @param string $actionName
91 | */
92 | private function registerDependency(string $controllerName, string $actionName)
93 | {
94 | $presenter = new ClarcMakeCommandProviderAppendUseCasePresenter();
95 |
96 | $currentProviderCode = file_get_contents(LaravelConfig::FILE_CLARC_PROVIDER);
97 |
98 | $identifer = $controllerName . $actionName;
99 | $inputData = new ClarcProviderAppendUseCaseInputData(
100 | $currentProviderCode,
101 | '',
102 | $identifer,
103 | $this->createUsingTarget(LaravelConfig::NAMESPACE_INPUT_PORT, $controllerName, $actionName, 'InputPortInterface', true),
104 | $this->createUsingTarget(LaravelConfig::NAMESPACE_INTERACTOR, $controllerName, $actionName, 'Interactor'),
105 | $this->createUsingTarget(LaravelConfig::NAMESPACE_OUTPUT_PORT, $controllerName, $actionName, 'OutputPortInterface', true),
106 | $this->createUsingTarget(LaravelConfig::NAMESPACE_PRESENTER, $controllerName, $actionName, 'Presenter')
107 | );
108 | $interactor = new ClarcProviderAppendUseCaseInteractor($presenter);
109 | $interactor->handle($inputData);
110 | }
111 |
112 | /**
113 | * @param string $packagePrefix
114 | * @param string $controllerName
115 | * @param string $actionName
116 | * @param string $suffix
117 | * @param bool $appendActionNameToNameSpace
118 | * @return string
119 | */
120 | private function createUsingTarget(string $packagePrefix, string $controllerName, string $actionName, string $suffix, bool $appendActionNameToNameSpace = false)
121 | {
122 | $prefix = '\\' . $packagePrefix . '\\' . $controllerName;
123 | if ($appendActionNameToNameSpace) {
124 | return $prefix . '\\' . $actionName . '\\' . $controllerName . $actionName . $suffix;
125 | } else {
126 | return $prefix . '\\' . $controllerName . $actionName . $suffix;
127 | }
128 | }
129 |
130 | /**
131 | * @return array
132 | */
133 | private function askFields(): array
134 | {
135 | $results = [];
136 |
137 | while (true) {
138 | $this->separator();
139 | $choice = $this->choice('Please select field type', ['int', 'string', 'array', 'user defined', 'finish'], 'finish');
140 |
141 | if ($choice === 'finish') {
142 | goto finish;
143 | }
144 |
145 | $this->info('Please input field information');
146 |
147 | $name = $this->need('name');
148 | if (key_exists($name, $results)) {
149 | $this->error('"' . $name . '" is already registered.');
150 | $this->separator();
151 | $this->info('current fields');
152 | $this->separator();
153 | $this->showFields($results);
154 | continue;
155 | }
156 |
157 | $namespace = null;
158 | $type = $choice;
159 | if ($choice === 'user defined') {
160 | $namespace = $this->need('namespace');
161 | $type = $this->need('type');
162 | }
163 | $fieldInfo = new TypeAndName(
164 | $type,
165 | $name,
166 | $namespace
167 | );
168 |
169 | $results[$fieldInfo->name] = $fieldInfo;
170 | }
171 |
172 | finish:
173 | return $results;
174 | }
175 |
176 | /**
177 | * @param string $controllerName
178 | * @param string $actionName
179 | * @param ClarcMakeCommandObjectCreatePresenter $presenter
180 | * @return bool
181 | */
182 | private function checkFiles(string $controllerName, string $actionName, ClarcMakeCommandObjectCreatePresenter $presenter): bool
183 | {
184 | $overwrites = $presenter->willBeOverwriteFiles($controllerName, $actionName);
185 | if (empty($overwrites)) {
186 | return true;
187 | }
188 |
189 | $this->info('The following file will be overwritten.');
190 | $this->separator();
191 | foreach ($overwrites as $file) {
192 | $fullPath = realpath($file);
193 | $this->info($fullPath);
194 | }
195 |
196 | return $this->confirm('ok?', false);
197 | }
198 |
199 | /**
200 | * @param TypeAndName[] $fields
201 | */
202 | private function showFields(array $fields)
203 | {
204 | $firstLine = true;
205 | foreach ($fields as $field) {
206 | if (!$firstLine) {
207 | $this->newline();
208 | }
209 | $this->info('name: ' . $field->name);
210 | $this->info('type: ' . $field->type);
211 | if ($field->hasNamespace()) {
212 | $this->info('namespace: ' . $field->namespace);
213 | }
214 | $firstLine = false;
215 | }
216 | }
217 |
218 | /**
219 | * @param string $controllerName
220 | * @return string|null
221 | */
222 | private function getCurrentControllerContent(string $controllerName): ?string
223 | {
224 | $controllerFile = LaravelConfig::DIR_CONTROLLER . $controllerName . 'Controller.php';
225 | if (!file_exists($controllerFile)) {
226 | return null;
227 | }
228 |
229 | $content = file_get_contents($controllerFile);
230 | if (empty($content)) {
231 | return null;
232 | }
233 |
234 | return $content;
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/src/Commands/Make/ClarcMakeCommandObjectCreatePresenter.php:
--------------------------------------------------------------------------------
1 | command = $command;
27 | $this->schema = $schema;
28 | }
29 |
30 | public function output(ClarcObjectCreateOutputData $outputData)
31 | {
32 | $this->putSourceFile(LaravelConfig::DIR_CONTROLLER, $outputData->getControllerSourceFile());
33 | $this->putSourceFile($this->appendSchemaDirectory(LaravelConfig::DIR_INPUT_PORT, true), $outputData->getInputDataSourceFile());
34 | $this->putSourceFile($this->appendSchemaDirectory(LaravelConfig::DIR_INPUT_PORT, true), $outputData->getInputPortSourceFile());
35 | $this->putSourceFile($this->appendSchemaDirectory(LaravelConfig::DIR_INTERACTOR), $outputData->getInteractorSourceFile());
36 | $this->putSourceFile($this->appendSchemaDirectory(LaravelConfig::DIR_OUTPUT_PORT, true), $outputData->getOutputDataSourceFile());
37 | $this->putSourceFile($this->appendSchemaDirectory(LaravelConfig::DIR_OUTPUT_PORT, true), $outputData->getOutputPortSourceFile());
38 | $this->putSourceFile($this->appendSchemaDirectory(LaravelConfig::DIR_PRESENTER), $outputData->getPresenterSourceFile());
39 | $this->putSourceFile($this->appendSchemaDirectory(LaravelConfig::DIR_VIEWMODEL, true), $outputData->getViewModelSourceFile());
40 |
41 | $this->command->newline();
42 | }
43 |
44 | private function appendSchemaDirectory(string $path, bool $appendUsecase = false) {
45 | if ($appendUsecase) {
46 | return $path . '\\' . $this->schema->categoryName . '\\' . $this->schema->usecaseName . '\\';
47 | } else {
48 | return $path . '\\' . $this->schema->categoryName . '\\';
49 | }
50 | }
51 |
52 | public function willBeOverwriteFiles(string $controllerName, string $actionName) {
53 | $prefix = $controllerName . $actionName;
54 | $checkTargets = [
55 | LaravelConfig::DIR_INPUT_PORT . $prefix . 'InputPortInterface.php',
56 | LaravelConfig::DIR_INPUT_PORT . $prefix . 'InputData.php',
57 | LaravelConfig::DIR_INTERACTOR . $prefix . 'Interactor.php',
58 | LaravelConfig::DIR_OUTPUT_PORT . $prefix . 'OutputPortInterface.php',
59 | LaravelConfig::DIR_OUTPUT_PORT . $prefix . 'OutputData.php',
60 | LaravelConfig::DIR_PRESENTER . $prefix . 'Presenter.php',
61 | LaravelConfig::DIR_VIEWMODEL . $prefix . 'ViewModel.php',
62 | ];
63 |
64 | $overwrites = [];
65 | foreach ($checkTargets as $checkTarget) {
66 | if (file_exists($checkTarget)) {
67 | array_push($overwrites, $checkTarget);
68 | }
69 | }
70 |
71 | return $overwrites;
72 | }
73 |
74 | private function putSourceFile(string $directory, SourceFileData $sourceFileData)
75 | {
76 | if (!file_exists($directory)) {
77 | mkdir($directory, '0777', true);
78 | }
79 | $file = $directory . $sourceFileData->getClassName() . '.php';
80 | $this->command->info('Creating file ' . $file);
81 | file_put_contents($file, $sourceFileData->getContents());
82 | $this->command->info('Wrote ' . realpath($file));
83 | }
84 | }
--------------------------------------------------------------------------------
/src/Commands/Make/ClarcMakeCommandProviderAppendUseCasePresenter.php:
--------------------------------------------------------------------------------
1 | getClarcProviderCode());
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Commands/Shared/Menu/ClarcCommandSharedMenu.php:
--------------------------------------------------------------------------------
1 | options = $options;
17 | }
18 |
19 | public function getOptions()
20 | {
21 | return $this->options;
22 | }
23 |
24 | public function interact(string $selector): bool
25 | {
26 | foreach ($this->options as $option)
27 | {
28 | if ($option->match($selector))
29 | {
30 | $option->interact();
31 | return true;
32 | }
33 | }
34 |
35 | return false;
36 | }
37 | }
--------------------------------------------------------------------------------
/src/Commands/Shared/Menu/ClarcCommandSharedMenuOption.php:
--------------------------------------------------------------------------------
1 | selector = $selector;
20 | $this->name = $name;
21 | }
22 |
23 | public function match(string $word): bool
24 | {
25 | return strtolower($word) === strtolower($this->selector);
26 | }
27 |
28 | public function toString()
29 | {
30 | return $this->selector . '. ' . $this->name;
31 | }
32 |
33 | public abstract function interact();
34 | }
--------------------------------------------------------------------------------
/src/Config/LaravelConfig.php:
--------------------------------------------------------------------------------
1 | renderer = $renderer;
35 | $this->currentControllerContent = $currentControllerContent;
36 | }
37 |
38 | public function build(UseCaseSchema $schema, string $namespace, string $inputPortName, string $inputPortNamespace): SourceFileData
39 | {
40 | $name = $schema->categoryName . 'Controller';
41 |
42 | $clazz = $this->prepareMeta($name, $namespace, $inputPortNamespace, $inputPortName);
43 | $actionName = lcfirst($schema->usecaseName);
44 |
45 | if (!$this->existMethod($clazz->getMethodsSetting()->getMethods(), $actionName)) {
46 | $clazz->setupClass()
47 | ->addUse($inputPortNamespace . '\\' . $inputPortName);
48 | $clazz->getMethodsSetting()
49 | ->addMethod($actionName, function ($methodDefinition) use ($actionName, $inputPortName) {
50 | $methodDefinition
51 | ->setAccessLevel(AccessLevel::public())
52 | ->addArgument('inputPort', $inputPortName)
53 | ->addBody('// TODO: Implement ' . $actionName . '() method.');
54 | });
55 | }
56 |
57 | $contents = $this->renderer->render($clazz);
58 |
59 | return new SourceFileData($name, $contents);
60 | }
61 |
62 | private function prepareMeta(string $name, string $namespace, string $inputPortNamespace, string $inputPortName)
63 | {
64 | if (is_null($this->currentControllerContent)) {
65 | $clazz = new ClassMeta($name, $namespace);
66 | $clazz->setupClass()
67 | ->addUse('Illuminate\Routing\Controller as BaseController')
68 | ->setExtend('BaseController');
69 | return $clazz;
70 | } else {
71 | $parser = new ClassParser();
72 | $meta = $parser->parse($this->currentControllerContent);
73 | return $meta;
74 | }
75 | }
76 |
77 | /**
78 | * @param MethodDefinition[] $methods
79 | * @param string $name
80 | * @return bool
81 | */
82 | private function existMethod(array $methods, string $name): bool
83 | {
84 | foreach ($methods as $method) {
85 | if ($method->getName() === $name) {
86 | return true;
87 | }
88 | }
89 | return false;
90 | }
91 | }
--------------------------------------------------------------------------------
/src/LaravelSourceFileBuilder/LaravelPresenterSourceFileBuilder.php:
--------------------------------------------------------------------------------
1 | classRenderer = $classRenderer;
29 | }
30 |
31 |
32 | function build(UseCaseSchema $schema, string $namespace, string $outputDataName, string $outputPortName, string $outputPortNamespace): SourceFileData
33 | {
34 | $name = $schema->fullName() . 'Presenter';
35 |
36 | $clazz = new ClassMeta($name , $namespace);
37 | $clazz->setupClass()
38 | ->addUse($outputPortNamespace . '\\' . $outputPortName)
39 | ->addUse($outputPortNamespace . '\\' . $outputDataName)
40 | ->addUse(LaravelConfig::NAMESPACE_MIDDLEWARE . '\\' . 'ClarcMiddleware')
41 | ->addUse(LaravelConfig::NAMESPACE_VIEWMODEL . '\\' . $schema->categoryName . '\\' . $schema->usecaseName . '\\' . $schema->fullName() . 'ViewModel')
42 | ->addImplement($outputPortName)
43 | ->setConstructor(function ($definition) {
44 | $definition->addArgument('middleware', 'ClarcMiddleware')
45 | ->addBody('$this->middleware = $middleware;');
46 | });
47 |
48 | $clazz->setupFields()
49 | ->addField('middleware', 'ClarcMiddleware');
50 |
51 | $clazz->setupMethods()
52 | ->addMethod('output', function ($methodDefine) use ($outputDataName, $schema) {
53 | $methodDefine->addArgument('outputData', $outputDataName)
54 | ->setAccessLevel(AccessLevel::public())
55 | ->addBody('$viewModel = new ' . $schema->fullName() . 'ViewModel($outputData);')
56 | ->addBody('$this->middleware->setData(view(\'view_resource\', compact(\'viewModel\')));');
57 | });
58 |
59 | $contents = $this->classRenderer->render($clazz);
60 |
61 | return new SourceFileData($name, $contents);
62 | }
63 | }
--------------------------------------------------------------------------------
/src/UseCases/ClarcObject/Create/ClarcObjectCreateInputData.php:
--------------------------------------------------------------------------------
1 | controllerName = $controllerName;
47 | $this->currentControllerContent = $currentControllerContent;
48 | $this->actionName = $actionName;
49 | $this->inputDataFields = $inputDataFields;
50 | $this->outputDataFields = $outputDataFields;
51 | }
52 | }
--------------------------------------------------------------------------------
/src/UseCases/ClarcObject/Create/ClarcObjectCreateInputPort.php:
--------------------------------------------------------------------------------
1 | outputPort = $outputPort;
44 | $this->classRenderer = $classRenderer;
45 | $this->interfaceRenderer = $interfaceRenderer;
46 | }
47 |
48 | public function handle(ClarcObjectCreateInputData $inputData)
49 | {
50 | $inputPortNameSpace = LaravelConfig::NAMESPACE_INPUT_PORT . '\\' . $inputData->controllerName . '\\' . $inputData->actionName;
51 | $namespaces = new UseCaseCreateNamespaceData(
52 | LaravelConfig::NAMESPACE_CONTROLLER,
53 | $inputPortNameSpace,
54 | LaravelConfig::NAMESPACE_INTERACTOR . '\\' . $inputData->controllerName,
55 | LaravelConfig::NAMESPACE_OUTPUT_PORT . '\\' . $inputData->controllerName . '\\' . $inputData->actionName,
56 | LaravelConfig::NAMESPACE_PRESENTER . '\\' . $inputData->controllerName,
57 | LaravelConfig::NAMESPACE_VIEWMODEL . '\\' . $inputData->controllerName . '\\' . $inputData->actionName
58 | );
59 | $usecaseCreateOutputData = $this->executeClarcCore($inputData, $namespaces);
60 |
61 | $outputData = new ClarcObjectCreateOutputData(
62 | $usecaseCreateOutputData->getControllerSourceFile(),
63 | $usecaseCreateOutputData->getInputPortSourceFile(),
64 | $usecaseCreateOutputData->getInteractorSourceFile(),
65 | $usecaseCreateOutputData->getInputDataSourceFile(),
66 | $usecaseCreateOutputData->getOutputPortSourceFile(),
67 | $usecaseCreateOutputData->getOutputDataSourceFile(),
68 | $usecaseCreateOutputData->getPresenterSourceFile(),
69 | $usecaseCreateOutputData->getViewModelSourceFile()
70 | );
71 | $this->outputPort->output($outputData);
72 | }
73 |
74 | /**
75 | * @param ClarcObjectCreateInputData $inputData
76 | * @param UseCaseCreateNamespaceData $namespaces
77 | * @return UseCaseCreateOutputData
78 | */
79 | public function executeClarcCore(ClarcObjectCreateInputData $inputData, UseCaseCreateNamespaceData $namespaces): UseCaseCreateOutputData
80 | {
81 | $usecaseCreateDataCollector = new ClarcObjectUseCaseCreateOutputCollector();
82 | $usecaseCreateInteractor = new UseCaseCreateInteractor(
83 | $usecaseCreateDataCollector,
84 | $this->classRenderer,
85 | $this->interfaceRenderer,
86 | new LaravelControllerSourceFileBuilder($this->classRenderer, $inputData->currentControllerContent),
87 | new LaravelPresenterSourceFileBuilder($this->classRenderer));
88 | $usecaseInputData = new UseCaseCreateInputData(
89 | $namespaces,
90 | new UseCaseSchema($inputData->controllerName, $inputData->actionName),
91 | $inputData->inputDataFields,
92 | $inputData->outputDataFields
93 | );
94 |
95 | $usecaseCreateInteractor->handle($usecaseInputData);
96 |
97 | return $usecaseCreateDataCollector->getRecentOutputData();
98 | }
99 | }
--------------------------------------------------------------------------------
/src/UseCases/ClarcObject/Create/ClarcObjectCreateOutputData.php:
--------------------------------------------------------------------------------
1 | controllerSourceFile = $controllerSourceFile;
63 | $this->inputPortSourceFile = $inputPortSourceFile;
64 | $this->interactorSourceFile = $interactorDataSourceFile;
65 | $this->inputDataSourceFile = $inputDataSourceFile;
66 | $this->outputPortSourceFile = $outputPortSourceFile;
67 | $this->outputDataSourceFile = $outputDataSourceFile;
68 | $this->presenterSourceFile = $presenterSourceFile;
69 | $this->viewModelSourceFile = $viewModelSourceFile;
70 | }
71 |
72 | /**
73 | * @return SourceFileData
74 | */
75 | public function getControllerSourceFile(): SourceFileData
76 | {
77 | return $this->controllerSourceFile;
78 | }
79 |
80 | /**
81 | * @return SourceFileData
82 | */
83 | public function getInputPortSourceFile(): SourceFileData
84 | {
85 | return $this->inputPortSourceFile;
86 | }
87 |
88 | /**
89 | * @return SourceFileData
90 | */
91 | public function getInteractorSourceFile(): SourceFileData
92 | {
93 | return $this->interactorSourceFile;
94 | }
95 |
96 | /**
97 | * @return SourceFileData
98 | */
99 | public function getInputDataSourceFile(): SourceFileData
100 | {
101 | return $this->inputDataSourceFile;
102 | }
103 |
104 | /**
105 | * @return SourceFileData
106 | */
107 | public function getOutputPortSourceFile(): SourceFileData
108 | {
109 | return $this->outputPortSourceFile;
110 | }
111 |
112 | /**
113 | * @return SourceFileData
114 | */
115 | public function getOutputDataSourceFile(): SourceFileData
116 | {
117 | return $this->outputDataSourceFile;
118 | }
119 |
120 | /**
121 | * @return SourceFileData
122 | */
123 | public function getPresenterSourceFile(): SourceFileData
124 | {
125 | return $this->presenterSourceFile;
126 | }
127 |
128 | /**
129 | * @return SourceFileData
130 | */
131 | public function getViewModelSourceFile(): SourceFileData
132 | {
133 | return $this->viewModelSourceFile;
134 | }
135 | }
--------------------------------------------------------------------------------
/src/UseCases/ClarcObject/Create/ClarcObjectCreateOutputPortInterface.php:
--------------------------------------------------------------------------------
1 | recentOutputData;
23 | }
24 |
25 | function output(UseCaseCreateOutputData $outputData)
26 | {
27 | $this->recentOutputData = $outputData;
28 | }
29 | }
--------------------------------------------------------------------------------
/src/UseCases/ClarcProvider/AppendUseCase/ClarcProviderAppendUseCaseInputData.php:
--------------------------------------------------------------------------------
1 | clarcProviderCode = $clarcProviderCode;
62 | $this->clarcDebugProviderCode = $clarcDebugProviderCode;
63 | $this->identifer = $identifer;
64 | $this->inputPortName = $inputPortName;
65 | $this->interactorName = $interactorName;
66 | $this->outputPortName = $outputPortName;
67 | $this->presenterName = $presenterName;
68 | }
69 | }
--------------------------------------------------------------------------------
/src/UseCases/ClarcProvider/AppendUseCase/ClarcProviderAppendUseCaseInputPortInterface.php:
--------------------------------------------------------------------------------
1 | outputPort = $outputPort;
26 | }
27 |
28 |
29 | function handle(ClarcProviderAppendUseCaseInputData $inputData)
30 | {
31 | $parser = new ClassParser();
32 | $classMeta = $parser->parse($inputData->clarcProviderCode);
33 |
34 | $classMeta->setupMethods()
35 | ->updateMethod('registerUseCases', function ($definition) use ($inputData) {
36 | $body = $definition->getBody();
37 | $definition->clearBody();
38 |
39 | $scripts = $this->divideScripts($body);
40 |
41 | $comment = '// ' . $inputData->identifer;
42 | $inputPortBindCode = $this->makeBindCode($inputData->inputPortName, $inputData->interactorName);
43 | $outputPortBindCode = $this->makeBindCode($inputData->outputPortName, $inputData->presenterName);
44 | $script = new UseCaseSettingScript($comment, $inputPortBindCode, $outputPortBindCode);
45 |
46 | if (!$this->exists($scripts, $script)) {
47 | array_push($scripts, $script);
48 | }
49 |
50 | usort($scripts, function ($l, $r) {
51 | return $l->getKey() < $r->getKey() ? -1 : 1;
52 | });
53 |
54 | foreach ($scripts as $script) {
55 | $lines = $script->getScripts();
56 | foreach ($lines as $line)
57 | {
58 | $definition->addBody($line);
59 | }
60 | }
61 | });
62 |
63 | $renderer = new ClassRenderer();
64 | $rendered = $renderer->render($classMeta);
65 |
66 | $outputData = new ClarcProviderAppendUseCaseOutputData($rendered);
67 |
68 | $this->outputPort->output($outputData);
69 | }
70 |
71 | private function makeBindCode(string $interfaceName, string $implementName)
72 | {
73 | return '$this->app->bind( ' . $interfaceName . '::class, ' . $implementName . '::class);';
74 | }
75 |
76 | /**
77 | * @param array $lines
78 | * @return AppendUseCaseScriptInterface[]
79 | * @throws \Exception
80 | */
81 | private function divideScripts(array $lines): array
82 | {
83 | $results = [];
84 | $lineCount = count($lines);
85 | for ($i = 0; $i < $lineCount; $i++)
86 | {
87 | $line = $lines[$i];
88 | if ($this->startsWith($line, '//')) {
89 | $comment = $line;
90 | $bindInputPort = $lines[++$i];
91 | $bindOutputPort = $lines[++$i];
92 | array_push($results, new UseCaseSettingScript($comment, $bindInputPort, $bindOutputPort));
93 | } else if ($this->startsWith($line, '$this->app->bind(')) {
94 | $bindInputPort = $line;
95 | $bindOutputPort = $lines[++$i];
96 | array_push($results, new UseCaseSettingScript(null, $bindInputPort, $bindOutputPort));
97 | } else {
98 | throw new \Exception();
99 | }
100 | }
101 |
102 | return $results;
103 | }
104 |
105 | private function exists(array $scripts, UseCaseSettingScript $script): bool
106 | {
107 | $duplicates = array_filter($scripts, function ($s) use ($script) { return $s->getKey() === $script->getKey(); });
108 | return !empty($duplicates);
109 | }
110 |
111 | private function startsWith(string $target, $word): bool
112 | {
113 | return strpos($target, $word) === 0;
114 | }
115 | }
116 |
117 | class UseCaseSetting
118 | {
119 | public $comment;
120 | public $bindInputPort;
121 | public $bindOutputPort;
122 |
123 | /**
124 | * UseCaseSetting constructor.
125 | * @param $comment
126 | * @param $bindInputPort
127 | * @param $bindOutputPort
128 | */
129 | public function __construct($comment, $bindInputPort, $bindOutputPort)
130 | {
131 | $this->comment = $comment;
132 | $this->bindInputPort = $bindInputPort;
133 | $this->bindOutputPort = $bindOutputPort;
134 | }
135 | }
--------------------------------------------------------------------------------
/src/UseCases/ClarcProvider/AppendUseCase/ClarcProviderAppendUseCaseOutputData.php:
--------------------------------------------------------------------------------
1 | clarcProviderCode = $clarcProviderCode;
21 | }
22 |
23 | /**
24 | * @return mixed
25 | */
26 | public function getClarcProviderCode()
27 | {
28 | return $this->clarcProviderCode;
29 | }
30 | }
--------------------------------------------------------------------------------
/src/UseCases/ClarcProvider/AppendUseCase/ClarcProviderAppendUseCaseOutputPortInterface.php:
--------------------------------------------------------------------------------
1 | commentLine = $commentLine;
22 | $this->bindInputPortLine = $bindInputPortLine;
23 | $this->bindOutputPortLine = $bindOutputPortLine;
24 | }
25 |
26 | /**
27 | * @return string
28 | */
29 | function getKey(): string
30 | {
31 | if (is_null($this->commentLine)) {
32 | return '';
33 | }
34 |
35 | return $this->commentLine;
36 | }
37 |
38 | /**
39 | * @return string[]
40 | */
41 | function getScripts(): array
42 | {
43 | if (is_null($this->commentLine)) {
44 | return [$this->bindInputPortLine, $this->bindOutputPortLine];
45 | }
46 |
47 | return [$this->commentLine, $this->bindInputPortLine, $this->bindOutputPortLine];
48 | }
49 | }
--------------------------------------------------------------------------------
/tests/BasicCodeGenerate/BasicCodeGenerateTest.php:
--------------------------------------------------------------------------------
1 | controllerContents(),
35 | $actionName,
36 | $inputDataFields,
37 | $outputDataFields);
38 | $interactor->handle($inputData);
39 | $outputData = $presenter->getOutputData();
40 |
41 | $projectPath = '..\\StrictLaraClean\\src';
42 | $packagePath = $projectPath . '\\packages';
43 |
44 | $directorySuffix = $controllerName . '\\' . $actionName;
45 |
46 | $controllerPath = $projectPath . '\\app\\Http\\Controllers';
47 | if (!file_exists($controllerPath)) {
48 | mkdir($controllerPath, "0777", true);
49 | }
50 | $inputPortPath = $packagePath . '\\InputPorts\\' . $directorySuffix;
51 | if (!file_exists($inputPortPath)) {
52 | mkdir($inputPortPath, "0777", true);
53 | }
54 | $interactorPath = $packagePath . '\\Interactors\\' . $directorySuffix;
55 | if (!file_exists($interactorPath)) {
56 | mkdir($interactorPath, "0777", true);
57 | }
58 | $outputPortPath = $packagePath . '\\OutputPorts\\' . $directorySuffix;
59 | if (!file_exists($outputPortPath)) {
60 | mkdir($outputPortPath, "0777", true);
61 | }
62 | $presenterPath = $projectPath . '\\app\\Http\\Presenters\\' . $controllerName;
63 | if (!file_exists($presenterPath)) {
64 | mkdir($presenterPath, "0777", true);
65 | }
66 |
67 | $usecaseName = $controllerName . $actionName;
68 |
69 | file_put_contents($controllerPath . '\\' . $controllerName . 'Controller.php', $outputData->getControllerSourceFile()->getContents());
70 | file_put_contents($inputPortPath . '\\' . $usecaseName . 'InputPortInterface.php', $outputData->getInputPortSourceFile()->getContents());
71 | file_put_contents($inputPortPath . '\\' . $usecaseName . 'InputData.php', $outputData->getInputDataSourceFile()->getContents());
72 | file_put_contents($interactorPath . '\\' . $usecaseName . 'Interactor.php', $outputData->getInteractorSourceFile()->getContents());
73 | file_put_contents($outputPortPath . '\\' . $usecaseName . 'OutputPortInterface.php', $outputData->getOutputPortSourceFile()->getContents());
74 | file_put_contents($outputPortPath . '\\' . $usecaseName . 'OutputData.php', $outputData->getOutputDataSourceFile()->getContents());
75 | file_put_contents($presenterPath . '\\' . $usecaseName . 'Presenter.php', $outputData->getPresenterSourceFile()->getContents());
76 |
77 | // echo $outputData->getControllerSourceFile()->getContents();
78 | // echo $outputData->getInputPortSourceFile()->getContents();
79 | // echo $outputData->getInteractorSourceFile()->getContents();
80 | // echo $outputData->getInputDataSourceFile()->getContents();
81 | // echo $outputData->getOutputPortSourceFile()->getContents();
82 | // echo $outputData->getOutputDataSourceFile()->getContents();
83 | // echo $outputData->getPresenterSourceFile()->getContents();
84 | }
85 |
86 | private function controllerContents()
87 | {
88 | return
89 | 'outputData;
23 | }
24 |
25 | function output(ClarcObjectCreateOutputData $outputData)
26 | {
27 | $this->outputData = $outputData;
28 | }
29 | }
--------------------------------------------------------------------------------
/tests/ClarcProviderAppendUseCaseInteractor/ClarcProviderAppendUseCaseInteractorTest.php:
--------------------------------------------------------------------------------
1 | handle($inputData);
32 | $outputdata = $presenter->outputData;
33 |
34 | file_put_contents($providerPath . '\\ClarcProvider.txt', $outputdata->getClarcProviderCode());
35 | }
36 | }
--------------------------------------------------------------------------------
/tests/ClarcProviderAppendUseCaseInteractor/TestClarcProviderAppendUseCaseUseCasePresenter.php:
--------------------------------------------------------------------------------
1 | outputData = $outputData;
20 | }
21 | }
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |