diff --git a/.docs/README.md b/.docs/README.md new file mode 100644 index 0000000..398d2d5 --- /dev/null +++ b/.docs/README.md @@ -0,0 +1,34 @@ +# Mate + +> Yummy opinionated PHP generator for web masters. + +## Installation + +```bash +composer require contributte/mate --dev +``` + +## Quickstart + +1. Create `.mate.neon` in your project root. + +```neon +data: + user: + fields: + username: {type: string} + email: {type: string} + password: {type: string} + createdAt: {type: Nette\Utils\DateTime} + updatedAt: {type: Nette\Utils\DateTime} +``` + +2. Run `vendor/bin/mate` or `php mate.phar` in your project root. + +``` +php mate.phar craft +``` + +## Configuration + +Under construction. diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a828607 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# EditorConfig is awesome: http://EditorConfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = tab +indent_size = tab +tab_width = 4 + +[{*.json,*.yml,*.yaml,*.md}] +indent_style = space +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..12910b6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# Not archived +.docs export-ignore +tests export-ignore +.editorconfig export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.travis.yml export-ignore +Makefile export-ignore +phpstan.neon export-ignore +README.md export-ignore +ruleset.xml export-ignore diff --git a/.github/.kodiak.toml b/.github/.kodiak.toml new file mode 100644 index 0000000..60c34b6 --- /dev/null +++ b/.github/.kodiak.toml @@ -0,0 +1,10 @@ +version = 1 + +[merge] +automerge_label = "automerge" +blacklist_title_regex = "^WIP.*" +blacklist_labels = ["WIP"] +method = "rebase" +delete_branch_on_merge = true +notify_on_conflict = true +optimistic_updates = false diff --git a/.github/workflows/codesniffer.yml b/.github/workflows/codesniffer.yml new file mode 100644 index 0000000..dfc76ff --- /dev/null +++ b/.github/workflows/codesniffer.yml @@ -0,0 +1,15 @@ +name: "Codesniffer" + +on: + pull_request: + + push: + branches: ["*"] + + schedule: + - cron: "0 8 * * 1" + +jobs: + codesniffer: + name: "Codesniffer" + uses: contributte/.github/.github/workflows/codesniffer.yml@v1 diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml new file mode 100644 index 0000000..db3ad34 --- /dev/null +++ b/.github/workflows/phpstan.yml @@ -0,0 +1,15 @@ +name: "Phpstan" + +on: + pull_request: + + push: + branches: ["*"] + + schedule: + - cron: "0 8 * * 1" + +jobs: + phpstan: + name: "Phpstan" + uses: contributte/.github/.github/workflows/phpstan.yml@v1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..af0754b --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,30 @@ +name: "Nette Tester" + +on: + pull_request: + + push: + branches: [ "*" ] + + schedule: + - cron: "0 8 * * 1" + +jobs: + test81: + name: "Nette Tester" + uses: contributte/.github/.github/workflows/nette-tester.yml@v1 + with: + php: "8.1" + + test80: + name: "Nette Tester" + uses: contributte/.github/.github/workflows/nette-tester.yml@v1 + with: + php: "8.0" + + testlower: + name: "Nette Tester" + uses: contributte/.github/.github/workflows/nette-tester.yml@v1 + with: + php: "8.0" + composer: "composer update --no-interaction --no-progress --prefer-dist --prefer-stable --prefer-lowest" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b0fa45a --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# IDE +/.idea + +# Composer +/vendor +/composer.lock + +# Tests +/coverage.xml diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5d90994 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Contributte + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..41f1ea5 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +.PHONY: install qa cs csf phpstan tests coverage + +install: + composer update + +qa: phpstan cs + +cs: +ifdef GITHUB_ACTION + vendor/bin/phpcs --standard=ruleset.xml --encoding=utf-8 --colors -nsp -q --report=checkstyle src tests | cs2pr +else + vendor/bin/phpcs --standard=ruleset.xml --encoding=utf-8 --colors -nsp src tests +endif + +csf: + vendor/bin/phpcbf --standard=ruleset.xml --encoding=utf-8 --colors -nsp src tests + +phpstan: + vendor/bin/phpstan analyse -c phpstan.neon + +tests: + vendor/bin/tester -s -p php --colors 1 -C tests/Cases + +coverage: +ifdef GITHUB_ACTION + vendor/bin/tester -s -p phpdbg --colors 1 -C --coverage coverage.xml --coverage-src src tests/Cases +else + vendor/bin/tester -s -p phpdbg --colors 1 -C --coverage coverage.html --coverage-src src tests/Cases +endif + +compile: + diff --git a/README.md b/README.md new file mode 100644 index 0000000..91f6a0a --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +![](https://heatbadger.now.sh/github/readme/contributte/mate/) + +

+ + + + +

+

+ + + + + +

+ +

+Website 🚀 contributte.org | Contact 👨🏻‍💻 f3l1x.io | Twitter 🐦 @contributte +

+ +## Usage + +To install the latest version of `contributte/mate` use [Composer](https://getcomposer.org). + +``` +composer require contributte/mate +``` + +## Documentation + +For details on how to use this package, check out our [documentation](.docs). + +## Version + +| State | Version | Branch | PHP | +|--------|---------|----------|---------| +| dev | `^0.1` | `master` | `>=8.2` | +| stable | `^0.1` | `master` | `>=8.2` | + +## Development + +See [how to contribute](https://contributte.org/contributing.html) to this package. + +This package is currently maintaining by these authors. + + + + + +----- + +Consider to [support](https://contributte.org/partners.html) **contributte** development team. +Also thank you for using this package. diff --git a/bin/mate b/bin/mate new file mode 100755 index 0000000..744b93d --- /dev/null +++ b/bin/mate @@ -0,0 +1,4 @@ +#!/usr/bin/env php +=8.2", + "nette/php-generator": "^4.1.6", + "nette/utils": "^4.0.5", + "nette/neon": "^3.4.3", + "nette/schema": "^1.3.0", + "latte/latte": "^3.0.18", + "symfony/console": "^7.1.5" + }, + "require-dev": { + "contributte/qa": "^0.4.0", + "contributte/tester": "^0.1.0", + "mockery/mockery": "^1.5.0", + "phpstan/phpstan": "^1.2.0", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-nette": "^1.0.0", + "phpstan/phpstan-strict-rules": "^1.1.0", + "humbug/box": "^4.6.2" + }, + "autoload": { + "psr-4": { + "Contributte\\Mate\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests" + } + }, + "minimum-stability": "dev", + "prefer-stable": true, + "config": { + "sort-packages": true, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + }, + "extra": { + "branch-alias": { + "dev-master": "0.1.x-dev" + } + } +} diff --git a/dist/.gitignore b/dist/.gitignore new file mode 100644 index 0000000..78d9101 --- /dev/null +++ b/dist/.gitignore @@ -0,0 +1,2 @@ +/* +!.gitignore diff --git a/examples/d01/.mate.neon b/examples/d01/.mate.neon new file mode 100644 index 0000000..e96f693 --- /dev/null +++ b/examples/d01/.mate.neon @@ -0,0 +1,8 @@ +data: + user: + fields: + username: {type: string} + email: {type: string} + password: {type: string} + createdAt: {type: Nette\Utils\DateTime} + updatedAt: {type: Nette\Utils\DateTime} diff --git a/examples/d01/app/Domain/User/CreateUserCommand.php b/examples/d01/app/Domain/User/CreateUserCommand.php new file mode 100644 index 0000000..c933f3f --- /dev/null +++ b/examples/d01/app/Domain/User/CreateUserCommand.php @@ -0,0 +1,17 @@ +username, + email: $command->email, + password: $command->password, + createdAt: $command->createdAt, + updatedAt: $command->updatedAt, + ); + + + $this->em->persist($entity); + $this->em->flush(); + + + return $entity; + } +} diff --git a/examples/d02/.mate.neon b/examples/d02/.mate.neon new file mode 100644 index 0000000..f03e385 --- /dev/null +++ b/examples/d02/.mate.neon @@ -0,0 +1,50 @@ +app: + namespace: App + + class: + entity: + baseClass: App\Model\Database\Entity\AbstractEntity + repository: + baseClass: App\Model\Database\Repository\AbstractRepository + handler: + dependencies: + em: Doctrine\ORM\EntityManagerInterface + +data: + user: + class: + # Database + - { type: entity, class: App\Domain\User\Database\User } + - { type: repository, class: App\Domain\User\Database\UserRepository } + # CQRS + - { type: command, class: App\Domain\User\CreateUserCommand } + - { type: handler, class: App\Domain\User\CreateUserHandler } + # API (create) + - { type: controller, flavor: create, class: App\Api\User\Create\CreateUserController } + - { type: request, flavor: create, class: App\Api\User\Create\CreateUserRequest } + - { type: request_body, flavor: create, class: App\Api\User\Create\CreateUserBody } + - { type: response, flavor: create, class: App\Api\User\Create\CreateUserResponse } + # API (update) + - { type: controller, flavor: update, class: App\Api\User\Update\UpdateUserController } + - { type: request, flavor: update, class: App\Api\User\Update\UpdateUserRequest } + - { type: request_body, flavor: update, class: App\Api\User\Update\UpdateUserBody } + - { type: response, flavor: update, class: App\Api\User\Update\UpdateUserResponse } + # API (get) + - { type: controller, flavor: get, class: App\Api\User\Get\GetUserController } + - { type: request, flavor: get, class: App\Api\User\Get\GetUserRequest } + - { type: response, flavor: get, class: App\Api\User\Get\GetUserResponse } + # API (list) + - { type: controller, flavor: list, class: App\Api\User\List\ListUserController } + - { type: request, flavor: list,class: App\Api\User\List\ListUserRequest } + - { type: request_filter, flavor: list,class: App\Api\User\List\ListUserFilter } + - { type: response, flavor: list,class: App\Api\User\List\ListUserResponse } + # API (delete) + - { type: controller, flavor: delete, class: App\Api\User\Delete\DeleteUserController } + - { type: response, flavor: delete,class: App\Api\User\Delete\DeleteUserResponse } + + fields: + username: {type: string} + email: {type: string} + password: {type: string} + createdAt: {type: Nette\Utils\DateTime} + updatedAt: {type: Nette\Utils\DateTime} diff --git a/examples/d02/app/Domain/User/CreateUserCommand.php b/examples/d02/app/Domain/User/CreateUserCommand.php new file mode 100644 index 0000000..c933f3f --- /dev/null +++ b/examples/d02/app/Domain/User/CreateUserCommand.php @@ -0,0 +1,17 @@ +username, + email: $command->email, + password: $command->password, + createdAt: $command->createdAt, + updatedAt: $command->updatedAt, + ); + + + $this->em->persist($entity); + $this->em->flush(); + + + return $entity; + } +} diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..cbfddc9 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,20 @@ +includes: + - vendor/phpstan/phpstan-deprecation-rules/rules.neon + - vendor/phpstan/phpstan-nette/extension.neon + - vendor/phpstan/phpstan-nette/rules.neon + - vendor/phpstan/phpstan-strict-rules/rules.neon + +parameters: + level: 9 + phpVersion: 80200 + + scanDirectories: + - src + + fileExtensions: + - php + + paths: + - src + + ignoreErrors: diff --git a/resources/templates/_defaults/LICENSE.latte b/resources/templates/_defaults/LICENSE.latte new file mode 100644 index 0000000..4337da9 --- /dev/null +++ b/resources/templates/_defaults/LICENSE.latte @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) {{=date('Y')} Contributte + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/resources/templates/_defaults/Makefile.latte b/resources/templates/_defaults/Makefile.latte new file mode 100644 index 0000000..fbf63e6 --- /dev/null +++ b/resources/templates/_defaults/Makefile.latte @@ -0,0 +1,56 @@ +############################################################ +# PROJECT ################################################## +############################################################ +.PHONY: project install setup clean + +project: install setup + +install: +composer install + +setup: +mkdir -p var/tmp var/log +chmod +0777 var/tmp var/log + +clean: +find var/tmp -mindepth 1 ! -name '.gitignore' -type f,d -exec rm -rf {} + +find var/log -mindepth 1 ! -name '.gitignore' -type f,d -exec rm -rf {} + + +############################################################ +# DEVELOPMENT ############################################## +############################################################ +.PHONY: qa dev cs csf phpstan tests coverage dev build + +qa: cs phpstan + +cs: +vendor/bin/codesniffer app + +csf: +vendor/bin/codefixer app + +phpstan: +vendor/bin/phpstan analyse -c phpstan.neon --memory-limit=512M + +tests: +echo "NO TESTS" + +coverage: +echo "NO TESTS" + +dev: +NETTE_DEBUG=1 NETTE_ENV=dev php -S 0.0.0.0:8000 -t www + +build: +echo "OK" + +############################################################ +# DEPLOYMENT ############################################### +############################################################ +.PHONY: deploy + +deploy: +$(MAKE) clean +$(MAKE) project +$(MAKE) build +$(MAKE) clean diff --git a/resources/templates/_defaults/README.latte b/resources/templates/_defaults/README.latte new file mode 100644 index 0000000..acef429 --- /dev/null +++ b/resources/templates/_defaults/README.latte @@ -0,0 +1 @@ +# Generated diff --git a/resources/templates/_defaults/editorconfig.latte b/resources/templates/_defaults/editorconfig.latte new file mode 100644 index 0000000..3faf149 --- /dev/null +++ b/resources/templates/_defaults/editorconfig.latte @@ -0,0 +1,16 @@ +# EditorConfig is awesome: http://EditorConfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = tab +indent_size = tab +tab_width = 4 + +[{*.json, *.yaml, *.yml, *.md}] +indent_style = space +indent_size = 2 diff --git a/resources/templates/_defaults/github/codesniffer.latte b/resources/templates/_defaults/github/codesniffer.latte new file mode 100644 index 0000000..dfc76ff --- /dev/null +++ b/resources/templates/_defaults/github/codesniffer.latte @@ -0,0 +1,15 @@ +name: "Codesniffer" + +on: + pull_request: + + push: + branches: ["*"] + + schedule: + - cron: "0 8 * * 1" + +jobs: + codesniffer: + name: "Codesniffer" + uses: contributte/.github/.github/workflows/codesniffer.yml@v1 diff --git a/resources/templates/_defaults/github/dependabot.latte b/resources/templates/_defaults/github/dependabot.latte new file mode 100644 index 0000000..0f0dadb --- /dev/null +++ b/resources/templates/_defaults/github/dependabot.latte @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: composer + directory: "/" + schedule: + interval: daily + labels: + - "dependencies" + - "automerge" diff --git a/resources/templates/_defaults/github/kodiak.latte b/resources/templates/_defaults/github/kodiak.latte new file mode 100644 index 0000000..60c34b6 --- /dev/null +++ b/resources/templates/_defaults/github/kodiak.latte @@ -0,0 +1,10 @@ +version = 1 + +[merge] +automerge_label = "automerge" +blacklist_title_regex = "^WIP.*" +blacklist_labels = ["WIP"] +method = "rebase" +delete_branch_on_merge = true +notify_on_conflict = true +optimistic_updates = false diff --git a/resources/templates/_defaults/github/phpstan.latte b/resources/templates/_defaults/github/phpstan.latte new file mode 100644 index 0000000..db3ad34 --- /dev/null +++ b/resources/templates/_defaults/github/phpstan.latte @@ -0,0 +1,15 @@ +name: "Phpstan" + +on: + pull_request: + + push: + branches: ["*"] + + schedule: + - cron: "0 8 * * 1" + +jobs: + phpstan: + name: "Phpstan" + uses: contributte/.github/.github/workflows/phpstan.yml@v1 diff --git a/resources/templates/_defaults/github/tests.latte b/resources/templates/_defaults/github/tests.latte new file mode 100644 index 0000000..5fa700c --- /dev/null +++ b/resources/templates/_defaults/github/tests.latte @@ -0,0 +1,23 @@ +name: "Nette Tester" + +on: + pull_request: + + push: + branches: ["*"] + + schedule: + - cron: "0 8 * * 1" + +jobs: + test81: + name: "Nette Tester" + uses: contributte/.github/.github/workflows/nette-tester.yml@v1 + with: + php: "8.1" + + test80: + name: "Nette Tester" + uses: contributte/.github/.github/workflows/nette-tester.yml@v1 + with: + php: "8.0" diff --git a/resources/templates/_defaults/gitignore.latte b/resources/templates/_defaults/gitignore.latte new file mode 100644 index 0000000..ea5e488 --- /dev/null +++ b/resources/templates/_defaults/gitignore.latte @@ -0,0 +1,11 @@ +# IDE +.idea + +# Composer +/vendor/* + +# Apache +!.htaccess + +# Nette config +/config/local.neon diff --git a/resources/templates/_defaults/phpstan.latte b/resources/templates/_defaults/phpstan.latte new file mode 100644 index 0000000..b5a5289 --- /dev/null +++ b/resources/templates/_defaults/phpstan.latte @@ -0,0 +1,20 @@ +includes: + - vendor/phpstan/phpstan-nette/extension.neon + - vendor/phpstan/phpstan-nette/rules.neon + - vendor/phpstan/phpstan-doctrine/extension.neon + - vendor/phpstan/phpstan-phpunit/rules.neon + +parameters: + level: 9 + phpVersion: 80000 + + scanDirectories: + - app + + fileExtensions: + - php + + paths: + - app + + ignoreErrors: diff --git a/resources/templates/_defaults/ruleset.latte b/resources/templates/_defaults/ruleset.latte new file mode 100644 index 0000000..b076ae3 --- /dev/null +++ b/resources/templates/_defaults/ruleset.latte @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/resources/templates/_defaults/var/gitignore.latte b/resources/templates/_defaults/var/gitignore.latte new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/resources/templates/_defaults/var/gitignore.latte @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/resources/templates/_defaults/www/htaccess.latte b/resources/templates/_defaults/www/htaccess.latte new file mode 100644 index 0000000..da941ae --- /dev/null +++ b/resources/templates/_defaults/www/htaccess.latte @@ -0,0 +1,31 @@ +# Apache configuration file (see httpd.apache.org/docs/current/mod/quickreference.html) + +# disable directory listing + + Options -Indexes + + +# enable cool URL + + RewriteEngine On + # RewriteBase / + + # use HTTPS + # RewriteCond %{HTTPS} !on + # RewriteRule .? https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] + + # prevents files starting with dot to be viewed by browser + RewriteRule /\.|^\.(?!well-known/) - [F] + + # front controller + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule !\.(pdf|js|ico|gif|jpg|png|css|rar|zip|tar\.gz|map)$ index.php [L] + + +# enable gzip compression + + + AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/javascript application/json application/xml image/svg+xml + + diff --git a/resources/templates/demo/app/Bootstrap.php b/resources/templates/demo/app/Bootstrap.php new file mode 100644 index 0000000..06afd25 --- /dev/null +++ b/resources/templates/demo/app/Bootstrap.php @@ -0,0 +1,28 @@ +use(NellaPreset::create(__DIR__)) + ->boot(); + } + + public static function run(): void + { + self::boot() + ->createContainer() + ->getByType(Application::class) + ->run(); + } + +} diff --git a/resources/templates/demo/app/UI/@Templates/@layout.latte b/resources/templates/demo/app/UI/@Templates/@layout.latte new file mode 100644 index 0000000..42775b3 --- /dev/null +++ b/resources/templates/demo/app/UI/@Templates/@layout.latte @@ -0,0 +1,40 @@ + + + + + + + {block #title|striptags}Nella Skeleton{/} + + + + + +
+
+
+

+ Nella Skeleton +

+
+ +
+
+ {$flash->message} +
+
+ +
+ {include #content} +
+ +
+ © {=date(Y)} by @f3l1x +
+
+
+ + + + + diff --git a/resources/templates/demo/app/UI/BasePresenter.php b/resources/templates/demo/app/UI/BasePresenter.php new file mode 100644 index 0000000..16b1f5b --- /dev/null +++ b/resources/templates/demo/app/UI/BasePresenter.php @@ -0,0 +1,10 @@ + + + + + + + + + + + + + + + + + /tests/tmp + diff --git a/src/Bootstrap.php b/src/Bootstrap.php new file mode 100644 index 0000000..3e10b1d --- /dev/null +++ b/src/Bootstrap.php @@ -0,0 +1,24 @@ +add(new CraftCommand()); + + return $application; + } + + public static function run(): void + { + self::boot()->run(); + } + +} diff --git a/src/Command/CraftCommand.php b/src/Command/CraftCommand.php new file mode 100644 index 0000000..6317b4e --- /dev/null +++ b/src/Command/CraftCommand.php @@ -0,0 +1,86 @@ +addOption('data', 'd', InputOption::VALUE_REQUIRED, 'Data structure reference'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $cwd = getcwd(); + + // Load config + $file = FileSystem::read($cwd . '/.mate.neon'); + + /** @var array{data: array}>} $config */ + $config = Neon::decode($file); + + // Validation + /** @var string|false|null $dataKey */ + $dataKey = $input->getOption('data'); + + if ($dataKey === null || $dataKey === false) { + $output->writeln('Missing --data option'); + + return Command::FAILURE; + } + + if (!isset($config['data'][$dataKey])) { + $output->writeln(sprintf('Unknown data reference "%s" in .mate.neon', $dataKey)); + + return Command::FAILURE; + } + + // Entity + $dataClass = ucfirst($dataKey); + + // Generators + $commandGenerator = new CommandGenerator(); + + foreach ($config['data'] as $data) { + $filename = $cwd . sprintf('/app/Domain/%s/Create%sCommand.php', $dataClass, $dataClass); + $generatedClass = $commandGenerator->generate( + namespace: sprintf('App\Domain\%s', $dataClass), + commandClass: sprintf('App\Domain\%s\Create%sCommand', $dataClass, $dataClass), + fields: $data['fields'] + ); + FileSystem::write($filename, $generatedClass); + } + + $handlerGenerator = new HandlerGenerator(); + + foreach ($config['data'] as $data) { + $filename = $cwd . sprintf('/app/Domain/%s/Create%sHandler.php', $dataClass, $dataClass); + $generatedClass = $handlerGenerator->generate( + namespace: sprintf('App\Domain\%s', $dataClass), + handlerClass: sprintf('App\Domain\%s\Create%sHandler', $dataClass, $dataClass), + commandClass: sprintf('App\Domain\%s\Create%sCommand', $dataClass, $dataClass), + entityClass: $dataClass, + fields: $data['fields'] + ); + FileSystem::write($filename, $generatedClass); + } + + return Command::SUCCESS; + } + +} diff --git a/src/Config/DataConfig.php b/src/Config/DataConfig.php new file mode 100644 index 0000000..95df142 --- /dev/null +++ b/src/Config/DataConfig.php @@ -0,0 +1,11 @@ + Expect::string()->required(), + 'email' => Expect::string()->required(), + 'password' => Expect::string()->required(), + ]) + ->castTo(self::class); + } + +} diff --git a/src/Generator/CommandGenerator.php b/src/Generator/CommandGenerator.php new file mode 100644 index 0000000..2f70a0f --- /dev/null +++ b/src/Generator/CommandGenerator.php @@ -0,0 +1,42 @@ + $fields + */ + public function generate( + string $namespace, + string $commandClass, + array $fields + ): string + { + $file = new PhpFile(); + $file->setStrictTypes(); + + $classNamespace = $file->addNamespace($namespace); + + $class = $classNamespace->addClass($classNamespace->simplifyType($commandClass)); + $class->setReadOnly(); + + $constructor = $class->addMethod('__construct'); + + foreach ($fields as $fieldName => $field) { + $parameter = $constructor->addPromotedParameter($fieldName); + $parameter->setPublic(); + $parameter->setType($field['type']); + $parameter->setReadOnly(); + } + + $printer = new Printer(); + + return $printer->printFile($file); + } + +} diff --git a/src/Generator/ControllerGenerator.php b/src/Generator/ControllerGenerator.php new file mode 100644 index 0000000..dea472f --- /dev/null +++ b/src/Generator/ControllerGenerator.php @@ -0,0 +1,19 @@ +printClass($class); + } + +} diff --git a/src/Generator/EntityGenerator.php b/src/Generator/EntityGenerator.php new file mode 100644 index 0000000..7581c57 --- /dev/null +++ b/src/Generator/EntityGenerator.php @@ -0,0 +1,19 @@ +printClass($class); + } + +} diff --git a/src/Generator/HandlerGenerator.php b/src/Generator/HandlerGenerator.php new file mode 100644 index 0000000..3c78a1c --- /dev/null +++ b/src/Generator/HandlerGenerator.php @@ -0,0 +1,65 @@ + $fields + */ + public function generate( + string $namespace, + string $handlerClass, + string $commandClass, + string $entityClass, + array $fields + ): string + { + $dumper = new Dumper(); + $file = new PhpFile(); + $file->setStrictTypes(); + + $classNamespace = $file->addNamespace($namespace); + + $class = $classNamespace->addClass($classNamespace->simplifyType($handlerClass)); + $class->setReadOnly(); + + $constructor = $class->addMethod('__construct'); + $paramEm = $constructor->addPromotedParameter('em'); + $paramEm->setPrivate(); + $paramEm->setType($classNamespace->simplifyType('Doctrine\ORM\EntityManagerInterface')); + + $invoke = $class->addMethod('__invoke'); + $invoke->setPublic(); + $invoke->setReturnType('object'); + $invoke->addParameter('command') + ->setType($commandClass); + + // Inner + $arguments = []; + + foreach ($fields as $fieldName => $field) { + $arguments[$fieldName] = new Literal('$command->' . $fieldName); + } + + $invoke->addBody('$entity = ?;', [ + new Literal($dumper->format('new ' . $entityClass . '(...?:)', $arguments)), + ]); + $invoke->addBody("\n"); + $invoke->addBody('$this->em->persist($entity);'); + $invoke->addBody('$this->em->flush();'); + $invoke->addBody("\n"); + $invoke->addBody('return $entity;'); + + $printer = new Printer(); + + return $printer->printFile($file); + } + +} diff --git a/src/Loader/MateLoader.php b/src/Loader/MateLoader.php new file mode 100644 index 0000000..052f78d --- /dev/null +++ b/src/Loader/MateLoader.php @@ -0,0 +1,8 @@ +