diff --git a/README.md b/README.md index 6ae4e09..96ff7e0 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,16 @@ Documentation is available at: https://docs.dotkernel.org/dot-maker/. ## Badges ![OSS Lifecycle](https://img.shields.io/osslifecycle/dotkernel/dot-maker) -![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-maker/1.1.0) +![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-maker/2.0.0) [![GitHub issues](https://img.shields.io/github/issues/dotkernel/dot-maker)](https://github.com/dotkernel/dot-maker/issues) [![GitHub forks](https://img.shields.io/github/forks/dotkernel/dot-maker)](https://github.com/dotkernel/dot-maker/network) [![GitHub stars](https://img.shields.io/github/stars/dotkernel/dot-maker)](https://github.com/dotkernel/dot-maker/stargazers) -[![GitHub license](https://img.shields.io/github/license/dotkernel/dot-maker)](https://github.com/dotkernel/dot-maker/blob/1.0/LICENSE) +[![GitHub license](https://img.shields.io/github/license/dotkernel/dot-maker)](https://github.com/dotkernel/dot-maker/blob/2.0/LICENSE) -[![Build Static](https://github.com/dotkernel/dot-maker/actions/workflows/continuous-integration.yml/badge.svg?branch=1.0)](https://github.com/dotkernel/dot-maker/actions/workflows/continuous-integration.yml) +[![Build Static](https://github.com/dotkernel/dot-maker/actions/workflows/continuous-integration.yml/badge.svg?branch=2.0)](https://github.com/dotkernel/dot-maker/actions/workflows/continuous-integration.yml) [![codecov](https://codecov.io/gh/dotkernel/dot-maker/graph/badge.svg?token=KT9UA402B4)](https://codecov.io/gh/dotkernel/dot-maker) -[![PHPStan](https://github.com/dotkernel/dot-maker/actions/workflows/static-analysis.yml/badge.svg?branch=1.0)](https://github.com/dotkernel/dot-maker/actions/workflows/static-analysis.yml) +[![PHPStan](https://github.com/dotkernel/dot-maker/actions/workflows/static-analysis.yml/badge.svg?branch=2.0)](https://github.com/dotkernel/dot-maker/actions/workflows/static-analysis.yml) ## Installation diff --git a/SECURITY.md b/SECURITY.md index b832122..3188de0 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,17 +2,15 @@ ## Supported Versions - | Version | Supported | PHP Version | |---------|--------------------|-----------------------------------------------------------------------------------------------------------| -| 1.x | :white_check_mark: | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-maker/1.0.0) | - +| 2.x | :white_check_mark: | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-maker/2.0.0) | +| 1.x | :white_check_mark: | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-maker/1.1.0) | ## Reporting Potential Security Issues -If you have encountered a potential security vulnerability in this project, -please report it to us at . We will work with you to -verify the vulnerability and patch it. +If you have encountered a potential security vulnerability in this project, please report it to us at . +We will work with you to verify the vulnerability and patch it. When reporting issues, please provide the following information: @@ -20,19 +18,12 @@ When reporting issues, please provide the following information: - A description indicating how to reproduce the issue - A summary of the security vulnerability and impact -We request that you contact us via the email address above and give the -project contributors a chance to resolve the vulnerability and issue a new -release prior to any public exposure; this helps protect the project's -users, and provides them with a chance to upgrade and/or update in order to -protect their applications. - +We request that you contact us via the email address above and give the project contributors a chance to resolve the vulnerability and issue a new release prior to any public exposure; +this helps protect the project's users and provides them with a chance to upgrade and/or update to protect their applications. ## Policy If we verify a reported security vulnerability, our policy is: -- We will patch the current release branch, as well as the immediate prior minor - release branch. - -- After patching the release branches, we will immediately issue new security - fix releases for each patched release branch. +- We will patch the current release branch, as well as the immediate prior minor release branch. +- After patching the release branches, we will immediately issue new security fix releases for each patched release branch. diff --git a/composer.json b/composer.json index dd9b9c3..82e7353 100644 --- a/composer.json +++ b/composer.json @@ -33,9 +33,9 @@ "require-dev": { "laminas/laminas-coding-standard": "^3.1.0", "mikey179/vfsstream": "^1.6.12", - "phpstan/phpstan": "^2.1.17", - "phpstan/phpstan-phpunit": "^2.0.6", - "phpunit/phpunit": "^10.5.47" + "phpstan/phpstan": "^2.1.33", + "phpstan/phpstan-phpunit": "^2.0.10", + "phpunit/phpunit": "^10.5.60" }, "scripts": { "check": [ diff --git a/docs/book/v2/component/collection.md b/docs/book/v2/component/collection.md new file mode 100644 index 0000000..ac80684 --- /dev/null +++ b/docs/book/v2/component/collection.md @@ -0,0 +1,67 @@ +# Create Collection + +> This page assumes that you have created a Composer "make" script as described on the [Setup page](../setup.md#add-dot-maker-to-composerjson). + +> Collections can be created only in APIs. + +## Run the command + +To create a Collection, use either of the following commands: + +```shell +composer make collection +``` + +OR + +```shell +./vendor/bin/dot-maker collection +``` + +## Identify the target module + +`dot-maker` needs to know in which module you want to create the new Collection. +To determine this, it will prompt you to enter the name of an existing module: + +> Existing module name: + +If you input a module name which does not exist (like, "NonExistentModule"), an error will be thrown: + +> Module "NonExistentModule" not found + +and will keep prompting for a valid module name until you provide one. + +Once an existing module name (like, "ExistingModule") is provided, `dot-maker` will output a success message: + +> Found Module "ExistingModule" + +## Name the Collection + +Once the target module has been identified, you will be prompted to input a name for the Collection: + +> Collection name: + +**The name must contain only letters and numbers.** + +> You don't have to append "Collection" to the name. +> It is automatically appended when necessary. +> See our [Naming Standards](../naming-standards.md) page for more information. + +If you leave the name blank, the process will exit. + +If you input an invalid name (like, "."), an error will be thrown: + +> Invalid Collection name: "." + +If you input the name of an existing Collection (like, "ExistingCollection"), an error will be thrown: + +> Class "ExistingCollection" already exists at /path/to/project/src/ExistingModule/src/Collection/ExistingCollection.php + +If you input a valid name (like, "NewCollection"), `dot-maker` will create the Collection and output a success message: + +> Created Collection: /path/to/project/src/ExistingModule/src/Collection/NewCollection.php + +## Create multiple Collections + +To allow the creation of multiple Collections, the process will loop until you leave the name blank. +Each iteration creates a new Collection under the same module. diff --git a/docs/book/v2/component/command.md b/docs/book/v2/component/command.md new file mode 100644 index 0000000..c054a97 --- /dev/null +++ b/docs/book/v2/component/command.md @@ -0,0 +1,68 @@ +# Create Command + +> This page assumes that you have created a Composer "make" script as described on the [Setup page](../setup.md#add-dot-maker-to-composerjson). + +> `dot-maker` will look for a matching ServiceInterface in the module (e.g.: BookServiceInterface — BookCommand). +> If it finds one, it will automatically inject it into the Command. + +To create a Command, use either of the following commands: + +## Run the command + +```shell +composer make command +``` + +OR + +```shell +./vendor/bin/dot-maker command +``` + +## Identify the target module + +`dot-maker` needs to know in which module you want to create the new Command. +To determine this, it will prompt you to enter the name of an existing module: + +> Existing module name: + +If you input a module name which does not exist (like, "NonExistentModule"), an error will be thrown: + +> Module "NonExistentModule" not found + +and will keep prompting for a valid module name until you provide one. + +Once an existing module name (like, "ExistingModule") is provided, `dot-maker` will output a success message: + +> Found Module "ExistingModule" + +## Name the Command + +Once the target module has been identified, you will be prompted to input a name for the Command: + +> Command name: + +**The name must contain only letters and numbers.** + +> You don't have to append "Command" to the name. +> It is automatically appended when necessary. +> See our [Naming Standards](../naming-standards.md) page for more information. + +If you leave the name blank, the process will exit. + +If you input an invalid name (like, "."), an error will be thrown: + +> Invalid Command name: "." + +If you input the name of an existing Command (like, "ExistingCommand"), an error will be thrown: + +> Class "ExistingCommand" already exists at /path/to/project/src/ExistingModule/src/Command/ExistingCommand.php + +If you input a valid name (like, "NewCommand"), `dot-maker` will create the Command and output a success message: + +> Created Command: /path/to/project/src/ExistingModule/src/Command/NewCommand.php + +## Create multiple Commands + +To allow the creation of multiple Commands, the process will loop until you leave the name blank. +Each iteration creates a new Command under the same module. diff --git a/docs/book/v2/component/entity.md b/docs/book/v2/component/entity.md new file mode 100644 index 0000000..bfd7ce3 --- /dev/null +++ b/docs/book/v2/component/entity.md @@ -0,0 +1,79 @@ +# Create Entity + +> This page assumes that you have created a Composer "make" script as described on the [Setup page](../setup.md#add-dot-maker-to-composerjson). + +## Run the command + +To create an Entity, use either of the following commands: + +```shell +composer make entity +``` + +OR + +```shell +./vendor/bin/dot-maker entity +``` + +## Identify the target module + +`dot-maker` needs to know in which module you want to create the new Entity. +To determine this, it will prompt you to enter the name of an existing module: + +> Existing module name: + +If you input a module name which does not exist (like, "NonExistentModule"), an error will be thrown: + +> Module "NonExistentModule" not found + +and will keep prompting for a valid module name until you provide one. + +Once an existing module name (like, "ExistingModule") is provided, `dot-maker` will output a success message: + +> Found Module "ExistingModule" + +## Name the Entity + +Once the target module has been identified, you will be prompted to input a name for the Entity: + +> Entity name: + +**The name must contain only letters and numbers.** + +> You don't have to append "Entity" to the name. +> It is automatically appended when necessary. +> See our [Naming Standards](../naming-standards.md) page for more information. + +If you leave the name blank, the process will exit. + +If you input an invalid name (like, "."), an error will be thrown: + +> Invalid Entity name: "." + +If you input the name of an existing Entity (like, "ExistingEntity"), an error will be thrown: + +- for projects compatible with the Core architecture: + +> Class "ExistingEntity" already exists at /path/to/project/src/Core/src/ExistingModule/src/Entity/ExistingEntity.php + +- for projects which are NOT compatible with the Core architecture: + +> Class "ExistingEntity" already exists at /path/to/project/src/ExistingModule/src/Entity/ExistingEntity.php + +If you input a valid name (like, "NewEntity"), `dot-maker` will create the Entity and output a success message: + +- for projects compatible with the Core architecture: + +> Created Entity: /path/to/project/src/Core/src/ExistingModule/src/Entity/NewEntity.php +> Created Repository: /path/to/project/src/Core/src/ExistingModule/src/Repository/NewEntityRepository.php + +- for projects which are NOT compatible with the Core architecture: + +> Created Entity: /path/to/project/src/ExistingModule/src/Entity/NewEntity.php +> Created Repository: /path/to/project/src/ExistingModule/src/Repository/NewEntityRepository.php + +## Create multiple Entities + +To allow the creation of multiple Entities, the process will loop until you leave the name blank. +Each iteration creates a new pair of Entity and Repository under the same module. diff --git a/docs/book/v2/component/form.md b/docs/book/v2/component/form.md new file mode 100644 index 0000000..c1cc2fe --- /dev/null +++ b/docs/book/v2/component/form.md @@ -0,0 +1,105 @@ +# Create Form + +> This page assumes that you have created a Composer "make" script as described on the [Setup page](../setup.md#add-dot-maker-to-composerjson). + +> Forms cannot be created in APIs. + +To create a Form, use either of the following commands: + +## Run the command + +```shell +composer make form +``` + +OR + +```shell +./vendor/bin/dot-maker form +``` + +## Identify the target module + +`dot-maker` needs to know in which module you want to create the new Form. +To determine this, it will prompt you to enter the name of an existing module: + +> Existing module name: + +If you input a module name which does not exist (like, "NonExistentModule"), an error will be thrown: + +> Module "NonExistentModule" not found + +and will keep prompting for a valid module name until you provide one. + +Once an existing module name (like, "ExistingModule") is provided, `dot-maker` will output a success message: + +> Found Module "ExistingModule" + +## Name the Form + +Once the target module has been identified, you will be prompted to input a name for the Form: + +> Form name: + +**The name must contain only letters and numbers.** + +> You don't have to append "Form" to the name. +> It is automatically appended when necessary. +> See our [Naming Standards](../naming-standards.md) page for more information. + +If you leave the name blank, the process will exit. + +If you input an invalid name (like, "."), an error will be thrown: + +> Invalid Form name: "." + +If you input the name of an existing Form (like, "ExistingForm"), an error will be thrown: + +> Class "ExistingForm" already exists at /path/to/project/src/ExistingModule/src/Form/ExistingForm.php + +Once you input a valid name (like, "Book"), `dot-maker` will prompt you to confirm which CRUD operations will be performed on the resource: + +### Create resources + +The prompt asks you whether you want to create resources: + +> Allow creating Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src/ExistingModule/src/Form/CreateBookForm.php` +- `src/ExistingModule/src/InputFilter/CreateBookInputFilter.php` + +Without confirmation, the process will skip to the next component. + +### Delete resources + +The prompt asks you whether you want to delete resources: + +> Allow deleting Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src/ExistingModule/src/Form/DeleteBookForm.php` +- `src/ExistingModule/src/InputFilter/DeleteBookInputFilter.php` +- `src/ExistingModule/src/InputFilter/Input/ConfirmDeleteBookInput.php` + +Without confirmation, the process will skip to the next component. + +### Edit resources + +The prompt asks you whether you want to edit resources: + +> Allow editing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src/ExistingModule/src/Form/EditBookForm.php` +- `src/ExistingModule/src/InputFilter/EditBookInputFilter.php` + +Without confirmation, the process will skip to the next component. + +## Create multiple Forms + +To allow the creation of multiple Forms, the process will loop until you leave the name blank. +Each iteration creates a new set of Forms, InputFilter and Inputs under the same module. diff --git a/docs/book/v2/component/handler.md b/docs/book/v2/component/handler.md new file mode 100644 index 0000000..695ca6f --- /dev/null +++ b/docs/book/v2/component/handler.md @@ -0,0 +1,243 @@ +# Create Handler + +> This page assumes that you have created a Composer "make" script as described on the [Setup page](../setup.md#add-dot-maker-to-composerjson). + +> `dot-maker` will look for a matching ServiceInterface in the module (e.g.: BookServiceInterface — BookHandler). +> If it finds one, it will automatically inject it into the Handler. + +To create a Handler, use either of the following commands: + +## Run the command + +```shell +composer make handler +``` + +OR + +```shell +./vendor/bin/dot-maker handler +``` + +## Identify the target module + +`dot-maker` needs to know in which module you want to create the new Handler. +To determine this, it will prompt you to enter the name of an existing module: + +> Existing module name: + +If you input a module name which does not exist (like, "NonExistentModule"), an error will be thrown: + +> Module "NonExistentModule" not found + +and will keep prompting for a valid module name until you provide one. + +Once an existing module name (like, "ExistingModule") is provided, `dot-maker` will output a success message: + +> Found Module "ExistingModule" + +## Name the Handler + +Once the target module has been identified, you will be prompted to input a name for the Handler: + +> Handler name: + +**The name must contain only letters and numbers.** + +> You don't have to append "Handler" to the name. +> It is automatically appended when necessary. +> See our [Naming Standards](../naming-standards.md) page for more information. + +If you leave the name blank, the process will exit. + +If you input an invalid name (like, "."), an error will be thrown: + +> Invalid Handler name: "." + +If you input the name of an existing Handler (like, "ExistingHandler"), an error will be thrown: + +> Class "ExistingHandler" already exists at /path/to/project/src/ExistingModule/src/Handler/ExistingHandler.php + +Once you input a valid name (like, "Book"), the process will iterate over a list of CRUD operations, asking you to confirm whether the Module needs a request Handler for each operation. + +### API projects + +#### List resources + +The prompt asks you whether you want to list resources: + +> Allow listing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src/ExistingModule/src/Collection/BookCollection.php`: describes a resource-specific collection +- `src/ExistingModule/src/Handler/Book/GetBookCollectionHandler.php`: handles the resource collection representation + +The matching Collection and ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +#### View resources + +The prompt asks you whether you want to view resources: + +> Allow viewing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following file: + +- `src/ExistingModule/src/Handler/Book/GetBookResourceHandler.php`: handles the resource representation + +The matching ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +#### Create resources + +The prompt asks you whether you want to create resources: + +> Allow creating Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src/ExistingModule/src/Handler/Book/PostBookResourceHandler.php`: handles the resource creation +- `src/ExistingModule/src/InputFilter/CreateBookInputFilter.php`: request payload validators + +The matching InputFilter and ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +#### Delete resources + +The prompt asks you whether you want to delete resources: + +> Allow deleting Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src/ExistingModule/src/Handler/Book/DeleteBookResourceHandler.php`: handles the resource deletion + +The matching ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +#### Edit resources + +The prompt asks you whether you want to edit resources: + +> Allow editing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src/ExistingModule/src/Handler/Book/PatchBookResourceHandler.php`: handles the resource update +- `src/ExistingModule/src/InputFilter/EditBookInputFilter.php`: request payload validators + +The matching InputFilter and ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +#### Replace resources + +The prompt asks you whether you want to replace resources: + +> Allow replacing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src/ExistingModule/src/Handler/Book/PutBookResourceHandler.php`: handles the resource replacement +- `src/ExistingModule/src/InputFilter/ReplaceBookInputFilter.php`: request payload validators + +The matching InputFilter and ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +### Non-API projects + +#### List resources + +The prompt asks you whether you want to list resources: + +> Allow listing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src/ExistingModule/src/Handler/Book/GetBookListHandler.php`: renders the resource list page +- `src/ExistingModule/templates/existing-module/book-list.html.twig`: renders the resource list + +The matching ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +#### View resources + +The prompt asks you whether you want to view resources: + +> Allow viewing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src/ExistingModule/src/Handler/Book/GetBookViewHandler.php`: renders the resource page +- `src/ExistingModule/templates/existing-module/book-view.html.twig`: renders the resource + +The matching ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +#### Create resources + +The prompt asks you whether you want to create resources: + +> Allow creating Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src/ExistingModule/src/Handler/Book/GetBookCreateFormHandler.php`: renders the resource creation form page +- `src/ExistingModule/src/Handler/Book/PostBookCreateHandler.php`: handles the resource creation +- `src/ExistingModule/src/Form/CreateBookForm.php`: form fields +- `src/ExistingModule/src/InputFilter/CreateBookInputFilter.php`: form field validators +- `src/ExistingModule/templates/existing-module/book-view.html.twig`: renders the resource creation form + +The matching Form and ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +#### Delete resources + +The prompt asks you whether you want to delete resources: + +> Allow deleting Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src/ExistingModule/src/Handler/Book/GetBookDeleteFormHandler.php`: renders the resource deletion form page +- `src/ExistingModule/src/Handler/Book/PostBookDeleteHandler.php`: handles the resource deletion +- `src/ExistingModule/src/Form/DeleteBookForm.php`: form fields +- `src/ExistingModule/src/InputFilter/DeleteBookInputFilter.php`: form field validators +- `src/ExistingModule/src/InputFilter/Input/ConfirmDeleteBookInput.php`: checkbox input for deletion confirmation +- `src/ExistingModule/templates/existing-module/book-delete-form.html.twig`: renders the resource deletion form + +The matching ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +#### Edit resources + +The prompt asks you whether you want to edit resources: + +> Allow editing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src/ExistingModule/src/Handler/Book/GetBookEditFormHandler.php`: renders the resource edit form page +- `src/ExistingModule/src/Handler/Book/PostBookEditHandler.php`: handles the resource update +- `src/ExistingModule/src/Form/EditBookForm.php`: form fields +- `src/ExistingModule/src/InputFilter/EditBookInputFilter.php`: form field validators +- `src/ExistingModule/templates/existing-module/book-edit-form.html.twig`: renders the resource creation form + +The matching InputFilter and ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +## Create multiple Handlers + +To allow the creation of multiple Handlers, the process will loop until you leave the name blank. +Each iteration creates a new set of Handlers(, Forms), InputFilters and Inputs under the same module. diff --git a/docs/book/v2/component/input-filter.md b/docs/book/v2/component/input-filter.md new file mode 100644 index 0000000..e333f55 --- /dev/null +++ b/docs/book/v2/component/input-filter.md @@ -0,0 +1,113 @@ +# Create InputFilter + +> This page assumes that you have created a Composer "make" script as described on the [Setup page](../setup.md#add-dot-maker-to-composerjson). + +To create an InputFilter, use either of the following commands: + +## Run the command + +```shell +composer make input-filter +``` + +OR + +```shell +./vendor/bin/dot-maker input-filter +``` + +## Identify the target module + +`dot-maker` needs to know in which module you want to create the new InputFilter. +To determine this, it will prompt you to enter the name of an existing module: + +> Existing module name: + +If you input a module name which does not exist (like, "NonExistentModule"), an error will be thrown: + +> Module "NonExistentModule" not found + +and will keep prompting for a valid module name until you provide one. + +Once an existing module name (like, "ExistingModule") is provided, `dot-maker` will output a success message: + +> Found Module "ExistingModule" + +## Name the InputFilter + +Once the target module has been identified, you will be prompted to input a name for the InputFilter: + +> InputFilter name: + +**The name must contain only letters and numbers.** + +> You don't have to append "InputFilter" to the name. +> It is automatically appended when necessary. +> See our [Naming Standards](../naming-standards.md) page for more information. + +If you leave the name blank, the process will exit. + +If you input an invalid name (like, "."), an error will be thrown: + +> Invalid InputFilter name: "." + +If you input the name of an existing InputFilter (like, "ExistingInputFilter"), an error will be thrown: + +> Class "ExistingInputFilter" already exists at /path/to/project/src/ExistingModule/src/InputFilter/ExistingInputFilter.php + +Once you input a valid name (like, "Book"), `dot-maker` will prompt you to confirm which CRUD operations will be performed on the resource: + +### Create resources + +The prompt asks you whether you want to create resources: + +> Allow creating Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following file: + +- `src/ExistingModule/src/InputFilter/CreateBookInputFilter.php` + +Without confirmation, the process will skip to the next component. + +### Delete resources + +The prompt asks you whether you want to delete resources: + +> Allow deleting Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following file: + +- `src/ExistingModule/src/InputFilter/DeleteBookInputFilter.php` + +Without confirmation, the process will skip to the next component. + +### Edit resources + +The prompt asks you whether you want to edit resources: + +> Allow editing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following file: + +- `src/ExistingModule/src/InputFilter/EditBookInputFilter.php` + +Without confirmation, the process will skip to the next component. + +### Replace resources + +> This file is created only when the project type is API. + +The prompt asks you whether you want to replace resources: + +> Allow replacing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following file: + +- `src/ExistingModule/src/InputFilter/ReplaceBookInputFilter.php` + +Without confirmation, the process will skip to the next component. + +## Create multiple InputFilters + +To allow the creation of multiple InputFilters, the process will loop until you leave the name blank. +Each iteration creates a new set of InputFilters under the same module. diff --git a/docs/book/v2/component/input.md b/docs/book/v2/component/input.md new file mode 100644 index 0000000..c55932b --- /dev/null +++ b/docs/book/v2/component/input.md @@ -0,0 +1,65 @@ +# Create Input + +> This page assumes that you have created a Composer "make" script as described on the [Setup page](../setup.md#add-dot-maker-to-composerjson). + +To create an Input, use either of the following commands: + +## Run the command + +```shell +composer make input +``` + +OR + +```shell +./vendor/bin/dot-maker input +``` + +## Identify the target module + +`dot-maker` needs to know in which module you want to create the new Input. +To determine this, it will prompt you to enter the name of an existing module: + +> Existing module name: + +If you input a module name which does not exist (like, "NonExistentModule"), an error will be thrown: + +> Module "NonExistentModule" not found + +and will keep prompting for a valid module name until you provide one. + +Once an existing module name (like, "ExistingModule") is provided, `dot-maker` will output a success message: + +> Found Module "ExistingModule" + +## Name the Input + +Once the target module has been identified, you will be prompted to input a name for the Input: + +> Input name: + +**The name must contain only letters and numbers.** + +> You don't have to append "Input" to the name. +> It is automatically appended when necessary. +> See our [Naming Standards](../naming-standards.md) page for more information. + +If you leave the name blank, the process will exit. + +If you input an invalid name (like, "."), an error will be thrown: + +> Invalid Input name: "." + +If you input the name of an existing Input (like, "ExistingInput"), an error will be thrown: + +> Class "ExistingInput" already exists at /path/to/project/src/ExistingModule/src/InputFilter/Input/ExistingInput.php + +If you input a valid name (like, "NewInput"), `dot-maker` will create the Input and output a success message: + +> Created Input: /path/to/project/src/ExistingModule/src/InputFilter/Input/NewInput.php + +## Create multiple Inputs + +To allow the creation of multiple Inputs, the process will loop until you leave the name blank. +Each iteration creates a new Input under the same module. diff --git a/docs/book/v2/component/middleware.md b/docs/book/v2/component/middleware.md new file mode 100644 index 0000000..72ddb5c --- /dev/null +++ b/docs/book/v2/component/middleware.md @@ -0,0 +1,65 @@ +# Create Middleware + +> This page assumes that you have created a Composer "make" script as described on the [Setup page](../setup.md#add-dot-maker-to-composerjson). + +To create a Middleware, use either of the following commands: + +## Run the command + +```shell +composer make middleware +``` + +OR + +```shell +./vendor/bin/dot-maker middleware +``` + +## Identify the target module + +`dot-maker` needs to know in which module you want to create the new Middleware. +To determine this, it will prompt you to enter the name of an existing module: + +> Existing module name: + +If you input a module name which does not exist (like, "NonExistentModule"), an error will be thrown: + +> Module "NonExistentModule" not found + +and will keep prompting for a valid module name until you provide one. + +Once an existing module name (like, "ExistingModule") is provided, `dot-maker` will output a success message: + +> Found Module "ExistingModule" + +## Name the Middleware + +Once the target module has been identified, you will be prompted to input a name for the Middleware: + +> Middleware name: + +**The name must contain only letters and numbers.** + +> You don't have to append "Middleware" to the name. +> It is automatically appended when necessary. +> See our [Naming Standards](../naming-standards.md) page for more information. + +If you leave the name blank, the process will exit. + +If you input an invalid name (like, "."), an error will be thrown: + +> Invalid Middleware name: "." + +If you input the name of an existing Middleware (like, "ExistingMiddleware"), an error will be thrown: + +> Class "ExistingMiddleware" already exists at /path/to/project/src/ExistingModule/src/Middleware/ExistingMiddleware.php + +If you input a valid name (like, "NewMiddleware"), `dot-maker` will create the Middleware and output a success message: + +> Created Middleware: /path/to/project/src/ExistingModule/src/Middleware/NewMiddleware.php + +## Create multiple Middleware + +To allow the creation of multiple Middleware, the process will loop until you leave the name blank. +Each iteration creates a new Middleware under the same module. diff --git a/docs/book/v2/component/module.md b/docs/book/v2/component/module.md new file mode 100644 index 0000000..af6a387 --- /dev/null +++ b/docs/book/v2/component/module.md @@ -0,0 +1,357 @@ +# Create Module + +> This page assumes that you have created a Composer "make" script as described on the [Setup page](../setup.md#add-dot-maker-to-composerjson). + +To create a Module, use either of the following commands: + +## Run the command + +```shell +composer make module +``` + +OR + +```shell +./vendor/bin/dot-maker module +``` + +## Name the Module + +You will be prompted to input a name for the Module: + +> New module name: + +**The name must contain only letters and numbers.** + +If you leave the name blank, the process will exit. + +If you input an invalid name (like, "."), an error will be thrown: + +> Invalid Module name: "." + +If you input the name of an existing Module (like, "ExistingModule"), an error will be thrown: + +> Module "ExistingModule" already exists at /path/to/project/src/ExistingModule + +If you input a valid name (like, "NewModule"), `dot-maker` will create the Command and output a success message: + +> Created directory: /path/to/project/src/NewModule + +## Create Module files + +From here, the process will run through a predefined set of files to create the Module components. + +> The process will not ask for file names because they will be generated from the Module name. + +### Entity and Repository + +Next, you will be asked to confirm whether the Module needs an Entity (and its matching Repository): + +> Create entity and repository? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- for projects compatible with the Core architecture: + - `src/Core/src//src/Entity/.php` + - `src/Core/src//src/Repository/Repository.php` +- for projects which are NOT compatible with the Core architecture: + - `src//src/Entity/.php` + - `src//src/Repository/Repository.php` + +Without confirmation, the process will skip to the next component. + +### Service and ServiceInterface + +Next, you will be asked to confirm whether the Module needs a Service (and its matching ServiceInterface): + +> Create service and service interface? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src//src/Service/Service.php` +- `src//src/Service/ServiceInterface.php` + +If, at the previous step, you chose to create a Repository, it will be automatically injected into the Service. +Also, some common methods will be added to the Service and the ServiceInterface. + +Without confirmation, the process will skip to the next component. + +### Command + +Next, you will be asked to confirm whether the Module needs a Command: + +> Create command? [Y(es)/n(o)]: + +On confirmation, the process will create the following file: + +- `src//src/Command/Command.php` + +Without confirmation, the process will skip to the next component. + +### Middleware + +Next, you will be asked to confirm whether the Module needs a Middleware: + +> Create middleware? [Y(es)/n(o)]: + +On confirmation, the process will create the following file: + +- `src//src/Middleware/Middleware.php` + +If, previously you chose to create a ServiceInterface, it will be automatically injected into the Middleware. + +Without confirmation, the process will skip to the next component. + +### Handler + +Next, you will be asked to confirm whether the Module needs request Handlers: + +> Create handler? [Y(es)/n(o)]: + +On confirmation, depending on the project type, the process will iterate over a list of CRUD operations, asking you to confirm whether the Module needs a request Handler for each operation. + +#### API projects + +##### List resources + +The prompt asks you whether you want to list resources: + +> Allow listing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src//src/Collection/Collection.php`: describes a resource-specific collection +- `src//src/Handler//GetCollectionHandler.php`: handles the resource collection representation + +The matching Collection and ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +##### View resources + +The prompt asks you whether you want to view resources: + +> Allow viewing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following file: + +- `src//src/Handler//GetResourceHandler.php`: handles the resource representation + +The matching ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +##### Create resources + +The prompt asks you whether you want to create resources: + +> Allow creating Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src//src/Handler//PostResourceHandler.php`: handles the resource creation +- `src//src/InputFilter/CreateInputFilter.php`: request payload validators + +The matching InputFilter and ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +##### Delete resources + +The prompt asks you whether you want to delete resources: + +> Allow deleting Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src//src/Handler//DeleteResourceHandler.php`: handles the resource deletion + +The matching ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +##### Edit resources + +The prompt asks you whether you want to edit resources: + +> Allow editing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src//src/Handler//PatchResourceHandler.php`: handles the resource update +- `src//src/InputFilter/EditInputFilter.php`: request payload validators + +The matching InputFilter and ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +##### Replace resources + +The prompt asks you whether you want to replace resources: + +> Allow replacing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src//src/Handler//PutResourceHandler.php`: handles the resource replacement +- `src//src/InputFilter/ReplaceInputFilter.php`: request payload validators + +The matching InputFilter and ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +#### Non-API projects + +##### List resources + +The prompt asks you whether you want to list resources: + +> Allow listing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src//src/Handler//GetListHandler.php`: renders the resource list page +- `src//templates//-list.html.twig`: renders the resource list + +The matching ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +##### View resources + +The prompt asks you whether you want to view resources: + +> Allow viewing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src//src/Handler//GetViewHandler.php`: renders the resource page +- `src//templates//-view.html.twig`: renders the resource + +The matching ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +##### Create resources + +The prompt asks you whether you want to create resources: + +> Allow creating Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src//src/Handler//GetCreateFormHandler.php`: renders the resource creation form page +- `src//src/Handler//PostCreateHandler.php`: handles the resource creation +- `src//src/Form/CreateForm.php`: form fields +- `src//src/InputFilter/CreateInputFilter.php`: form field validators +- `src//templates//-view.html.twig`: renders the resource creation form + +The matching Form and ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +##### Delete resources + +The prompt asks you whether you want to delete resources: + +> Allow deleting Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src//src/Handler//GetDeleteFormHandler.php`: renders the resource deletion form page +- `src//src/Handler//PostDeleteHandler.php`: handles the resource deletion +- `src//src/Form/DeleteForm.php`: form fields +- `src//src/InputFilter/DeleteInputFilter.php`: form field validators +- `src//src/InputFilter/Input/ConfirmDeleteInput.php`: checkbox input for deletion confirmation +- `src//templates//-delete-form.html.twig`: renders the resource deletion form + +The matching ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +##### Edit resources + +The prompt asks you whether you want to edit resources: + +> Allow editing Resources? [Y(es)/n(o)]: + +On confirmation, the process will create the following files: + +- `src//src/Handler//GetEditFormHandler.php`: renders the resource edit form page +- `src//src/Handler//PostEditHandler.php`: handles the resource update +- `src//src/Form/EditForm.php`: form fields +- `src//src/InputFilter/EditInputFilter.php`: form field validators +- `src//templates//-edit-form.html.twig`: renders the resource creation form + +The matching InputFilter and ServiceInterface will be automatically injected into the Handler. + +Without confirmation, the process will skip to the next component. + +### RoutesDelegator + +This component is created only if you chose to create request Handlers. +It is generated automatically and contains the basic CRUD operations based on the selected actions during Handler creation. + +Location: `src//src/RoutesDelegator.php` + +### OpenAPI + +> This component is created only when a project type is API. + +It is generated automatically and contains the basic description of the API endpoints found in this Module. +Once you extend the entities with additional fields, you will need to update the OpenAPI file manually. + +Location: `src//src/RoutesDelegator.php` + +### ConfigProvider + +It is generated automatically and contains all the delegators, factories and service aliases for this Module. +Once you add new files that need dependency injection, you will need to update the ConfigProvider manually. + +Location: `src//src/ConfigProvider.php` + +### Core ConfigProvider + +> This component is created only when a project uses the Core architecture. + +It is generated automatically and contains all the delegators, factories and service aliases for this Module. +Once you add new files that need dependency injection, you will need to update the ConfigProvider manually. + +Location: `src/Core/src//src/ConfigProvider.php` + +### Templates + +> This file structure is NOT created when the project type is API. + +Location: `src//templates//*.html.twig` + +## Next steps + +Once the Module is created, `dot-maker` will display a list of tasks that need to be performed manually: + +```text +Next steps: +=========== +- add to config/config.php: + \\ConfigProvider::class, +- add to config/config.php: + Core\\ConfigProvider::class, +- add to config/autoload/cli.global.php under dot_cli.commands: + \\Command\Command::getDefaultName() => \\Command\Command::class, +- add to config/pipeline.php: + $app->pipe(\\Middleware\Middleware::class); +- add to config/autoload/authorization.global.php (OR config/autoload/authorization-guards.global.php) + the routes registered in /path/to/project/src//src/RoutesDelegator.php +- add to composer.json under autoload.psr-4: + "\\\\": "src//src/" +- add to composer.json under autoload.psr-4: + "Core\\\\": "src/Core/src//src/" +- dump Composer autoloader by executing this command: + composer dump +- generate Doctrine migration: + php ./vendor/bin/doctrine-migrations diff +- Review each new file, verify their contents and start adding logic to them. +``` + +where `` is the project type (**API**, **Admin**, **Frontend**, **Light** or **Queue**) and `` is the name of the Module. diff --git a/docs/book/v2/component/repository.md b/docs/book/v2/component/repository.md new file mode 100644 index 0000000..6305986 --- /dev/null +++ b/docs/book/v2/component/repository.md @@ -0,0 +1,79 @@ +# Create Repository + +> This page assumes that you have created a Composer "make" script as described on the [Setup page](../setup.md#add-dot-maker-to-composerjson). + +## Run the command + +To create a Repository, use either of the following commands: + +```shell +composer make repository +``` + +OR + +```shell +./vendor/bin/dot-maker repository +``` + +## Identify the target module + +`dot-maker` needs to know in which module you want to create the new Repository. +To determine this, it will prompt you to enter the name of an existing module: + +> Existing module name: + +If you input a module name which does not exist (like, "NonExistentModule"), an error will be thrown: + +> Module "NonExistentModule" not found + +and will keep prompting for a valid module name until you provide one. + +Once an existing module name (like, "ExistingModule") is provided, `dot-maker` will output a success message: + +> Found Module "ExistingModule" + +## Name the Repository + +Once the target module has been identified, you will be prompted to input a name for the Repository: + +> Repository name: + +**The name must contain only letters and numbers.** + +> You don't have to append "Repository" to the name. +> It is automatically appended when necessary. +> See our [Naming Standards](../naming-standards.md) page for more information. + +If you leave the name blank, the process will exit. + +If you input an invalid name (like, "."), an error will be thrown: + +> Invalid Repository name: "." + +If you input the name of an existing Repository (like, "ExistingRepository"), an error will be thrown: + +- for projects compatible with the Core architecture: + +> Class "ExistingRepository" already exists at /path/to/project/src/Core/src/ExistingModule/src/Repository/ExistingRepository.php + +- for projects which are NOT compatible with the Core architecture: + +> Class "ExistingRepository" already exists at /path/to/project/src/ExistingModule/src/Repository/ExistingRepository.php + +If you input a valid name (like, "NewRepository"), `dot-maker` will create the Repository and output a success message: + +- for projects compatible with the Core architecture: + +> Created Entity: /path/to/project/src/Core/src/ExistingModule/src/Entity/NewEntity.php +> Created Repository: /path/to/project/src/Core/src/ExistingModule/src/Repository/NewEntityRepository.php + +- for projects which are NOT compatible with the Core architecture: + +> Created Entity: /path/to/project/src/ExistingModule/src/Entity/NewEntity.php +> Created Repository: /path/to/project/src/ExistingModule/src/Repository/NewEntityRepository.php + +## Create multiple Repositories + +To allow the creation of multiple Repositories, the process will loop until you leave the name blank. +Each iteration creates a new pair of Entity and Repository under the same module. diff --git a/docs/book/v2/component/service-interface.md b/docs/book/v2/component/service-interface.md new file mode 100644 index 0000000..4253c01 --- /dev/null +++ b/docs/book/v2/component/service-interface.md @@ -0,0 +1,70 @@ +# Create ServiceInterface + +> This page assumes that you have created a Composer "make" script as described on the [Setup page](../setup.md#add-dot-maker-to-composerjson). + +> `dot-maker` will look for a matching Repository in the module (e.g.: BookRepository — BookService). +> If it finds one, it will automatically inject it into the Service. +> Also, some common methods will be added to the Service and the ServiceInterface. + +## Run the command + +To create a ServiceInterface, use either of the following commands: + +```shell +composer make service-interface +``` + +OR + +```shell +./vendor/bin/dot-maker service-interface +``` + +## Identify the target module + +`dot-maker` needs to know in which module you want to create the new ServiceInterface. +To determine this, it will prompt you to enter the name of an existing module: + +> Existing module name: + +If you input a module name which does not exist (like, "NonExistentModule"), an error will be thrown: + +> Module "NonExistentModule" not found + +and will keep prompting for a valid module name until you provide one. + +Once an existing module name (like, "ExistingModule") is provided, `dot-maker` will output a success message: + +> Found Module "ExistingModule" + +## Name the ServiceInterface + +Once the target module has been identified, you will be prompted to input a name for the ServiceInterface: + +> ServiceInterface name: + +**The name must contain only letters and numbers.** + +> You don't have to append "ServiceInterface" to the name. +> It is automatically appended when necessary. +> See our [Naming Standards](../naming-standards.md) page for more information. + +If you leave the name blank, the process will exit. + +If you input an invalid name (like, "."), an error will be thrown: + +> Invalid ServiceInterface name: "." + +If you input the name of an existing ServiceInterface (like, "ExistingServiceInterface"), an error will be thrown: + +> Class "ExistingServiceInterface" already exists at /path/to/project/src/ExistingModule/src/Service/ExistingServiceInterface.php + +If you input a valid name (like, "NewServiceInterface"), `dot-maker` will create the ServiceInterface and output a success message: + +> Created ServiceInterface: /path/to/project/src/ExistingModule/src/Service/NewServiceInterface.php +> Created Service: /path/to/project/src/ExistingModule/src/Service/NewService.php + +## Create multiple ServiceInterfaces + +To allow the creation of multiple ServiceInterfaces, the process will loop until you leave the name blank. +Each iteration creates a new pair of Service and ServiceInterface under the same module. diff --git a/docs/book/v2/component/service.md b/docs/book/v2/component/service.md new file mode 100644 index 0000000..512c9f6 --- /dev/null +++ b/docs/book/v2/component/service.md @@ -0,0 +1,70 @@ +# Create Service + +> This page assumes that you have created a Composer "make" script as described on the [Setup page](../setup.md#add-dot-maker-to-composerjson). + +> `dot-maker` will look for a matching Repository in the module (e.g.: BookRepository — BookService). +> If it finds one, it will automatically inject it into the Service. +> Also, some common methods will be added to the Service and the ServiceInterface. + +## Run the command + +To create a Service, use either of the following commands: + +```shell +composer make service +``` + +OR + +```shell +./vendor/bin/dot-maker service +``` + +## Identify the target module + +`dot-maker` needs to know in which module you want to create the new Service. +To determine this, it will prompt you to enter the name of an existing module: + +> Existing module name: + +If you input a module name which does not exist (like, "NonExistentModule"), an error will be thrown: + +> Module "NonExistentModule" not found + +and will keep prompting for a valid module name until you provide one. + +Once an existing module name (like, "ExistingModule") is provided, `dot-maker` will output a success message: + +> Found Module "ExistingModule" + +## Name the Service + +Once the target module has been identified, you will be prompted to input a name for the Service: + +> Service name: + +**The name must contain only letters and numbers.** + +> You don't have to append "Service" to the name. +> It is automatically appended when necessary. +> See our [Naming Standards](../naming-standards.md) page for more information. + +If you leave the name blank, the process will exit. + +If you input an invalid name (like, "."), an error will be thrown: + +> Invalid Service name: "." + +If you input the name of an existing Service (like, "ExistingService"), an error will be thrown: + +> Class "ExistingService" already exists at /path/to/project/src/ExistingModule/src/Service/ExistingService.php + +If you input a valid name (like, "NewService"), `dot-maker` will create the Service and output a success message: + +> Created Service: /path/to/project/src/ExistingModule/src/Service/NewService.php +> Created ServiceInterface: /path/to/project/src/ExistingModule/src/Service/NewServiceInterface.php + +## Create multiple Services + +To allow the creation of multiple Services, the process will loop until you leave the name blank. +Each iteration creates a new pair of Service and ServiceInterface under the same module. diff --git a/docs/book/v2/installation.md b/docs/book/v2/installation.md new file mode 100644 index 0000000..22a7b04 --- /dev/null +++ b/docs/book/v2/installation.md @@ -0,0 +1,7 @@ +# Installation + +Install `dotkernel/dot-maker` by executing the following Composer command: + +```shell +composer require --dev dotkernel/dot-maker +``` diff --git a/docs/book/v2/io/input.md b/docs/book/v2/io/input.md new file mode 100644 index 0000000..9b01059 --- /dev/null +++ b/docs/book/v2/io/input.md @@ -0,0 +1,38 @@ +# Input data + +`dot-maker` uses two types of input: + +- prompt: where the user has to enter some data and press **Enter** +- confirm: where the user has to enter one of the predefined options and/or press **Enter** + +## Prompt + +The user is presented with a message, for example: + +> Input file name: + +and they must enter some data and press **Enter**. + +Whether the command accepts blank input depends on the command. +Some will accept blank input and exit, others will keep prompting until the user enters some valid data. + +## Confirm + +> Confirmations are case-insensitive, so the user may enter lowercase, uppercase or mixed-case letters. + +The user is presented with a message, for example: + +> Perform an action? [Y(es)/n(o)]: + +OR + +> Perform an action? [y(es)/N(o)]: + +and they must enter one of the predefined options and/or press **Enter**. + +The valid options are: `y`, `yes`, `n`, `no` and blank. +If the user enters an invalid value, the command will loop until it receives a valid input. + +If the user submits a blank input, the default value is used. +The default value can be identified by finding the branch starting with a capital letter. +So, for `[Y(es)/n(o)]` the default value is `yes` and for `[y(es)/N(o)]` the default value is `no`. diff --git a/docs/book/v2/io/output.md b/docs/book/v2/io/output.md new file mode 100644 index 0000000..25261a7 --- /dev/null +++ b/docs/book/v2/io/output.md @@ -0,0 +1,21 @@ +# Output data + +`dot-maker` aims to deliver a simple and intuitive user experience. +Each user input can result in one of the following outputs: + +- on success: the message is green and shows what file was created +- on failure: the message is red and shows what went wrong + +## Debug info + +On executing a create component command, the first output you will see is related to project detection. + +The first line is an info message confirming the project type, for example: + +> Detected project type: Api + +The second line is an info message confirming whether the project uses Core architecture, for example: + +> Core architecture: Yes + +These are detected by looking at the modules registered in `composer.json` under **autoload**.**psr-4**. diff --git a/docs/book/v2/naming-standards.md b/docs/book/v2/naming-standards.md new file mode 100644 index 0000000..3fc69aa --- /dev/null +++ b/docs/book/v2/naming-standards.md @@ -0,0 +1,221 @@ +# Naming Standards + +Inside the Dotkernel ecosystem, we strictly follow the following standards to make sure that we provide clean and easy-to-follow code: + +- [PSR-4](https://www.php-fig.org/psr/psr-4/): the autoloading standard—for easy autoloading of classes. +- [PSR-12](https://www.php-fig.org/psr/psr-12/): the extended coding style—for suggestive file names. + +## Collections + +> Can only be created in API projects. + +These are files that are used to contain a set of resources of the same type. + +- location: `src//src/Collection` +- pattern: _Name_ + `Collection` +- example: `src//src/Collection/ExampleCollection.php` + +## Commands + +These are CLI scripts that are used to perform a specific task. + +- location: `src//src/Command` +- pattern: _Name_ + `Command` +- example: `src//src/Command/ExampleCommand.php` + +Core-compatible projects MAY also have Core Commands: + +- location: `src/Core/src//src/Command` +- pattern: _Name_ + `Command` +- example: `src/Core/src//src/Command/ExampleCommand.php` + +## ConfigProvider + +These are configuration classes where aliases, delegators, factories and template paths are registered. + +- location: `src//src` +- pattern: `ConfigProvider` +- example: `src//src/ConfigProvider.php` + +Core-compatible projects also have a Core ConfigProvider: + +- location: `src/Core/src//src` +- pattern: `ConfigProvider` +- example: `src/Core/src//src/ConfigProvider.php` + +When this file exists, Doctrine entity configurations are stored here. +Else, those too are stored in `src//src/ConfigProvider.php`. + +## Entities + +These are classes that represent a single resource and are used to store and retrieve data in a database. + +- location: `src/Core/src/ModuleName/Entity` +- pattern: _Name_ +- example: `src/Core/src/ModuleName/Entity/Example.php` + +### Legacy projects + +- location: `src//src/Entity` +- pattern: _Name_ +- example: `src//src/Entity/Example.php` + +## Factories + +These are classes responsible for instantiating specific objects. + +- location: `src//src/Factory` +- pattern: _Name_ + `Factory` +- example: `src//src/Factory/ExampleFactory.php` + +Core-compatible projects MAY also have Core Factories: + +- location: `src/Core/src//src/Factory` +- pattern: _Name_ + `Factory` +- example: `src/Core/src//src/Factory/ExampleFactory.php` + +## Fixtures + +These are classes used to populate the database with test data. + +- location: `src/Core/src/App/src/Fixture` +- pattern: _Name_ + `Loader` +- example: `src/Core/src/App/src/Fixture/ExampleLoader.php` + +## Forms + +> Cannot be created in API projects. + +These are objectual representations of an HTML form. + +- location: `src//src/Form` +- pattern: _Name_ + `Form` +- example: `src//src/Form/ExampleForm.php` + +## Handlers + +These are classes responsible for handling a specific HTTP request. + +- location: `src//src/Handler/` +- pattern: _Verb_ + _Name_ + _Action_ + `Handler` +- example: `src//src/Handler/PostCreateHandler.php` + +### API projects + +- location: `src//src/Handler/` +- pattern: + - resources: _Verb_ + _Name_ + `ResourceHandler` + - collections: _Verb_ + _Name_ + `CollectionHandler` +- example: + - resources: `src//src/Handler//CreateResourceHandler.php` + - collections: `src//src/Handler//GetCollectionHandler.php` + +## Inputs + +These are objectual representations of an HTML form input. + +- location: `src//src/InputFilter/Input` +- pattern: _Name_ + `Input` +- example: `src//src/InputFilter/Input/ExampleInput.php` + +Core-compatible projects MAY also have Core Inputs: + +- location: `src/Core/src//src/InputFilter/Input` +- pattern: _Name_ + `Input` +- example: `src/Core/src//src/InputFilter/Input/ExampleInput.php` + +## InputFilters + +These are classes that are used to validate and filter form data. + +- location: `src//src/InputFilter` +- pattern: _Name_ + `InputFilter` +- example: `src//src/InputFilter/ExampleInputFilter.php` + +Core-compatible projects MAY also have Core InputFilters: + +- location: `src/Core/src//src/InputFilter` +- pattern: _Name_ + `InputFilter` +- example: `src/Core/src//src/InputFilter/ExampleInputFilter.php` + +## Middleware + +These are classes that can be used to perform actions before and after a request has been handled. + +- location: `src//src/Middleware` +- pattern: _Name_ + `Middleware` +- example: `src//src/Middleware/ExampleMiddleware.php` + +Core-compatible projects MAY also have Core Middleware: + +- location: `src/Core/src//src/Middleware` +- pattern: _Name_ + `Middleware` +- example: `src/Core/src//src/Middleware/ExampleMiddleware.php` + +## Migrations + +These are classes used to modify the database schema. + +- location: `src/Core/src/App/src/Migration` +- pattern: `Version` + _Timestamp_ +- example: `src/Core/src/App/src/Migration/Version20250407142911.php` + +## OpenApi + +> Can only be created in API projects. + +These are configuration classes where OpenApi specifications are registered. + +- location: `src//src` +- pattern: `OpenApi` +- example: `src//src/OpenApi.php` + +## Repositories + +These are classes that represent an intermediary layer between the database and the entities. + +- location: `src/Core/src/ModuleName/Repository` +- pattern: _Name_ + `Repository` +- example: `src/Core/src/ModuleName/Repository/ExampleRepository.php` + +### Legacy projects + +- location: `src//src/Repository` +- pattern: _Name_ + `Repository` +- example: `src//src/Repository/ExampleRepository.php` + +## RoutesDelegator + +These are configuration classes where HTTP request routes are registered. + +- location: `src//src` +- pattern: `RoutesDelegator` +- example: `src//src/RoutesDelegator.php` + +## Services + +These are classes that contain task-specific methods to be reused across the application. + +- location: `src//src/Service` +- pattern: _Name_ + `Service` +- example: `src//src/Service/ExampleService.php` + +Core-compatible projects MAY also have Core Services: + +- location: `src/Core/src//src/Service` +- pattern: _Name_ + `Service` +- example: `src/Core/src//src/Service/ExampleService.php` + +## ServiceInterfaces + +These are interfaces that serve as contracts for services. + +- location: `src//src/ServiceInterface` +- pattern: _Name_ + `ServiceInterface` +- example: `src//src/Service/ExampleServiceInterface.php` + +Core-compatible projects MAY also have Core ServiceInterfaces: + +- location: `src/Core/src//src/Service` +- pattern: _Name_ + `Service` +- example: `src/Core/src//src/Service/ExampleServiceInterface.php` diff --git a/docs/book/v2/overview.md b/docs/book/v2/overview.md new file mode 100644 index 0000000..539029d --- /dev/null +++ b/docs/book/v2/overview.md @@ -0,0 +1,23 @@ +# Overview + +Programmatically generate Dotkernel project files and directories. + +## Badges + +![OSS Lifecycle](https://img.shields.io/osslifecycle/dotkernel/dot-maker) +![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-maker/2.0.0) + +[![GitHub issues](https://img.shields.io/github/issues/dotkernel/dot-maker)](https://github.com/dotkernel/dot-maker/issues) +[![GitHub forks](https://img.shields.io/github/forks/dotkernel/dot-maker)](https://github.com/dotkernel/dot-maker/network) +[![GitHub stars](https://img.shields.io/github/stars/dotkernel/dot-maker)](https://github.com/dotkernel/dot-maker/stargazers) +[![GitHub license](https://img.shields.io/github/license/dotkernel/dot-maker)](https://github.com/dotkernel/dot-maker/blob/2.0/LICENSE) + +[![Build Static](https://github.com/dotkernel/dot-maker/actions/workflows/continuous-integration.yml/badge.svg?branch=2.0)](https://github.com/dotkernel/dot-maker/actions/workflows/continuous-integration.yml) +[![codecov](https://codecov.io/gh/dotkernel/dot-maker/graph/badge.svg?token=KT9UA402B4)](https://codecov.io/gh/dotkernel/dot-maker) +[![PHPStan](https://github.com/dotkernel/dot-maker/actions/workflows/static-analysis.yml/badge.svg?branch=2.0)](https://github.com/dotkernel/dot-maker/actions/workflows/static-analysis.yml) + +Make sure you run through our guides on using the [inputs](./io/input.md) and the [outputs](./io/output.md). + + + + diff --git a/docs/book/v2/setup.md b/docs/book/v2/setup.md new file mode 100644 index 0000000..33fcdf1 --- /dev/null +++ b/docs/book/v2/setup.md @@ -0,0 +1,20 @@ +# Setup + +Once installed, `dot-maker` is ready for usage, no need for extra configurations. + +## Add dot-maker to composer.json + +> This step is optional but recommended. + +Open your project's `composer.json` and locate the `scripts` section. +If it does not exist, create it at the document's root level. + +Register a new script by appending `"alias": "dot-maker"` to the `scripts` section, where **alias** can be any string you want; like, for example, **make**. + +```json +{ + "scripts": { + "make": "dot-maker" + } +} +``` diff --git a/docs/book/v2/usage.md b/docs/book/v2/usage.md new file mode 100644 index 0000000..cef812c --- /dev/null +++ b/docs/book/v2/usage.md @@ -0,0 +1,8 @@ +# Usage + +Invoke `dot-maker` by executing: + +- the bin file in your vendor directory `./vendor/bin/dot-maker` +- the (optional) Composer script created during [Setup](setup.md#add-dot-maker-to-composerjson): `composer make` + +Follow the instructions on the screen to create a specific Dotkernel project component. diff --git a/mkdocs.yml b/mkdocs.yml index d192d0e..7f24577 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -2,13 +2,36 @@ docs_dir: docs/book site_dir: docs/html extra: project: Packages - current_version: v1 + current_version: v2 versions: + - v2 - v1 installation: composer_dev: true nav: - Home: index.md + - v2: + - Overview: v2/overview.md + - Installation: v2/installation.md + - Setup: v2/setup.md + - Usage: v2/usage.md + - Component: + - Create Collection: v2/component/collection.md + - Create Command: v2/component/command.md + - Create Entity: v2/component/entity.md + - Create Form: v2/component/form.md + - Create Handler: v2/component/handler.md + - Create Input: v2/component/input.md + - Create InputFilter: v2/component/input-filter.md + - Create Middleware: v2/component/middleware.md + - Create Module: v2/component/module.md + - Create Repository: v2/component/repository.md + - Create Service: v2/component/service.md + - Create ServiceInterface: v2/component/service-interface.md + - IO: + - Input: v2/io/input.md + - Output: v2/io/output.md + - Naming Standards: v2/naming-standards.md - v1: - Overview: v1/overview.md - Installation: v1/installation.md diff --git a/src/Component/Import.php b/src/Component/Import.php index ad31610..0ebec66 100644 --- a/src/Component/Import.php +++ b/src/Component/Import.php @@ -53,7 +53,9 @@ class Import public const ROOT_APP_COLLECTION_RESOURCECOLLECTION = '%s\\App\\Collection\\ResourceCollection'; public const ROOT_APP_CONFIGPROVIDER = '%s\\App\\ConfigProvider'; public const ROOT_APP_ENTITY_ABSTRACTENTITY = '%s\\App\\Entity\\AbstractEntity'; + public const ROOT_APP_ENTITY_NUMERICIDENTIFIERTRAIT = '%s\\App\\Entity\\NumericIdentifierTrait'; public const ROOT_APP_ENTITY_TIMESTAMPSTRAIT = '%s\\App\\Entity\\TimestampsTrait'; + public const ROOT_APP_ENTITY_UUIDIDENTIFIERTRAIT = '%s\\App\\Entity\\UuidIdentifierTrait'; public const ROOT_APP_EXCEPTION_BADREQUESTEXCEPTION = '%s\\App\\Exception\\BadRequestException'; public const ROOT_APP_EXCEPTION_CONFLICTEXCEPTION = '%s\\App\\Exception\\ConflictException'; public const ROOT_APP_EXCEPTION_NOTFOUNDEXCEPTION = '%s\\App\\Exception\\NotFoundException'; diff --git a/src/Type/Entity.php b/src/Type/Entity.php index dc85ddb..6966571 100644 --- a/src/Type/Entity.php +++ b/src/Type/Entity.php @@ -9,7 +9,6 @@ use Dot\Maker\Component\Import; use Dot\Maker\Component\Inject; use Dot\Maker\Component\Method; -use Dot\Maker\Component\Method\Constructor; use Dot\Maker\Context; use Dot\Maker\Exception\BadRequestException; use Dot\Maker\Exception\DuplicateFileException; @@ -78,6 +77,7 @@ public function render(Component $entity, Component $repository): string ->setExtends('AbstractEntity') ->useClass($this->getAbstractEntityFqcn()) ->useClass($this->getTimestampsTraitFqcn()) + ->useClass($this->getUuidIdentifierTraitFqcn()) ->useClass($repository->getFqcn()) ->useClass(Import::DATETIMEIMMUTABLE) ->useClass(Import::DOCTRINE_ORM_MAPPING, 'ORM') @@ -91,29 +91,23 @@ public function render(Component $entity, Component $repository): string ->addInject( new Inject('ORM\HasLifecycleCallbacks') ) - ->addTrait('TimestampsTrait'); - - $constructor = (new Constructor())->setBody(<<created(); -BODY); - $class->addMethod($constructor); + ->addTrait('TimestampsTrait') + ->addTrait('UuidIdentifierTrait'); $getArrayCopy = (new Method('getArrayCopy')) ->setReturnType('array') ->setComment(<<setBody(<< \$this->uuid->toString(), + 'id' => \$this->id->toString(), 'created' => \$this->created, 'updated' => \$this->updated, ]; @@ -134,6 +128,17 @@ public function getAbstractEntityFqcn(): string return sprintf($format, $this->context->getRootNamespace()); } + public function getNumericIdentifierTraitFqcn(): string + { + $format = Import::ROOT_APP_ENTITY_NUMERICIDENTIFIERTRAIT; + + if ($this->context->hasCore()) { + return sprintf($format, Context::NAMESPACE_CORE); + } + + return sprintf($format, $this->context->getRootNamespace()); + } + public function getTimestampsTraitFqcn(): string { $format = Import::ROOT_APP_ENTITY_TIMESTAMPSTRAIT; @@ -144,4 +149,15 @@ public function getTimestampsTraitFqcn(): string return sprintf($format, $this->context->getRootNamespace()); } + + public function getUuidIdentifierTraitFqcn(): string + { + $format = Import::ROOT_APP_ENTITY_UUIDIDENTIFIERTRAIT; + + if ($this->context->hasCore()) { + return sprintf($format, Context::NAMESPACE_CORE); + } + + return sprintf($format, $this->context->getRootNamespace()); + } } diff --git a/src/Type/Handler/GetDeleteResourceHandler.php b/src/Type/Handler/GetDeleteResourceHandler.php index e94776d..01c4fe9 100644 --- a/src/Type/Handler/GetDeleteResourceHandler.php +++ b/src/Type/Handler/GetDeleteResourceHandler.php @@ -103,7 +103,7 @@ public function render(Component $handler, Component $entity, Component $form, C ) ->setBody(<<getVariable()} = \$this->{$serviceInterface->toCamelCase(true)}->{$entity->getFindMethodName()}(\$request->getAttribute('uuid')); + {$entity->getVariable()} = \$this->{$serviceInterface->toCamelCase(true)}->{$entity->getFindMethodName()}(\$request->getAttribute('id')); } catch (NotFoundException \$exception) { \$this->messenger->addError(\$exception->getMessage()); @@ -112,7 +112,7 @@ public function render(Component $handler, Component $entity, Component $form, C \$this->{$form->toCamelCase()}->setAttribute( 'action', - \$this->router->generateUri('{$entity->toKebabCase()}::delete-{$entity->toKebabCase()}', ['uuid' => {$entity->getVariable()}->getUuid()->toString()]) + \$this->router->generateUri('{$entity->toKebabCase()}::delete-{$entity->toKebabCase()}', ['id' => {$entity->getVariable()}->getId()->toString()]) ); return new HtmlResponse( diff --git a/src/Type/Handler/GetEditResourceHandler.php b/src/Type/Handler/GetEditResourceHandler.php index ced86ee..a4de240 100644 --- a/src/Type/Handler/GetEditResourceHandler.php +++ b/src/Type/Handler/GetEditResourceHandler.php @@ -103,7 +103,7 @@ public function render(Component $handler, Component $entity, Component $form, C ) ->setBody(<<getVariable()} = \$this->{$serviceInterface->toCamelCase(true)}->{$entity->getFindMethodName()}(\$request->getAttribute('uuid')); + {$entity->getVariable()} = \$this->{$serviceInterface->toCamelCase(true)}->{$entity->getFindMethodName()}(\$request->getAttribute('id')); } catch (NotFoundException \$exception) { \$this->messenger->addError(\$exception->getMessage()); @@ -113,7 +113,7 @@ public function render(Component $handler, Component $entity, Component $form, C \$this->{$form->toCamelCase()} ->setAttribute( 'action', - \$this->router->generateUri('{$entity->toKebabCase()}::edit-{$entity->toKebabCase()}', ['uuid' => {$entity->getVariable()}->getUuid()->toString()]) + \$this->router->generateUri('{$entity->toKebabCase()}::edit-{$entity->toKebabCase()}', ['id' => {$entity->getVariable()}->getId()->toString()]) ) ->bind({$entity->getVariable()}); diff --git a/src/Type/Handler/GetViewResourceHandler.php b/src/Type/Handler/GetViewResourceHandler.php index f9f9259..e8abe51 100644 --- a/src/Type/Handler/GetViewResourceHandler.php +++ b/src/Type/Handler/GetViewResourceHandler.php @@ -94,7 +94,7 @@ public function render(Component $handler, Component $entity, Component $service ) ->setBody(<<getVariable()} = \$this->{$serviceInterface->toCamelCase(true)}->{$entity->getFindMethodName()}(\$request->getAttribute('uuid')); + {$entity->getVariable()} = \$this->{$serviceInterface->toCamelCase(true)}->{$entity->getFindMethodName()}(\$request->getAttribute('id')); } catch (NotFoundException \$exception) { \$this->messenger->addError(\$exception->getMessage()); diff --git a/src/Type/Handler/PostDeleteResourceHandler.php b/src/Type/Handler/PostDeleteResourceHandler.php index e7f1336..baddebb 100644 --- a/src/Type/Handler/PostDeleteResourceHandler.php +++ b/src/Type/Handler/PostDeleteResourceHandler.php @@ -110,7 +110,7 @@ public function render(Component $handler, Component $entity, Component $form, C ) ->setBody(<<getVariable()} = \$this->{$serviceInterface->toCamelCase(true)}->{$entity->getFindMethodName()}(\$request->getAttribute('uuid')); + {$entity->getVariable()} = \$this->{$serviceInterface->toCamelCase(true)}->{$entity->getFindMethodName()}(\$request->getAttribute('id')); } catch (NotFoundException \$exception) { \$this->messenger->addError(\$exception->getMessage()); @@ -119,7 +119,7 @@ public function render(Component $handler, Component $entity, Component $form, C \$this->{$form->toCamelCase()}->setAttribute( 'action', - \$this->router->generateUri('{$entity->toKebabCase()}::delete-{$entity->toKebabCase()}', ['uuid' => {$entity->getVariable()}->getUuid()->toString()]) + \$this->router->generateUri('{$entity->toKebabCase()}::delete-{$entity->toKebabCase()}', ['id' => {$entity->getVariable()}->getId()->toString()]) ); try { diff --git a/src/Type/Handler/PostEditResourceHandler.php b/src/Type/Handler/PostEditResourceHandler.php index 4145f17..333522e 100644 --- a/src/Type/Handler/PostEditResourceHandler.php +++ b/src/Type/Handler/PostEditResourceHandler.php @@ -112,7 +112,7 @@ public function render(Component $handler, Component $entity, Component $form, C ) ->setBody(<<getVariable()} = \$this->{$serviceInterface->toCamelCase(true)}->{$entity->getFindMethodName()}(\$request->getAttribute('uuid')); + {$entity->getVariable()} = \$this->{$serviceInterface->toCamelCase(true)}->{$entity->getFindMethodName()}(\$request->getAttribute('id')); } catch (NotFoundException \$exception) { \$this->messenger->addError(\$exception->getMessage()); @@ -122,7 +122,7 @@ public function render(Component $handler, Component $entity, Component $form, C \$this->{$form->toCamelCase()} ->setAttribute( 'action', - \$this->router->generateUri('{$entity->toKebabCase()}::edit-{$entity->toKebabCase()}', ['uuid' => {$entity->getVariable()}->getUuid()->toString()]) + \$this->router->generateUri('{$entity->toKebabCase()}::edit-{$entity->toKebabCase()}', ['id' => {$entity->getVariable()}->getId()->toString()]) ); try { diff --git a/src/Type/OpenApi.php b/src/Type/OpenApi.php index cc4907e..8f5ebf4 100644 --- a/src/Type/OpenApi.php +++ b/src/Type/OpenApi.php @@ -83,15 +83,15 @@ public function render( * @see {$apiDeleteResourceHandler->getComponent()->getClassName()}::handle() */ #[OA\Delete( - path: '/{$entity->toKebabCase()}/{uuid}', - description: 'Authenticated (super)admin deletes resource of type {$entity->getClassName()}, identified by its UUID', + path: '/{$entity->toKebabCase()}/{id}', + description: 'Authenticated (super)admin deletes resource of type {$entity->getClassName()}, identified by its ID', summary: 'Admin deletes a resource of type {$entity->getClassName()}', security: [['AuthToken' => []]], tags: ['{$entity->getClassName()}'], parameters: [ new OA\Parameter( - name: 'uuid', - description: '{$entity->getClassName()} UUID', + name: 'id', + description: '{$entity->getClassName()} ID', in: 'path', required: true, schema: new OA\Schema(type: 'string'), @@ -121,15 +121,15 @@ public function render( * @see {$apiGetResourceHandler->getComponent()->getClassName()}::handle() */ #[OA\Get( - path: '/{$entity->toKebabCase()}/{uuid}', - description: 'Authenticated (super)admin fetches a resource of type {$entity->getClassName()}, identified by its UUID', + path: '/{$entity->toKebabCase()}/{id}', + description: 'Authenticated (super)admin fetches a resource of type {$entity->getClassName()}, identified by its ID', summary: 'Admin fetches a resource of type {$entity->getClassName()}', security: [['AuthToken' => []]], tags: ['{$entity->getClassName()}'], parameters: [ new OA\Parameter( - name: 'uuid', - description: '{$entity->getClassName()} UUID', + name: 'id', + description: '{$entity->getClassName()} ID', in: 'path', required: true, schema: new OA\Schema(type: 'string'), @@ -237,7 +237,7 @@ public function render( * @see {$apiPatchResourceHandler->getComponent()->getClassName()}::handle() */ #[OA\Patch( - path: '/{$entity->toKebabCase()}/{uuid}', + path: '/{$entity->toKebabCase()}/{id}', description: 'Authenticated (super)admin updates an existing {$entity->getClassName()}', summary: 'Admin updates an existing {$entity->getClassName()}', security: [['AuthToken' => []]], @@ -252,8 +252,8 @@ public function render( tags: ['{$entity->getClassName()}'], parameters: [ new OA\Parameter( - name: 'uuid', - description: '{$entity->getClassName()} UUID', + name: 'id', + description: '{$entity->getClassName()} ID', in: 'path', required: true, schema: new OA\Schema(type: 'string'), @@ -345,7 +345,7 @@ public function render( * @see {$apiPutResourceHandler->getComponent()->getClassName()}::handle() */ #[OA\Put( - path: '/{$entity->toKebabCase()}/{uuid}', + path: '/{$entity->toKebabCase()}/{id}', description: 'Authenticated (super)admin replaces an existing {$entity->getClassName()}', summary: 'Admin updates an existing {$entity->getClassName()}', security: [['AuthToken' => []]], @@ -360,8 +360,8 @@ public function render( tags: ['{$entity->getClassName()}'], parameters: [ new OA\Parameter( - name: 'uuid', - description: '{$entity->getClassName()} UUID', + name: 'id', + description: '{$entity->getClassName()} ID', in: 'path', required: true, schema: new OA\Schema(type: 'string'), @@ -402,7 +402,7 @@ public function render( #[OA\Schema( schema: '{$entity->getClassName()}', properties: [ - new OA\Property(property: 'uuid', type: 'string', example: '1234abcd-abcd-4321-12ab-123456abcdef'), + new OA\Property(property: 'id', type: 'string', example: '1234abcd-abcd-4321-12ab-123456abcdef'), new OA\Property(property: 'created', type: 'object', example: new DateTimeImmutable()), new OA\Property(property: 'updated', type: 'object', example: new DateTimeImmutable()), new OA\Property( diff --git a/src/Type/Repository.php b/src/Type/Repository.php index 19d801f..f8bd24a 100644 --- a/src/Type/Repository.php +++ b/src/Type/Repository.php @@ -106,7 +106,7 @@ public function render(Component $repository, Component $entity): string ->orderBy(\$params['sort'], \$params['dir']) ->setFirstResult(\$params['offset']) ->setMaxResults(\$params['limit']) - ->groupBy('{$entity->toCamelCase()}.uuid'); + ->groupBy('{$entity->toCamelCase()}.id'); \$queryBuilder->getQuery()->useQueryCache(true); return \$queryBuilder; diff --git a/src/Type/RoutesDelegator.php b/src/Type/RoutesDelegator.php index 4ebe7d3..220442f 100644 --- a/src/Type/RoutesDelegator.php +++ b/src/Type/RoutesDelegator.php @@ -100,7 +100,7 @@ public function render( * @throws NotFoundExceptionInterface */ COMM) - ->appendBody('$uuid = ConfigProvider::REGEXP_UUID;') + ->appendBody('$id = ConfigProvider::REGEXP_UUID;') ->appendBody('', 0) ->appendBody('/** @var RouteCollectorInterface $routeCollector */') ->appendBody('$routeCollector = $container->get(RouteCollectorInterface::class);') @@ -111,7 +111,7 @@ public function render( $class->useClass($apiDeleteResourceHandler->getComponent()->getFqcn()); $invoke->appendBody( sprintf( - '->delete(\'/%s/\' . $uuid, %s, \'%s::delete-%s\')', + '->delete(\'/%s/\' . $id, %s, \'%s::delete-%s\')', $entity->toKebabCase(), $apiDeleteResourceHandler->getComponent()->getClassString(), $entity->toKebabCase(), @@ -125,7 +125,7 @@ public function render( $class->useClass($apiGetResourceHandler->getComponent()->getFqcn()); $invoke->appendBody( sprintf( - '->get(\'/%s/\' . $uuid, %s, \'%s::view-%s\')', + '->get(\'/%s/\' . $id, %s, \'%s::view-%s\')', $entity->toKebabCase(), $apiGetResourceHandler->getComponent()->getClassString(), $entity->toKebabCase(), @@ -153,7 +153,7 @@ public function render( $class->useClass($apiPatchResourceHandler->getComponent()->getFqcn()); $invoke->appendBody( sprintf( - '->patch(\'/%s/\' . $uuid, %s, \'%s::update-%s\')', + '->patch(\'/%s/\' . $id, %s, \'%s::update-%s\')', $entity->toKebabCase(), $apiPatchResourceHandler->getComponent()->getClassString(), $entity->toKebabCase(), @@ -181,7 +181,7 @@ public function render( $class->useClass($apiPutResourceHandler->getComponent()->getFqcn()); $invoke->appendBody( sprintf( - '->put(\'/%s/\' . $uuid, %s, \'%s::replace-%s\')', + '->put(\'/%s/\' . $id, %s, \'%s::replace-%s\')', $entity->toKebabCase(), $apiPutResourceHandler->getComponent()->getClassString(), $entity->toKebabCase(), @@ -223,7 +223,7 @@ public function render( $class->useClass($getDeleteResourceHandler->getComponent()->getFqcn()); $invoke->appendBody( sprintf( - '->get(\'/delete-%s/\' . $uuid, %s, \'%s::delete-%s-form\')', + '->get(\'/delete-%s/\' . $id, %s, \'%s::delete-%s-form\')', $entity->toKebabCase(), $getDeleteResourceHandler->getComponent()->getClassString(), $entity->toKebabCase(), @@ -237,7 +237,7 @@ public function render( $class->useClass($postDeleteResourceHandler->getComponent()->getFqcn()); $invoke->appendBody( sprintf( - '->post(\'/delete-%s/\' . $uuid, %s, \'%s::delete-%s\')', + '->post(\'/delete-%s/\' . $id, %s, \'%s::delete-%s\')', $entity->toKebabCase(), $postDeleteResourceHandler->getComponent()->getClassString(), $entity->toKebabCase(), @@ -251,7 +251,7 @@ public function render( $class->useClass($getEditResourceHandler->getComponent()->getFqcn()); $invoke->appendBody( sprintf( - '->get(\'/edit-%s/\' . $uuid, %s, \'%s::edit-%s-form\')', + '->get(\'/edit-%s/\' . $id, %s, \'%s::edit-%s-form\')', $entity->toKebabCase(), $getEditResourceHandler->getComponent()->getClassString(), $entity->toKebabCase(), @@ -265,7 +265,7 @@ public function render( $class->useClass($postEditResourceHandler->getComponent()->getFqcn()); $invoke->appendBody( sprintf( - '->post(\'/edit-%s/\' . $uuid, %s, \'%s::edit-%s\')', + '->post(\'/edit-%s/\' . $id, %s, \'%s::edit-%s\')', $entity->toKebabCase(), $postEditResourceHandler->getComponent()->getClassString(), $entity->toKebabCase(), @@ -293,7 +293,7 @@ public function render( $class->useClass($getViewResourceHandler->getComponent()->getFqcn()); $invoke->appendBody( sprintf( - '->get(\'/view-%s/\' . $uuid, %s, \'%s::view-%s-form\')', + '->get(\'/view-%s/\' . $id, %s, \'%s::view-%s-form\')', $entity->toKebabCase(), $getViewResourceHandler->getComponent()->getClassString(), $entity->toKebabCase(), diff --git a/src/Type/Service.php b/src/Type/Service.php index 4b9c0e3..369ac99 100644 --- a/src/Type/Service.php +++ b/src/Type/Service.php @@ -187,7 +187,7 @@ public function render( $findResource = (new Method($entity->getFindMethodName())) ->setReturnType($entity->getClassName()) ->addParameter( - new Parameter('uuid', 'string') + new Parameter('id', 'string') ) ->setComment(<<setBody(<<getVariable()} = \$this->{$repository->toCamelCase()}->find(\$uuid); + {$entity->getVariable()} = \$this->{$repository->toCamelCase()}->find(\$id); if (! {$entity->getVariable()} instanceof {$entity->getClassName()}) { throw new NotFoundException(Message::resourceNotFound('{$entity->getClassName()}')); } diff --git a/src/Type/ServiceInterface.php b/src/Type/ServiceInterface.php index d220ac0..6da6510 100644 --- a/src/Type/ServiceInterface.php +++ b/src/Type/ServiceInterface.php @@ -124,7 +124,7 @@ public function render(Component $serviceInterface, Component $repository, Compo (new Declaration($entity->getFindMethodName())) ->setReturnType($entity->getClassName()) ->addParameter( - new Parameter('uuid', 'string') + new Parameter('id', 'string') ) ->setComment(<<created(); - } + use UuidIdentifierTrait; /** * @return array{ - * uuid: non-empty-string, - * created: DateTimeImmutable, + * id: non-empty-string, + * created: DateTimeImmutable|null, * updated: DateTimeImmutable|null, * } */ public function getArrayCopy(): array { return [ - 'uuid' => \$this->uuid->toString(), + 'id' => \$this->id->toString(), 'created' => \$this->created, 'updated' => \$this->updated, ]; diff --git a/test/Type/HandlerTest.php b/test/Type/HandlerTest.php index b2550a9..1135bac 100644 --- a/test/Type/HandlerTest.php +++ b/test/Type/HandlerTest.php @@ -1153,7 +1153,7 @@ public function handle( ServerRequestInterface \$request, ): ResponseInterface { try { - \$bookStore = \$this->bookStoreService->findBookStore(\$request->getAttribute('uuid')); + \$bookStore = \$this->bookStoreService->findBookStore(\$request->getAttribute('id')); } catch (NotFoundException \$exception) { \$this->messenger->addError(\$exception->getMessage()); @@ -1391,7 +1391,7 @@ public function handle( ServerRequestInterface \$request, ): ResponseInterface { try { - \$bookStore = \$this->bookStoreService->findBookStore(\$request->getAttribute('uuid')); + \$bookStore = \$this->bookStoreService->findBookStore(\$request->getAttribute('id')); } catch (NotFoundException \$exception) { \$this->messenger->addError(\$exception->getMessage()); @@ -1400,7 +1400,7 @@ public function handle( \$this->deleteBookStoreForm->setAttribute( 'action', - \$this->router->generateUri('book-store::delete-book-store', ['uuid' => \$bookStore->getUuid()->toString()]) + \$this->router->generateUri('book-store::delete-book-store', ['id' => \$bookStore->getId()->toString()]) ); return new HtmlResponse( @@ -1473,7 +1473,7 @@ public function handle( ServerRequestInterface \$request, ): ResponseInterface { try { - \$bookStore = \$this->bookStoreService->findBookStore(\$request->getAttribute('uuid')); + \$bookStore = \$this->bookStoreService->findBookStore(\$request->getAttribute('id')); } catch (NotFoundException \$exception) { \$this->messenger->addError(\$exception->getMessage()); @@ -1482,7 +1482,7 @@ public function handle( \$this->deleteBookStoreForm->setAttribute( 'action', - \$this->router->generateUri('book-store::delete-book-store', ['uuid' => \$bookStore->getUuid()->toString()]) + \$this->router->generateUri('book-store::delete-book-store', ['id' => \$bookStore->getId()->toString()]) ); try { @@ -1573,7 +1573,7 @@ public function handle( ServerRequestInterface \$request, ): ResponseInterface { try { - \$bookStore = \$this->bookStoreService->findBookStore(\$request->getAttribute('uuid')); + \$bookStore = \$this->bookStoreService->findBookStore(\$request->getAttribute('id')); } catch (NotFoundException \$exception) { \$this->messenger->addError(\$exception->getMessage()); @@ -1583,7 +1583,7 @@ public function handle( \$this->editBookStoreForm ->setAttribute( 'action', - \$this->router->generateUri('book-store::edit-book-store', ['uuid' => \$bookStore->getUuid()->toString()]) + \$this->router->generateUri('book-store::edit-book-store', ['id' => \$bookStore->getId()->toString()]) ) ->bind(\$bookStore); @@ -1661,7 +1661,7 @@ public function handle( ServerRequestInterface \$request, ): ResponseInterface { try { - \$bookStore = \$this->bookStoreService->findBookStore(\$request->getAttribute('uuid')); + \$bookStore = \$this->bookStoreService->findBookStore(\$request->getAttribute('id')); } catch (NotFoundException \$exception) { \$this->messenger->addError(\$exception->getMessage()); @@ -1671,7 +1671,7 @@ public function handle( \$this->editBookStoreForm ->setAttribute( 'action', - \$this->router->generateUri('book-store::edit-book-store', ['uuid' => \$bookStore->getUuid()->toString()]) + \$this->router->generateUri('book-store::edit-book-store', ['id' => \$bookStore->getId()->toString()]) ); try { diff --git a/test/Type/OpenApiTest.php b/test/Type/OpenApiTest.php index 7921d1b..89f42ed 100644 --- a/test/Type/OpenApiTest.php +++ b/test/Type/OpenApiTest.php @@ -168,15 +168,15 @@ private function dataProviderRenderedOpenApi(): string * @see DeleteModuleNameResourceHandler::handle() */ #[OA\Delete( - path: '/module-name/{uuid}', - description: 'Authenticated (super)admin deletes resource of type ModuleName, identified by its UUID', + path: '/module-name/{id}', + description: 'Authenticated (super)admin deletes resource of type ModuleName, identified by its ID', summary: 'Admin deletes a resource of type ModuleName', security: [['AuthToken' => []]], tags: ['ModuleName'], parameters: [ new OA\Parameter( - name: 'uuid', - description: 'ModuleName UUID', + name: 'id', + description: 'ModuleName ID', in: 'path', required: true, schema: new OA\Schema(type: 'string'), @@ -198,15 +198,15 @@ private function dataProviderRenderedOpenApi(): string * @see GetModuleNameResourceHandler::handle() */ #[OA\Get( - path: '/module-name/{uuid}', - description: 'Authenticated (super)admin fetches a resource of type ModuleName, identified by its UUID', + path: '/module-name/{id}', + description: 'Authenticated (super)admin fetches a resource of type ModuleName, identified by its ID', summary: 'Admin fetches a resource of type ModuleName', security: [['AuthToken' => []]], tags: ['ModuleName'], parameters: [ new OA\Parameter( - name: 'uuid', - description: 'ModuleName UUID', + name: 'id', + description: 'ModuleName ID', in: 'path', required: true, schema: new OA\Schema(type: 'string'), @@ -296,7 +296,7 @@ private function dataProviderRenderedOpenApi(): string * @see PatchModuleNameResourceHandler::handle() */ #[OA\Patch( - path: '/module-name/{uuid}', + path: '/module-name/{id}', description: 'Authenticated (super)admin updates an existing ModuleName', summary: 'Admin updates an existing ModuleName', security: [['AuthToken' => []]], @@ -311,8 +311,8 @@ private function dataProviderRenderedOpenApi(): string tags: ['ModuleName'], parameters: [ new OA\Parameter( - name: 'uuid', - description: 'ModuleName UUID', + name: 'id', + description: 'ModuleName ID', in: 'path', required: true, schema: new OA\Schema(type: 'string'), @@ -388,7 +388,7 @@ private function dataProviderRenderedOpenApi(): string * @see PutModuleNameResourceHandler::handle() */ #[OA\Put( - path: '/module-name/{uuid}', + path: '/module-name/{id}', description: 'Authenticated (super)admin replaces an existing ModuleName', summary: 'Admin updates an existing ModuleName', security: [['AuthToken' => []]], @@ -403,8 +403,8 @@ private function dataProviderRenderedOpenApi(): string tags: ['ModuleName'], parameters: [ new OA\Parameter( - name: 'uuid', - description: 'ModuleName UUID', + name: 'id', + description: 'ModuleName ID', in: 'path', required: true, schema: new OA\Schema(type: 'string'), @@ -440,7 +440,7 @@ private function dataProviderRenderedOpenApi(): string #[OA\Schema( schema: 'ModuleName', properties: [ - new OA\Property(property: 'uuid', type: 'string', example: '1234abcd-abcd-4321-12ab-123456abcdef'), + new OA\Property(property: 'id', type: 'string', example: '1234abcd-abcd-4321-12ab-123456abcdef'), new OA\Property(property: 'created', type: 'object', example: new DateTimeImmutable()), new OA\Property(property: 'updated', type: 'object', example: new DateTimeImmutable()), new OA\Property( diff --git a/test/Type/RepositoryTest.php b/test/Type/RepositoryTest.php index ecb634a..c841c49 100644 --- a/test/Type/RepositoryTest.php +++ b/test/Type/RepositoryTest.php @@ -230,7 +230,7 @@ public function getBookStores( ->orderBy(\$params['sort'], \$params['dir']) ->setFirstResult(\$params['offset']) ->setMaxResults(\$params['limit']) - ->groupBy('bookStore.uuid'); + ->groupBy('bookStore.id'); \$queryBuilder->getQuery()->useQueryCache(true); return \$queryBuilder; diff --git a/test/Type/RoutesDelegatorTest.php b/test/Type/RoutesDelegatorTest.php index de0e6cc..0085f5d 100644 --- a/test/Type/RoutesDelegatorTest.php +++ b/test/Type/RoutesDelegatorTest.php @@ -155,7 +155,7 @@ public function __invoke( string \$serviceName, callable \$callback, ): Application { - \$uuid = ConfigProvider::REGEXP_UUID; + \$id = ConfigProvider::REGEXP_UUID; /** @var RouteCollectorInterface \$routeCollector */ \$routeCollector = \$container->get(RouteCollectorInterface::class); @@ -163,12 +163,12 @@ public function __invoke( \$routeCollector ->get('/create-book-store', GetCreateBookStoreFormHandler::class, 'book-store::create-book-store-form') ->post('/create-book-store', PostCreateBookStoreHandler::class, 'book-store::create-book-store') - ->get('/delete-book-store/' . \$uuid, GetDeleteBookStoreFormHandler::class, 'book-store::delete-book-store-form') - ->post('/delete-book-store/' . \$uuid, PostDeleteBookStoreHandler::class, 'book-store::delete-book-store') - ->get('/edit-book-store/' . \$uuid, GetEditBookStoreFormHandler::class, 'book-store::edit-book-store-form') - ->post('/edit-book-store/' . \$uuid, PostEditBookStoreHandler::class, 'book-store::edit-book-store') + ->get('/delete-book-store/' . \$id, GetDeleteBookStoreFormHandler::class, 'book-store::delete-book-store-form') + ->post('/delete-book-store/' . \$id, PostDeleteBookStoreHandler::class, 'book-store::delete-book-store') + ->get('/edit-book-store/' . \$id, GetEditBookStoreFormHandler::class, 'book-store::edit-book-store-form') + ->post('/edit-book-store/' . \$id, PostEditBookStoreHandler::class, 'book-store::edit-book-store') ->get('/list-book-store', GetListBookStoreHandler::class, 'book-store::list-book-store') - ->get('/view-book-store/' . \$uuid, GetViewBookStoreHandler::class, 'book-store::view-book-store-form'); + ->get('/view-book-store/' . \$id, GetViewBookStoreHandler::class, 'book-store::view-book-store-form'); return \$callback(); } @@ -216,18 +216,18 @@ public function __invoke( string \$serviceName, callable \$callback, ): Application { - \$uuid = ConfigProvider::REGEXP_UUID; + \$id = ConfigProvider::REGEXP_UUID; /** @var RouteCollectorInterface \$routeCollector */ \$routeCollector = \$container->get(RouteCollectorInterface::class); \$routeCollector - ->delete('/book-store/' . \$uuid, DeleteBookStoreResourceHandler::class, 'book-store::delete-book-store') - ->get('/book-store/' . \$uuid, GetBookStoreResourceHandler::class, 'book-store::view-book-store') + ->delete('/book-store/' . \$id, DeleteBookStoreResourceHandler::class, 'book-store::delete-book-store') + ->get('/book-store/' . \$id, GetBookStoreResourceHandler::class, 'book-store::view-book-store') ->get('/book-store', GetBookStoreCollectionHandler::class, 'book-store::list-book-store') - ->patch('/book-store/' . \$uuid, PatchBookStoreResourceHandler::class, 'book-store::update-book-store') + ->patch('/book-store/' . \$id, PatchBookStoreResourceHandler::class, 'book-store::update-book-store') ->post('/book-store', PostBookStoreResourceHandler::class, 'book-store::create-book-store') - ->put('/book-store/' . \$uuid, PutBookStoreResourceHandler::class, 'book-store::replace-book-store'); + ->put('/book-store/' . \$id, PutBookStoreResourceHandler::class, 'book-store::replace-book-store'); return \$callback(); } diff --git a/test/Type/ServiceInterfaceTest.php b/test/Type/ServiceInterfaceTest.php index 8f3c997..f2b474f 100644 --- a/test/Type/ServiceInterfaceTest.php +++ b/test/Type/ServiceInterfaceTest.php @@ -342,7 +342,7 @@ public function saveBookStore( * @throws NotFoundException */ public function findBookStore( - string \$uuid, + string \$id, ): BookStore; } diff --git a/test/Type/ServiceTest.php b/test/Type/ServiceTest.php index f4ef6ec..36cb93b 100644 --- a/test/Type/ServiceTest.php +++ b/test/Type/ServiceTest.php @@ -382,9 +382,9 @@ public function saveBookStore( * @throws NotFoundException */ public function findBookStore( - string \$uuid, + string \$id, ): BookStore { - \$bookStore = \$this->bookStoreRepository->find(\$uuid); + \$bookStore = \$this->bookStoreRepository->find(\$id); if (! \$bookStore instanceof BookStore) { throw new NotFoundException(Message::resourceNotFound('BookStore')); }