initial commit
This commit is contained in:
19
vendor/symfony/maker-bundle/LICENSE
vendored
Normal file
19
vendor/symfony/maker-bundle/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2004-2020 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
78
vendor/symfony/maker-bundle/composer.json
vendored
Normal file
78
vendor/symfony/maker-bundle/composer.json
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"description": "Symfony Maker helps you create empty commands, controllers, form classes, tests and more so you can forget about writing boilerplate code.",
|
||||
"homepage": "https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html",
|
||||
"name": "symfony/maker-bundle",
|
||||
"type": "symfony-bundle",
|
||||
"license": "MIT",
|
||||
"keywords": ["generator", "code generator", "scaffolding", "scaffold", "dev"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"doctrine/inflector": "^2.0",
|
||||
"nikic/php-parser": "^5.0",
|
||||
"symfony/config": "^6.4|^7.0",
|
||||
"symfony/console": "^6.4|^7.0",
|
||||
"symfony/dependency-injection": "^6.4|^7.0",
|
||||
"symfony/deprecation-contracts": "^2.2|^3",
|
||||
"symfony/filesystem": "^6.4|^7.0",
|
||||
"symfony/finder": "^6.4|^7.0",
|
||||
"symfony/framework-bundle": "^6.4|^7.0",
|
||||
"symfony/http-kernel": "^6.4|^7.0",
|
||||
"symfony/process": "^6.4|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/semver": "^3.0",
|
||||
"doctrine/doctrine-bundle": "^2.5.0",
|
||||
"doctrine/orm": "^2.15|^3",
|
||||
"symfony/http-client": "^6.4|^7.0",
|
||||
"symfony/phpunit-bridge": "^6.4.1|^7.0",
|
||||
"symfony/security-core": "^6.4|^7.0",
|
||||
"symfony/security-http": "^6.4|^7.0",
|
||||
"symfony/yaml": "^6.4|^7.0",
|
||||
"twig/twig": "^3.0|^4.x-dev"
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/orm": "<2.15",
|
||||
"doctrine/doctrine-bundle": "<2.10"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Bundle\\MakerBundle\\": "src/" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "Symfony\\Bundle\\MakerBundle\\Tests\\": "tests/" }
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"tools:upgrade": [
|
||||
"@tools:upgrade:php-cs-fixer",
|
||||
"@tools:upgrade:phpstan",
|
||||
"@tools:upgrade:twigcs"
|
||||
],
|
||||
"tools:upgrade:php-cs-fixer": "composer upgrade -W -d tools/php-cs-fixer",
|
||||
"tools:upgrade:phpstan": "composer upgrade -W -d tools/phpstan",
|
||||
"tools:upgrade:twigcs": "composer upgrade -W -d tools/twigcs",
|
||||
"tools:run": [
|
||||
"@tools:run:php-cs-fixer",
|
||||
"@tools:run:phpstan",
|
||||
"@tools:run:twigcs"
|
||||
],
|
||||
"tools:run:php-cs-fixer": "tools/php-cs-fixer/vendor/bin/php-cs-fixer fix",
|
||||
"tools:run:phpstan": "tools/phpstan/vendor/bin/phpstan --memory-limit=1G",
|
||||
"tools:run:twigcs": "tools/twigcs/vendor/bin/twigcs --config tools/twigcs/.twigcs.dist.php"
|
||||
}
|
||||
}
|
||||
8
vendor/symfony/maker-bundle/config/help/MakeAuth.txt
vendored
Normal file
8
vendor/symfony/maker-bundle/config/help/MakeAuth.txt
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
The <info>%command.name%</info> command generates various authentication systems,
|
||||
by asking questions.
|
||||
|
||||
It can provide an empty authenticator, or a full login form authentication process.
|
||||
In both cases it also updates your <info>security.yaml</info>.
|
||||
For the login form, it also generates a controller and the Twig template.
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeCommand.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeCommand.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates a new command:
|
||||
|
||||
<info>php %command.full_name% app:do-something</info>
|
||||
|
||||
If the argument is missing, the command will ask for the command name interactively.
|
||||
14
vendor/symfony/maker-bundle/config/help/MakeController.txt
vendored
Normal file
14
vendor/symfony/maker-bundle/config/help/MakeController.txt
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
The <info>%command.name%</info> command generates a new controller class.
|
||||
|
||||
<info>php %command.full_name% CoolStuffController</info>
|
||||
|
||||
If the argument is missing, the command will ask for the controller class name interactively.
|
||||
|
||||
If you have the <info>symfony/twig-bundle</info> installed, a Twig template will also be
|
||||
generated for the controller.
|
||||
|
||||
<info>composer require symfony/twig-bundle</info>
|
||||
|
||||
You can also generate the controller alone, without template with this option:
|
||||
|
||||
<info>php %command.full_name% --no-template</info>
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeCrud.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeCrud.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates crud controller with templates for selected entity.
|
||||
|
||||
<info>php %command.full_name% BlogPost</info>
|
||||
|
||||
If the argument is missing, the command will ask for the entity class name interactively.
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeDockerDatabase.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeDockerDatabase.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates or updates databases services in compose.yaml
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
Supports MySQL, MariaDB and PostgreSQL
|
||||
24
vendor/symfony/maker-bundle/config/help/MakeEntity.txt
vendored
Normal file
24
vendor/symfony/maker-bundle/config/help/MakeEntity.txt
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
The <info>%command.name%</info> command creates or updates an entity and repository class.
|
||||
|
||||
<info>php %command.full_name% BlogPost</info>
|
||||
|
||||
If the argument is missing, the command will ask for the entity class name interactively.
|
||||
|
||||
You can also mark this class as an API Platform resource. A hypermedia CRUD API will
|
||||
automatically be available for this entity class:
|
||||
|
||||
<info>php %command.full_name% --api-resource</info>
|
||||
|
||||
Symfony can also broadcast all changes made to the entity to the client using Symfony
|
||||
UX Turbo.
|
||||
|
||||
<info>php %command.full_name% --broadcast</info>
|
||||
|
||||
You can also generate all the getter/setter/adder/remover methods
|
||||
for the properties of existing entities:
|
||||
|
||||
<info>php %command.full_name% --regenerate</info>
|
||||
|
||||
You can also *overwrite* any existing methods:
|
||||
|
||||
<info>php %command.full_name% --regenerate --overwrite</info>
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeFixture.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeFixture.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates a new Doctrine fixtures class.
|
||||
|
||||
<info>php %command.full_name% AppFixtures</info>
|
||||
|
||||
If the argument is missing, the command will ask for a class interactively.
|
||||
16
vendor/symfony/maker-bundle/config/help/MakeForm.txt
vendored
Normal file
16
vendor/symfony/maker-bundle/config/help/MakeForm.txt
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
The <info>%command.name%</info> command generates a new form class.
|
||||
|
||||
<info>php %command.full_name% UserType</info>
|
||||
|
||||
If the argument is missing, the command will ask for the form class interactively.
|
||||
|
||||
You can optionally specify the bound class in a second argument.
|
||||
This can be the name of an entity like <info>User</info>
|
||||
|
||||
<info>php %command.full_name% UserType User</info>
|
||||
|
||||
You can also specify a fully qualified name to another class like <info>\App\Dto\UserData</info>.
|
||||
Slashes must be escaped in the argument.
|
||||
|
||||
<info>php %command.full_name% UserType \\App\\Dto\\UserData</info>
|
||||
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeFunctionalTest.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeFunctionalTest.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates a new functional test class.
|
||||
|
||||
<info>php %command.full_name% DefaultControllerTest</info>
|
||||
|
||||
If the argument is missing, the command will ask for the class name interactively.
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeListener.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeListener.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates a new event subscriber class or a new event listener class.
|
||||
|
||||
<info>php %command.full_name% ExceptionListener</info>
|
||||
|
||||
If the argument is missing, the command will ask for the class name interactively.
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeMessage.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeMessage.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates a new message class & handler.
|
||||
|
||||
<info>php %command.full_name% EmailMessage</info>
|
||||
|
||||
If the argument is missing, the command will ask for the message class interactively.
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeMiddleware.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeMiddleware.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates a new Middleware class.
|
||||
|
||||
<info>php %command.full_name% CustomMiddleware</info>
|
||||
|
||||
If the argument is missing, the command will ask for the message class interactively.
|
||||
7
vendor/symfony/maker-bundle/config/help/MakeMigration.txt
vendored
Normal file
7
vendor/symfony/maker-bundle/config/help/MakeMigration.txt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
The <info>%command.name%</info> command generates a new migration:
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
You can also generate a formatted migration with this option:
|
||||
|
||||
<info>php %command.full_name% --formatted</info>
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeRegistrationForm.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeRegistrationForm.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates a complete registration form, controller & template.
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
The command will ask for several pieces of information to build your form.
|
||||
18
vendor/symfony/maker-bundle/config/help/MakeResetPassword.txt
vendored
Normal file
18
vendor/symfony/maker-bundle/config/help/MakeResetPassword.txt
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
The <info>%command.name%</info> command generates all the files needed to implement
|
||||
a fully-functional & secure password reset system.
|
||||
|
||||
The SymfonycastsResetPasswordBundle is required and can be added using composer:
|
||||
<info>composer require symfonycasts/reset-password-bundle</info>
|
||||
|
||||
For more information on the <info>reset-password-bundle</info> check out:
|
||||
<href=https://github.com/symfonycasts/reset-password-bundle>https://github.com/symfonycasts/reset-password-bundle</>
|
||||
|
||||
<info>%command.name%</info> requires a user entity with an email property,
|
||||
email getter method, and a password setter method. Maker will ask for these
|
||||
interactively if they cannot be guessed.
|
||||
|
||||
Maker will also update your <info>reset-password.yaml</info> configuration file
|
||||
if one exists. If you have customized the configuration file, maker will attempt
|
||||
to modify it accordingly but preserve your customizations.
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
8
vendor/symfony/maker-bundle/config/help/MakeScheduler.txt
vendored
Normal file
8
vendor/symfony/maker-bundle/config/help/MakeScheduler.txt
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
The <info>%command.name%</info> command generates a schedule to automate repeated
|
||||
tasks using Symfony's Scheduler Component.
|
||||
|
||||
If the Scheduler Component is not installed, <info>%command.name%</info> will
|
||||
install it automatically using composer. You can of course do this manually by
|
||||
running <info>composer require symfony/scheduler</info>.
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeSerializerEncoder.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeSerializerEncoder.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates a new serializer encoder class.
|
||||
|
||||
<info>php %command.full_name% YamlEncoder</info>
|
||||
|
||||
If the argument is missing, the command will ask for the class name interactively.
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeSerializerNormalizer.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeSerializerNormalizer.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates a new serializer normalizer class.
|
||||
|
||||
<info>php %command.full_name% UserNormalizer</info>
|
||||
|
||||
If the argument is missing, the command will ask for the class name interactively.
|
||||
15
vendor/symfony/maker-bundle/config/help/MakeStimulusController.txt
vendored
Normal file
15
vendor/symfony/maker-bundle/config/help/MakeStimulusController.txt
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
The <info>%command.name%</info> command generates a new Stimulus controller.
|
||||
|
||||
<info>php %command.full_name% hello</info>
|
||||
|
||||
If the argument is missing, the command will ask for the controller name interactively.
|
||||
|
||||
To generate a TypeScript file (instead of a JavaScript file) use the <info>--typescript</info>
|
||||
(or <info>--ts</info>) option:
|
||||
|
||||
<info>php %command.full_name% hello --typescript</info>
|
||||
|
||||
It will also interactively ask for values, targets, classes to add to the Stimulus
|
||||
controller (optional).
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeSubscriber.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeSubscriber.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates a new event subscriber class.
|
||||
|
||||
<info>php %command.full_name% ExceptionSubscriber</info>
|
||||
|
||||
If the argument is missing, the command will ask for the class name interactively.
|
||||
7
vendor/symfony/maker-bundle/config/help/MakeTest.txt
vendored
Normal file
7
vendor/symfony/maker-bundle/config/help/MakeTest.txt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
The <info>%command.name%</info> command generates a new test class.
|
||||
|
||||
<info>php %command.full_name% TestCase BlogPostTest</info>
|
||||
|
||||
If the first argument is missing, the command will ask for the test type interactively.
|
||||
|
||||
If the second argument is missing, the command will ask for the class name interactively.
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeTwigExtension.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeTwigExtension.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates a new Twig extension with its runtime class.
|
||||
|
||||
<info>php %command.full_name% AppExtension</info>
|
||||
|
||||
If the argument is missing, the command will ask for the class name interactively.
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeUnitTest.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeUnitTest.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates a new unit test class.
|
||||
|
||||
<info>php %command.full_name% UtilTest</info>
|
||||
|
||||
If the argument is missing, the command will ask for the class name interactively.
|
||||
7
vendor/symfony/maker-bundle/config/help/MakeUser.txt
vendored
Normal file
7
vendor/symfony/maker-bundle/config/help/MakeUser.txt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
The <info>%command.name%</info> command generates a new user class for security
|
||||
and updates your security.yaml file for it. It will also generate a user provider
|
||||
class if your situation needs a custom class.
|
||||
|
||||
<info>php %command.full_name% User</info>
|
||||
|
||||
If the argument is missing, the command will ask for the class name interactively.
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeValidator.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeValidator.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates a new validation constraint.
|
||||
|
||||
<info>php %command.full_name% EnabledValidator</info>
|
||||
|
||||
If the argument is missing, the command will ask for the constraint class name interactively.
|
||||
5
vendor/symfony/maker-bundle/config/help/MakeVoter.txt
vendored
Normal file
5
vendor/symfony/maker-bundle/config/help/MakeVoter.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
The <info>%command.name%</info> command generates a new security voter.
|
||||
|
||||
<info>php %command.full_name% BlogPostVoter</info>
|
||||
|
||||
If the argument is missing, the command will ask for the class name interactively.
|
||||
8
vendor/symfony/maker-bundle/config/help/MakeWebhook.txt
vendored
Normal file
8
vendor/symfony/maker-bundle/config/help/MakeWebhook.txt
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
The <info>%command.name%</info> command creates a RequestParser, a WebhookHandler and adds the necessary configuration
|
||||
for a new Webhook.
|
||||
|
||||
<info>php %command.full_name% stripe</info>
|
||||
|
||||
If the argument is missing, the command will ask for the webhook name interactively.
|
||||
|
||||
It will also interactively ask for the RequestMatchers to use for the RequestParser's getRequestMatcher function.
|
||||
6
vendor/symfony/maker-bundle/config/help/_WithTests.txt
vendored
Normal file
6
vendor/symfony/maker-bundle/config/help/_WithTests.txt
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
To generate tailored PHPUnit tests, simply call:
|
||||
|
||||
<info>php %command.full_name% --with-tests</info>
|
||||
|
||||
This will generate a unit test in <info>tests/</info> for you to review then use
|
||||
to test the new functionality of your app.
|
||||
10
vendor/symfony/maker-bundle/config/help/_WithUid.txt
vendored
Normal file
10
vendor/symfony/maker-bundle/config/help/_WithUid.txt
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
Instead of using the default "int" type for the entity's "id", you can use the
|
||||
UUID type from Symfony's Uid component.
|
||||
<href=https://symfony.com/doc/current/components/uid.html#storing-uuids-in-databases>https://symfony.com/doc/current/components/uid.html#storing-uuids-in-databases</>
|
||||
|
||||
<info>php %command.full_name% --with-uuid</info>
|
||||
|
||||
Or you can use the ULID type from Symfony's Uid component.
|
||||
<href=https://symfony.com/doc/current/components/uid.html#storing-ulids-in-databases>https://symfony.com/doc/current/components/uid.html#storing-ulids-in-databases</>
|
||||
|
||||
<info>php %command.full_name% --with-ulid</info>
|
||||
8
vendor/symfony/maker-bundle/config/help/security/MakeCustom.txt
vendored
Normal file
8
vendor/symfony/maker-bundle/config/help/security/MakeCustom.txt
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
The <info>%command.name%</info> command generates a simple custom authenticator
|
||||
class based off the example provided in:
|
||||
|
||||
<href=https://symfony.com/doc/current/security/custom_authenticator.html>https://symfony.com/doc/current/security/custom_authenticator.html</>
|
||||
|
||||
This will also update your <info>security.yaml</info> for the new custom authenticator.
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
9
vendor/symfony/maker-bundle/config/help/security/MakeFormLogin.txt
vendored
Normal file
9
vendor/symfony/maker-bundle/config/help/security/MakeFormLogin.txt
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
The <info>%command.name%</info> command generates a controller and Twig template
|
||||
to allow users to login using the form_login authenticator.
|
||||
|
||||
The controller name, and logout ability can be customized by answering the
|
||||
questions asked when running <info>%command.name%</info>.
|
||||
|
||||
This will also update your <info>security.yaml</info> for the new authenticator.
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
175
vendor/symfony/maker-bundle/config/makers.xml
vendored
Normal file
175
vendor/symfony/maker-bundle/config/makers.xml
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<services>
|
||||
<defaults public="false" />
|
||||
|
||||
<service id="maker.maker.make_authenticator" class="Symfony\Bundle\MakerBundle\Maker\MakeAuthenticator">
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
<argument type="service" id="maker.security_config_updater" />
|
||||
<argument type="service" id="maker.generator" />
|
||||
<argument type="service" id="maker.doctrine_helper" />
|
||||
<argument type="service" id="maker.security_controller_builder" />
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_command" class="Symfony\Bundle\MakerBundle\Maker\MakeCommand">
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_twig_component" class="Symfony\Bundle\MakerBundle\Maker\MakeTwigComponent">
|
||||
<tag name="maker.command" />
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_controller" class="Symfony\Bundle\MakerBundle\Maker\MakeController">
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_crud" class="Symfony\Bundle\MakerBundle\Maker\MakeCrud">
|
||||
<argument type="service" id="maker.doctrine_helper" />
|
||||
<argument type="service" id="maker.renderer.form_type_renderer" />
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_docker_database" class="Symfony\Bundle\MakerBundle\Maker\MakeDockerDatabase">
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_entity" class="Symfony\Bundle\MakerBundle\Maker\MakeEntity">
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
<argument type="service" id="maker.doctrine_helper" />
|
||||
<argument>null</argument>
|
||||
<argument type="service" id="maker.generator" />
|
||||
<argument type="service" id="maker.entity_class_generator" />
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_fixtures" class="Symfony\Bundle\MakerBundle\Maker\MakeFixtures">
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_form" class="Symfony\Bundle\MakerBundle\Maker\MakeForm">
|
||||
<argument type="service" id="maker.doctrine_helper" />
|
||||
<argument type="service" id="maker.renderer.form_type_renderer" />
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_functional_test" class="Symfony\Bundle\MakerBundle\Maker\MakeFunctionalTest">
|
||||
<tag name="maker.command" />
|
||||
<deprecated package="symfony/maker-bundle" version="1.29">The "%service_id%" service is deprecated, use "maker.maker.make_test" instead.</deprecated>
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_listener" class="Symfony\Bundle\MakerBundle\Maker\MakeListener">
|
||||
<tag name="maker.command" />
|
||||
<argument type="service" id="maker.event_registry" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_message" class="Symfony\Bundle\MakerBundle\Maker\MakeMessage">
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_messenger_middleware" class="Symfony\Bundle\MakerBundle\Maker\MakeMessengerMiddleware">
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_registration_form" class="Symfony\Bundle\MakerBundle\Maker\MakeRegistrationForm">
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
<argument type="service" id="maker.renderer.form_type_renderer" />
|
||||
<argument type="service" id="maker.doctrine_helper" />
|
||||
<argument type="service" id="router" on-invalid="ignore" />
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_reset_password" class="Symfony\Bundle\MakerBundle\Maker\MakeResetPassword">
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
<argument type="service" id="maker.doctrine_helper" />
|
||||
<argument type="service" id="maker.entity_class_generator" />
|
||||
<argument type="service" id="router" on-invalid="ignore" />
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_schedule" class="Symfony\Bundle\MakerBundle\Maker\MakeSchedule">
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_serializer_encoder" class="Symfony\Bundle\MakerBundle\Maker\MakeSerializerEncoder">
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_serializer_normalizer" class="Symfony\Bundle\MakerBundle\Maker\MakeSerializerNormalizer">
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_subscriber" class="Symfony\Bundle\MakerBundle\Maker\MakeSubscriber">
|
||||
<tag name="maker.command" />
|
||||
<argument type="service" id="maker.event_registry" />
|
||||
<deprecated package="symfony/maker-bundle" version="1.51">The "%service_id%" service is deprecated, use "maker.maker.make_listener" instead.</deprecated>
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_twig_extension" class="Symfony\Bundle\MakerBundle\Maker\MakeTwigExtension">
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_test" class="Symfony\Bundle\MakerBundle\Maker\MakeTest">
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_unit_test" class="Symfony\Bundle\MakerBundle\Maker\MakeUnitTest">
|
||||
<tag name="maker.command" />
|
||||
<deprecated package="symfony/maker-bundle" version="1.29">The "%service_id%" service is deprecated, use "maker.maker.make_test" instead.</deprecated>
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_validator" class="Symfony\Bundle\MakerBundle\Maker\MakeValidator">
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_voter" class="Symfony\Bundle\MakerBundle\Maker\MakeVoter">
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_user" class="Symfony\Bundle\MakerBundle\Maker\MakeUser">
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
<argument type="service" id="maker.user_class_builder" />
|
||||
<argument type="service" id="maker.security_config_updater" />
|
||||
<argument type="service" id="maker.entity_class_generator" />
|
||||
<argument type="service" id="maker.doctrine_helper" />
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_migration" class="Symfony\Bundle\MakerBundle\Maker\MakeMigration">
|
||||
<argument>%kernel.project_dir%</argument>
|
||||
<argument type="service" id="maker.file_link_formatter" />
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_stimulus_controller" class="Symfony\Bundle\MakerBundle\Maker\MakeStimulusController">
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_form_login" class="Symfony\Bundle\MakerBundle\Maker\Security\MakeFormLogin">
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
<argument type="service" id="maker.security_config_updater" />
|
||||
<argument type="service" id="maker.security_controller_builder" />
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_custom_authenticator" class="Symfony\Bundle\MakerBundle\Maker\Security\MakeCustomAuthenticator">
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
<argument type="service" id="maker.generator" />
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
|
||||
<service id="maker.maker.make_webhook" class="Symfony\Bundle\MakerBundle\Maker\MakeWebhook">
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
<argument type="service" id="maker.generator" />
|
||||
<tag name="maker.command" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
29
vendor/symfony/maker-bundle/config/php-cs-fixer.config.php
vendored
Normal file
29
vendor/symfony/maker-bundle/config/php-cs-fixer.config.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This PHP-CS-Fixer config file is used by the TemplateLinter for userland
|
||||
* code when say make:controller is run. If a user does not have a php-cs-fixer
|
||||
* config file, this one is used on the generated PHP files.
|
||||
*
|
||||
* It should not be confused by the root level .php-cs-fixer.dist.php config
|
||||
* which is used to maintain the MakerBundle codebase itself.
|
||||
*/
|
||||
return (new PhpCsFixer\Config())
|
||||
->setRules([
|
||||
'@Symfony' => true,
|
||||
'@Symfony:risky' => true,
|
||||
'native_function_invocation' => false,
|
||||
'blank_line_before_statement' => ['statements' => ['break', 'case', 'continue', 'declare', 'default', 'do', 'exit', 'for', 'foreach', 'goto', 'if', 'include', 'include_once', 'phpdoc', 'require', 'require_once', 'return', 'switch', 'throw', 'try', 'while', 'yield', 'yield_from']],
|
||||
'array_indentation' => true,
|
||||
])
|
||||
->setRiskyAllowed(true)
|
||||
;
|
||||
88
vendor/symfony/maker-bundle/config/services.xml
vendored
Normal file
88
vendor/symfony/maker-bundle/config/services.xml
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<services>
|
||||
<defaults public="false" />
|
||||
|
||||
<service id="maker.file_manager" class="Symfony\Bundle\MakerBundle\FileManager">
|
||||
<argument type="service" id="filesystem" />
|
||||
<argument type="service" id="maker.autoloader_util" />
|
||||
<argument type="service" id="maker.file_link_formatter" />
|
||||
<argument>%kernel.project_dir%</argument>
|
||||
<argument>%twig.default_path%</argument>
|
||||
</service>
|
||||
|
||||
<service id="maker.autoloader_finder" class="Symfony\Bundle\MakerBundle\Util\ComposerAutoloaderFinder" >
|
||||
<argument /> <!-- root namespace -->
|
||||
</service>
|
||||
|
||||
<service id="maker.autoloader_util" class="Symfony\Bundle\MakerBundle\Util\AutoloaderUtil">
|
||||
<argument type="service" id="maker.autoloader_finder" />
|
||||
</service>
|
||||
|
||||
<service id="maker.file_link_formatter" class="Symfony\Bundle\MakerBundle\Util\MakerFileLinkFormatter" >
|
||||
<argument type="service" id="debug.file_link_formatter" on-invalid="ignore" />
|
||||
</service>
|
||||
|
||||
<service id="maker.event_registry" class="Symfony\Bundle\MakerBundle\EventRegistry">
|
||||
<argument type="service" id="event_dispatcher" />
|
||||
</service>
|
||||
|
||||
<service id="maker.console_error_listener" class="Symfony\Bundle\MakerBundle\Event\ConsoleErrorSubscriber">
|
||||
<tag name="kernel.event_subscriber" />
|
||||
</service>
|
||||
|
||||
<service id="maker.doctrine_helper" class="Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper">
|
||||
<argument /> <!-- entity namespace -->
|
||||
<argument type="service" id="doctrine" on-invalid="ignore" />
|
||||
</service>
|
||||
|
||||
<service id="maker.template_linter" class="Symfony\Bundle\MakerBundle\Util\TemplateLinter">
|
||||
<argument>%env(default::string:MAKER_PHP_CS_FIXER_BINARY_PATH)%</argument>
|
||||
<argument>%env(default::string:MAKER_PHP_CS_FIXER_CONFIG_PATH)%</argument>
|
||||
</service>
|
||||
|
||||
<service id="maker.auto_command.abstract" class="Symfony\Bundle\MakerBundle\Command\MakerCommand" abstract="true">
|
||||
<argument /> <!-- maker -->
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
<argument type="service" id="maker.generator" />
|
||||
<argument type="service" id="maker.template_linter" />
|
||||
</service>
|
||||
|
||||
<service id="maker.generator" class="Symfony\Bundle\MakerBundle\Generator">
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
<argument /> <!-- root namespace -->
|
||||
<argument>null</argument> <!-- PhpCompatUtil -->
|
||||
<argument type="service" id="maker.template_component_generator" />
|
||||
</service>
|
||||
|
||||
<service id="maker.entity_class_generator" class="Symfony\Bundle\MakerBundle\Doctrine\EntityClassGenerator">
|
||||
<argument type="service" id="maker.generator" />
|
||||
<argument type="service" id="maker.doctrine_helper" />
|
||||
</service>
|
||||
|
||||
<service id="maker.user_class_builder" class="Symfony\Bundle\MakerBundle\Security\UserClassBuilder" />
|
||||
|
||||
<service id="maker.security_config_updater" class="Symfony\Bundle\MakerBundle\Security\SecurityConfigUpdater" />
|
||||
|
||||
<service id="maker.renderer.form_type_renderer" class="Symfony\Bundle\MakerBundle\Renderer\FormTypeRenderer">
|
||||
<argument type="service" id="maker.generator" />
|
||||
</service>
|
||||
|
||||
<service id="maker.security_controller_builder" class="Symfony\Bundle\MakerBundle\Security\SecurityControllerBuilder">
|
||||
</service>
|
||||
|
||||
<service id="maker.php_compat_util" class="Symfony\Bundle\MakerBundle\Util\PhpCompatUtil">
|
||||
<argument type="service" id="maker.file_manager" />
|
||||
</service>
|
||||
|
||||
<service id="maker.template_component_generator" class="Symfony\Bundle\MakerBundle\Util\TemplateComponentGenerator">
|
||||
<argument /> <!-- generate_final_classes -->
|
||||
<argument /> <!-- generate_final_entities -->
|
||||
<argument /> <!-- root_namespace -->
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
166
vendor/symfony/maker-bundle/docs/index.rst
vendored
Normal file
166
vendor/symfony/maker-bundle/docs/index.rst
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
The Symfony MakerBundle
|
||||
=======================
|
||||
|
||||
Symfony Maker helps you create empty commands, controllers, form classes,
|
||||
tests and more so you can forget about writing boilerplate code. This bundle
|
||||
assumes you're using a standard Symfony directory structure, but many
|
||||
commands can generate code into any application.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Run this command to install and enable this bundle in your application:
|
||||
|
||||
.. code-block:: terminal
|
||||
|
||||
$ composer require --dev symfony/maker-bundle
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
This bundle provides several commands under the ``make:`` namespace. List them
|
||||
all executing this command:
|
||||
|
||||
.. code-block:: terminal
|
||||
|
||||
$ php bin/console list make
|
||||
|
||||
make:command Creates a new console command class
|
||||
make:controller Creates a new controller class
|
||||
make:entity Creates a new Doctrine entity class
|
||||
|
||||
[...]
|
||||
|
||||
make:validator Creates a new validator and constraint class
|
||||
make:voter Creates a new security voter class
|
||||
|
||||
The names of the commands are self-explanatory, but some of them include
|
||||
optional arguments and options. Check them out with the ``--help`` option:
|
||||
|
||||
.. code-block:: terminal
|
||||
|
||||
$ php bin/console make:controller --help
|
||||
|
||||
.. caution::
|
||||
|
||||
``make:entity`` requires ``doctrine/orm`` to be installed and configured. This maker support only ORM, not ODM.
|
||||
|
||||
Linting Generated Code
|
||||
______________________
|
||||
|
||||
MakerBundle uses php-cs-fixer to enforce coding standards when generating ``.php``
|
||||
files. When running a ``make`` command, MakerBundle will use a ``php-cs-fixer``
|
||||
version and configuration that is packaged with this bundle.
|
||||
|
||||
You can explicitly set a custom path to a php-cs-fixer binary and/or configuration
|
||||
file by their respective environment variables:
|
||||
|
||||
- ``MAKER_PHP_CS_FIXER_BINARY_PATH`` e.g. tools/vendor/bin/php-cs-fixer
|
||||
- ``MAKER_PHP_CS_FIXER_CONFIG_PATH`` e.g. .php-cs-fixer.config.php
|
||||
|
||||
|
||||
.. tip::
|
||||
|
||||
Is PHP-CS-Fixer installed globally? To avoid needing to set these in every
|
||||
project, you can instead set these on your operating system.
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
This bundle doesn't require any configuration. But, you *can* override the default
|
||||
configuration:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# config/packages/maker.yaml
|
||||
when@dev:
|
||||
maker:
|
||||
root_namespace: 'App'
|
||||
generate_final_classes: true
|
||||
generate_final_entities: false
|
||||
|
||||
root_namespace
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
**type**: ``string`` **default**: ``App``
|
||||
|
||||
The root namespace used when generating all of your classes
|
||||
(e.g. ``App\Entity\Article``, ``App\Command\MyCommand``, etc). Changing
|
||||
this to ``Acme`` would cause MakerBundle to create new classes like
|
||||
(e.g. ``Acme\Entity\Article``, ``Acme\Command\MyCommand``, etc).
|
||||
|
||||
generate_final_classes
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**type**: ``boolean`` **default**: ``true``
|
||||
|
||||
By default, MakerBundle will generate all of your classes with the
|
||||
``final`` PHP keyword except for doctrine entities. Set this to ``false``
|
||||
to override this behavior for all maker commands.
|
||||
|
||||
See https://www.php.net/manual/en/language.oop5.final.php
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
final class MyVoter
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
.. versionadded:: 1.61
|
||||
|
||||
``generate_final_classes`` was introduced in MakerBundle 1.61
|
||||
|
||||
|
||||
generate_final_entities
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**type**: ``boolean`` **default**: ``false``
|
||||
|
||||
By default, MakerBundle will not generate any of your doctrine entity
|
||||
classes with the ``final`` PHP keyword. Set this to ``true``
|
||||
to override this behavior for all maker commands that create
|
||||
entities.
|
||||
|
||||
See https://www.php.net/manual/en/language.oop5.final.php
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
#[ORM\Entity(repositoryClass: TaskRepository::class)]
|
||||
final class Task extends AbstractEntity
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
.. versionadded:: 1.61
|
||||
|
||||
``generate_final_entities`` was introduced in MakerBundle 1.61.
|
||||
|
||||
Creating your Own Makers
|
||||
------------------------
|
||||
|
||||
In case your applications need to generate custom boilerplate code, you can
|
||||
create your own ``make:...`` command reusing the tools provided by this bundle.
|
||||
To do that, you should create a class that extends
|
||||
`AbstractMaker`_ in your ``src/Maker/``
|
||||
directory. And this is really it!
|
||||
|
||||
For examples of how to complete your new maker command, see the `core maker commands`_.
|
||||
Make sure your class is registered as a service and tagged with ``maker.command``.
|
||||
If you're using the standard Symfony ``services.yaml`` configuration, this
|
||||
will be done automatically.
|
||||
|
||||
Overriding the Generated Code
|
||||
-----------------------------
|
||||
|
||||
Generated code can never be perfect for everyone. The MakerBundle tries to balance
|
||||
adding "extension points" with keeping the library simple so that existing commands
|
||||
can be improved and new commands can be added.
|
||||
|
||||
For that reason, in general, the generated code cannot be modified. In many cases,
|
||||
adding your *own* maker command is so easy, that we recommend that. However, if there
|
||||
is some extension point that you'd like, please open an issue so we can discuss!
|
||||
|
||||
.. _`AbstractMaker`: https://github.com/symfony/maker-bundle/blob/main/src/Maker/AbstractMaker.php
|
||||
.. _`core maker commands`: https://github.com/symfony/maker-bundle/tree/main/src/Maker
|
||||
26
vendor/symfony/maker-bundle/phpstan.dist.neon
vendored
Normal file
26
vendor/symfony/maker-bundle/phpstan.dist.neon
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
parameters:
|
||||
level: 6
|
||||
bootstrapFiles:
|
||||
- vendor/autoload.php
|
||||
- tools/phpstan/includes/vendor/autoload.php
|
||||
paths:
|
||||
- src/Maker
|
||||
- tests/Command
|
||||
- tests/Docker
|
||||
- tests/Maker
|
||||
excludePaths:
|
||||
- tests/Doctrine/fixtures
|
||||
- tests/fixtures
|
||||
- tests/Security/fixtures
|
||||
- tests/Security/yaml_fixtures
|
||||
- tests/tmp
|
||||
- tests/Util/fixtures
|
||||
- tests/Util/yaml_fixtures
|
||||
ignoreErrors:
|
||||
-
|
||||
message: '#Property .+phpCompatUtil is never read, only written\.#'
|
||||
path: src/Maker/*
|
||||
|
||||
-
|
||||
message: '#Class Symfony\\Bundle\\MakerBundle\\Maker\\LegacyApiTestCase not found#'
|
||||
path: src/Maker/*
|
||||
24
vendor/symfony/maker-bundle/src/ApplicationAwareMakerInterface.php
vendored
Normal file
24
vendor/symfony/maker-bundle/src/ApplicationAwareMakerInterface.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle;
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
/**
|
||||
* Implement this interface if your Maker needs access to the Application.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*/
|
||||
interface ApplicationAwareMakerInterface
|
||||
{
|
||||
public function setApplication(Application $application);
|
||||
}
|
||||
139
vendor/symfony/maker-bundle/src/Command/MakerCommand.php
vendored
Normal file
139
vendor/symfony/maker-bundle/src/Command/MakerCommand.php
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Command;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ApplicationAwareMakerInterface;
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
|
||||
use Symfony\Bundle\MakerBundle\FileManager;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\MakerInterface;
|
||||
use Symfony\Bundle\MakerBundle\Util\TemplateLinter;
|
||||
use Symfony\Bundle\MakerBundle\Validator;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Used as the Command class for the makers.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class MakerCommand extends Command
|
||||
{
|
||||
private InputConfiguration $inputConfig;
|
||||
private ConsoleStyle $io;
|
||||
private bool $checkDependencies = true;
|
||||
|
||||
public function __construct(
|
||||
private MakerInterface $maker,
|
||||
private FileManager $fileManager,
|
||||
private Generator $generator,
|
||||
private TemplateLinter $linter,
|
||||
) {
|
||||
$this->inputConfig = new InputConfiguration();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->maker->configureCommand($this, $this->inputConfig);
|
||||
}
|
||||
|
||||
protected function initialize(InputInterface $input, OutputInterface $output): void
|
||||
{
|
||||
$this->io = new ConsoleStyle($input, $output);
|
||||
|
||||
if (!$input->isInteractive()) {
|
||||
$this->io->warning(\sprintf('"%s" is not meant to be run in non-interactive mode.', $this->getName()));
|
||||
}
|
||||
|
||||
$this->fileManager->setIO($this->io);
|
||||
|
||||
if ($this->checkDependencies) {
|
||||
$dependencies = new DependencyBuilder();
|
||||
$this->maker->configureDependencies($dependencies, $input);
|
||||
|
||||
if ($missingPackagesMessage = $dependencies->getMissingPackagesMessage($this->getName())) {
|
||||
throw new RuntimeCommandException($missingPackagesMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function interact(InputInterface $input, OutputInterface $output): void
|
||||
{
|
||||
if (!$this->fileManager->isNamespaceConfiguredToAutoload($this->generator->getRootNamespace())) {
|
||||
$this->io->note([
|
||||
\sprintf('It looks like your app may be using a namespace other than "%s".', $this->generator->getRootNamespace()),
|
||||
'To configure this and make your life easier, see: https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html#configuration',
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($this->getDefinition()->getArguments() as $argument) {
|
||||
if ($input->getArgument($argument->getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\in_array($argument->getName(), $this->inputConfig->getNonInteractiveArguments(), true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $this->io->ask($argument->getDescription(), $argument->getDefault(), Validator::notBlank(...));
|
||||
$input->setArgument($argument->getName(), $value);
|
||||
}
|
||||
|
||||
$this->maker->interact($input, $this->io, $this);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
if ($output->isVerbose()) {
|
||||
$this->linter->writeLinterMessage($output);
|
||||
}
|
||||
|
||||
$this->maker->generate($input, $this->io, $this->generator);
|
||||
|
||||
// sanity check for custom makers
|
||||
if ($this->generator->hasPendingOperations()) {
|
||||
throw new \LogicException('Make sure to call the writeChanges() method on the generator.');
|
||||
}
|
||||
|
||||
$this->linter->lintFiles($this->generator->getGeneratedFiles());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function setApplication(?Application $application = null): void
|
||||
{
|
||||
parent::setApplication($application);
|
||||
|
||||
if ($this->maker instanceof ApplicationAwareMakerInterface) {
|
||||
if (null === $application) {
|
||||
throw new \RuntimeException('Application cannot be null.');
|
||||
}
|
||||
|
||||
$this->maker->setApplication($application);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Used for testing commands
|
||||
*/
|
||||
public function setCheckDependencies(bool $checkDeps): void
|
||||
{
|
||||
$this->checkDependencies = $checkDeps;
|
||||
}
|
||||
}
|
||||
136
vendor/symfony/maker-bundle/src/Console/MigrationDiffFilteredOutput.php
vendored
Normal file
136
vendor/symfony/maker-bundle/src/Console/MigrationDiffFilteredOutput.php
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Console;
|
||||
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class MigrationDiffFilteredOutput implements OutputInterface
|
||||
{
|
||||
private string $buffer = '';
|
||||
private bool $previousLineWasRemoved = false;
|
||||
|
||||
public function __construct(
|
||||
private OutputInterface $output,
|
||||
) {
|
||||
}
|
||||
|
||||
public function write($messages, bool $newline = false, $options = 0): void
|
||||
{
|
||||
$messages = $this->filterMessages($messages, $newline);
|
||||
|
||||
$this->output->write($messages, $newline, $options);
|
||||
}
|
||||
|
||||
public function writeln($messages, int $options = 0): void
|
||||
{
|
||||
$messages = $this->filterMessages($messages, true);
|
||||
|
||||
$this->output->writeln($messages, $options);
|
||||
}
|
||||
|
||||
public function setVerbosity(int $level): void
|
||||
{
|
||||
$this->output->setVerbosity($level);
|
||||
}
|
||||
|
||||
public function setDecorated(bool $decorated): void
|
||||
{
|
||||
$this->output->setDecorated($decorated);
|
||||
}
|
||||
|
||||
public function getVerbosity(): int
|
||||
{
|
||||
return $this->output->getVerbosity();
|
||||
}
|
||||
|
||||
public function isQuiet(): bool
|
||||
{
|
||||
return $this->output->isQuiet();
|
||||
}
|
||||
|
||||
public function isVerbose(): bool
|
||||
{
|
||||
return $this->output->isVerbose();
|
||||
}
|
||||
|
||||
public function isVeryVerbose(): bool
|
||||
{
|
||||
return $this->output->isVeryVerbose();
|
||||
}
|
||||
|
||||
public function isDebug(): bool
|
||||
{
|
||||
return $this->output->isDebug();
|
||||
}
|
||||
|
||||
public function isDecorated(): bool
|
||||
{
|
||||
return $this->output->isDecorated();
|
||||
}
|
||||
|
||||
public function setFormatter(OutputFormatterInterface $formatter): void
|
||||
{
|
||||
$this->output->setFormatter($formatter);
|
||||
}
|
||||
|
||||
public function getFormatter(): OutputFormatterInterface
|
||||
{
|
||||
return $this->output->getFormatter();
|
||||
}
|
||||
|
||||
public function fetch(): string
|
||||
{
|
||||
return $this->buffer;
|
||||
}
|
||||
|
||||
private function filterMessages($messages, bool $newLine)
|
||||
{
|
||||
if (!is_iterable($messages)) {
|
||||
$messages = [$messages];
|
||||
}
|
||||
|
||||
$hiddenPhrases = [
|
||||
'Generated new migration class',
|
||||
'To run just this migration',
|
||||
'To revert the migration you',
|
||||
];
|
||||
|
||||
foreach ($messages as $key => $message) {
|
||||
$this->buffer .= $message;
|
||||
|
||||
if ($newLine) {
|
||||
$this->buffer .= \PHP_EOL;
|
||||
}
|
||||
|
||||
if ($this->previousLineWasRemoved && !trim($message)) {
|
||||
// hide a blank line after a filtered line
|
||||
unset($messages[$key]);
|
||||
$this->previousLineWasRemoved = false;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->previousLineWasRemoved = false;
|
||||
foreach ($hiddenPhrases as $hiddenPhrase) {
|
||||
if (str_contains($message, $hiddenPhrase)) {
|
||||
$this->previousLineWasRemoved = true;
|
||||
unset($messages[$key]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($messages);
|
||||
}
|
||||
}
|
||||
45
vendor/symfony/maker-bundle/src/ConsoleStyle.php
vendored
Normal file
45
vendor/symfony/maker-bundle/src/ConsoleStyle.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*/
|
||||
final class ConsoleStyle extends SymfonyStyle
|
||||
{
|
||||
public function __construct(
|
||||
InputInterface $input,
|
||||
private OutputInterface $output,
|
||||
) {
|
||||
parent::__construct($input, $output);
|
||||
}
|
||||
|
||||
public function success($message): void
|
||||
{
|
||||
$this->writeln('<fg=green;options=bold,underscore>OK</> '.$message);
|
||||
}
|
||||
|
||||
public function comment($message): void
|
||||
{
|
||||
$this->text($message);
|
||||
}
|
||||
|
||||
public function getOutput(): OutputInterface
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
}
|
||||
144
vendor/symfony/maker-bundle/src/DependencyBuilder.php
vendored
Normal file
144
vendor/symfony/maker-bundle/src/DependencyBuilder.php
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle;
|
||||
|
||||
final class DependencyBuilder
|
||||
{
|
||||
private array $dependencies = [];
|
||||
private array $devDependencies = [];
|
||||
|
||||
/**
|
||||
* Add a dependency that will be reported if the given class is missing.
|
||||
*
|
||||
* If the dependency is *optional*, then it will only be reported to
|
||||
* the user if other required dependencies are missing. An example
|
||||
* is the "validator" when trying to work with forms.
|
||||
*/
|
||||
public function addClassDependency(string $class, string $package, bool $required = true, bool $devDependency = false): void
|
||||
{
|
||||
if ($devDependency) {
|
||||
$this->devDependencies[] = [
|
||||
'class' => $class,
|
||||
'name' => $package,
|
||||
'required' => $required,
|
||||
];
|
||||
} else {
|
||||
$this->dependencies[] = [
|
||||
'class' => $class,
|
||||
'name' => $package,
|
||||
'required' => $required,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public function requirePHP71(): void
|
||||
{
|
||||
trigger_deprecation('symfony/maker-bundle', 'v1.44.0', 'requirePHP71() is deprecated and will be removed in a future version.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getMissingDependencies(): array
|
||||
{
|
||||
return $this->calculateMissingDependencies($this->dependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getMissingDevDependencies(): array
|
||||
{
|
||||
return $this->calculateMissingDependencies($this->devDependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getAllRequiredDependencies(): array
|
||||
{
|
||||
return $this->getRequiredDependencyNames($this->dependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getAllRequiredDevDependencies(): array
|
||||
{
|
||||
return $this->getRequiredDependencyNames($this->devDependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getMissingPackagesMessage(string $commandName, $message = null): string
|
||||
{
|
||||
$packages = $this->getMissingDependencies();
|
||||
$packagesDev = $this->getMissingDevDependencies();
|
||||
|
||||
if (empty($packages) && empty($packagesDev)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$packagesCount = \count($packages) + \count($packagesDev);
|
||||
|
||||
$message = \sprintf(
|
||||
"Missing package%s: %s, run:\n",
|
||||
$packagesCount > 1 ? 's' : '',
|
||||
$message ?: \sprintf('to use the %s command', $commandName)
|
||||
);
|
||||
|
||||
if (!empty($packages)) {
|
||||
$message .= \sprintf("\ncomposer require %s", implode(' ', $packages));
|
||||
}
|
||||
|
||||
if (!empty($packagesDev)) {
|
||||
$message .= \sprintf("\ncomposer require %s --dev", implode(' ', $packagesDev));
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
private function getRequiredDependencyNames(array $dependencies): array
|
||||
{
|
||||
$packages = [];
|
||||
foreach ($dependencies as $package) {
|
||||
if (!$package['required']) {
|
||||
continue;
|
||||
}
|
||||
$packages[] = $package['name'];
|
||||
}
|
||||
|
||||
return array_unique($packages);
|
||||
}
|
||||
|
||||
private function calculateMissingDependencies(array $dependencies): array
|
||||
{
|
||||
$missingPackages = [];
|
||||
$missingOptionalPackages = [];
|
||||
foreach ($dependencies as $package) {
|
||||
if (class_exists($package['class']) || interface_exists($package['class']) || trait_exists($package['class'])) {
|
||||
continue;
|
||||
}
|
||||
if (true === $package['required']) {
|
||||
$missingPackages[] = $package['name'];
|
||||
} else {
|
||||
$missingOptionalPackages[] = $package['name'];
|
||||
}
|
||||
}
|
||||
if (empty($missingPackages)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_unique([...$missingPackages, ...$missingOptionalPackages]);
|
||||
}
|
||||
}
|
||||
77
vendor/symfony/maker-bundle/src/DependencyInjection/CompilerPass/MakeCommandRegistrationPass.php
vendored
Normal file
77
vendor/symfony/maker-bundle/src/DependencyInjection/CompilerPass/MakeCommandRegistrationPass.php
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\DependencyInjection\CompilerPass;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\Command\MakerCommand;
|
||||
use Symfony\Bundle\MakerBundle\MakerInterface;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Component\Console\Command\LazyCommand;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
class MakeCommandRegistrationPass implements CompilerPassInterface
|
||||
{
|
||||
public const MAKER_TAG = 'maker.command';
|
||||
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
foreach ($container->findTaggedServiceIds(self::MAKER_TAG) as $id => $tags) {
|
||||
$def = $container->getDefinition($id);
|
||||
if ($def->isDeprecated()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$class = $container->getParameterBag()->resolveValue($def->getClass());
|
||||
if (!is_subclass_of($class, MakerInterface::class)) {
|
||||
throw new InvalidArgumentException(\sprintf('Service "%s" must implement interface "%s".', $id, MakerInterface::class));
|
||||
}
|
||||
|
||||
$commandDefinition = new ChildDefinition('maker.auto_command.abstract');
|
||||
$commandDefinition->setClass(MakerCommand::class);
|
||||
$commandDefinition->replaceArgument(0, new Reference($id));
|
||||
|
||||
$tagAttributes = ['command' => $class::getCommandName()];
|
||||
|
||||
if (!method_exists($class, 'getCommandDescription')) {
|
||||
// no-op
|
||||
} elseif (class_exists(LazyCommand::class)) {
|
||||
$tagAttributes['description'] = $class::getCommandDescription();
|
||||
} else {
|
||||
$commandDefinition->addMethodCall('setDescription', [$class::getCommandDescription()]);
|
||||
}
|
||||
|
||||
$commandDefinition->addTag('console.command', $tagAttributes);
|
||||
|
||||
/*
|
||||
* @deprecated remove this block when removing make:unit-test and make:functional-test
|
||||
*/
|
||||
if (method_exists($class, 'getCommandAliases')) {
|
||||
foreach ($class::getCommandAliases() as $alias) {
|
||||
$commandDefinition->addTag('console.command', ['command' => $alias, 'description' => 'Deprecated alias of "make:test"']);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @deprecated remove this block when removing make:subscriber
|
||||
*/
|
||||
if (method_exists($class, 'getCommandAlias')) {
|
||||
$alias = $class::getCommandAlias();
|
||||
$commandDefinition->addTag('console.command', ['command' => $alias, 'description' => 'Deprecated alias of "make:listener"']);
|
||||
}
|
||||
|
||||
$container->setDefinition(\sprintf('maker.auto_command.%s', Str::asTwigVariable($class::getCommandName())), $commandDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
vendor/symfony/maker-bundle/src/DependencyInjection/CompilerPass/RemoveMissingParametersPass.php
vendored
Normal file
31
vendor/symfony/maker-bundle/src/DependencyInjection/CompilerPass/RemoveMissingParametersPass.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\DependencyInjection\CompilerPass;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* Removes injected parameter arguments if they don't exist in this app.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@symfonycasts.com>
|
||||
*/
|
||||
class RemoveMissingParametersPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
if (!$container->hasParameter('twig.default_path')) {
|
||||
$container->getDefinition('maker.file_manager')
|
||||
->replaceArgument(4, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\DependencyInjection\CompilerPass;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
class SetDoctrineAnnotatedPrefixesPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
$annotatedPrefixes = null;
|
||||
|
||||
foreach ($container->findTaggedServiceIds('doctrine.orm.configuration') as $id => $tags) {
|
||||
$metadataDriverImpl = null;
|
||||
foreach ($container->getDefinition($id)->getMethodCalls() as [$method, $arguments]) {
|
||||
if ('setMetadataDriverImpl' === $method) {
|
||||
$metadataDriverImpl = $container->getDefinition($arguments[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $metadataDriverImpl || !preg_match('/^doctrine\.orm\.(.+)_configuration$/D', $id, $m)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$managerName = $m[1];
|
||||
$methodCalls = $metadataDriverImpl->getMethodCalls();
|
||||
|
||||
foreach ($methodCalls as $i => [$method, $arguments]) {
|
||||
if ('addDriver' !== $method) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($arguments[0] instanceof Definition) {
|
||||
$class = $arguments[0]->getClass();
|
||||
|
||||
$id = \sprintf('.%d_doctrine_metadata_driver~%s', $i, ContainerBuilder::hash($arguments));
|
||||
$container->setDefinition($id, $arguments[0]);
|
||||
$arguments[0] = new Reference($id);
|
||||
$methodCalls[$i] = [$method, $arguments];
|
||||
}
|
||||
|
||||
$annotatedPrefixes[$managerName][] = [
|
||||
$arguments[1],
|
||||
new Reference($arguments[0]),
|
||||
];
|
||||
}
|
||||
|
||||
$metadataDriverImpl->setMethodCalls($methodCalls);
|
||||
}
|
||||
|
||||
if (null !== $annotatedPrefixes) {
|
||||
$container->getDefinition('maker.doctrine_helper')->setArgument(2, $annotatedPrefixes);
|
||||
}
|
||||
}
|
||||
}
|
||||
106
vendor/symfony/maker-bundle/src/Docker/DockerDatabaseServices.php
vendored
Normal file
106
vendor/symfony/maker-bundle/src/Docker/DockerDatabaseServices.php
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Docker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
|
||||
|
||||
/**
|
||||
* @author Jesse Rushlow <jr@rushlow.dev>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class DockerDatabaseServices
|
||||
{
|
||||
/**
|
||||
* @throws RuntimeCommandException
|
||||
*/
|
||||
public static function getDatabaseSkeleton(string $name, string $version): array
|
||||
{
|
||||
switch ($name) {
|
||||
case 'mariadb':
|
||||
return [
|
||||
'image' => \sprintf('mariadb:%s', $version),
|
||||
'environment' => [
|
||||
'MYSQL_ROOT_PASSWORD' => 'password',
|
||||
'MYSQL_DATABASE' => 'main',
|
||||
],
|
||||
];
|
||||
case 'mysql':
|
||||
return [
|
||||
'image' => \sprintf('mysql:%s', $version),
|
||||
'environment' => [
|
||||
'MYSQL_ROOT_PASSWORD' => 'password',
|
||||
'MYSQL_DATABASE' => 'main',
|
||||
],
|
||||
];
|
||||
case 'postgres':
|
||||
return [
|
||||
'image' => \sprintf('postgres:%s', $version),
|
||||
'environment' => [
|
||||
'POSTGRES_PASSWORD' => 'main',
|
||||
'POSTGRES_USER' => 'main',
|
||||
'POSTGRES_DB' => 'main',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
self::throwInvalidDatabase($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeCommandException
|
||||
*/
|
||||
public static function getDefaultPorts(string $name): array
|
||||
{
|
||||
switch ($name) {
|
||||
case 'mariadb':
|
||||
case 'mysql':
|
||||
return ['3306'];
|
||||
case 'postgres':
|
||||
return ['5432'];
|
||||
}
|
||||
|
||||
self::throwInvalidDatabase($name);
|
||||
}
|
||||
|
||||
public static function getSuggestedServiceVersion(string $name): string
|
||||
{
|
||||
if ('postgres' === $name) {
|
||||
return 'alpine';
|
||||
}
|
||||
|
||||
return 'latest';
|
||||
}
|
||||
|
||||
public static function getMissingExtensionName(string $name): ?string
|
||||
{
|
||||
$driver = match ($name) {
|
||||
'mariadb', 'mysql' => 'mysql',
|
||||
'postgres' => 'pgsql',
|
||||
default => self::throwInvalidDatabase($name),
|
||||
};
|
||||
|
||||
if (!\in_array($driver, \PDO::getAvailableDrivers(), true)) {
|
||||
return $driver;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeCommandException
|
||||
*/
|
||||
private static function throwInvalidDatabase(string $name): never
|
||||
{
|
||||
throw new RuntimeCommandException(\sprintf('%s is not a valid / supported docker database type.', $name));
|
||||
}
|
||||
}
|
||||
32
vendor/symfony/maker-bundle/src/Doctrine/BaseCollectionRelation.php
vendored
Normal file
32
vendor/symfony/maker-bundle/src/Doctrine/BaseCollectionRelation.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Doctrine;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
abstract class BaseCollectionRelation extends BaseRelation
|
||||
{
|
||||
abstract public function getTargetSetterMethodName(): string;
|
||||
|
||||
public function getAdderMethodName(): string
|
||||
{
|
||||
return 'add'.Str::asCamelCase(Str::pluralCamelCaseToSingular($this->getPropertyName()));
|
||||
}
|
||||
|
||||
public function getRemoverMethodName(): string
|
||||
{
|
||||
return 'remove'.Str::asCamelCase(Str::pluralCamelCaseToSingular($this->getPropertyName()));
|
||||
}
|
||||
}
|
||||
88
vendor/symfony/maker-bundle/src/Doctrine/BaseRelation.php
vendored
Normal file
88
vendor/symfony/maker-bundle/src/Doctrine/BaseRelation.php
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Doctrine;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
abstract class BaseRelation
|
||||
{
|
||||
public function __construct(
|
||||
private string $propertyName,
|
||||
private string $targetClassName,
|
||||
private ?string $targetPropertyName = null,
|
||||
private bool $isSelfReferencing = false,
|
||||
private bool $mapInverseRelation = true,
|
||||
private bool $avoidSetter = false,
|
||||
private bool $isCustomReturnTypeNullable = false,
|
||||
private ?string $customReturnType = null,
|
||||
private bool $isOwning = false,
|
||||
private bool $orphanRemoval = false,
|
||||
private bool $isNullable = false,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getPropertyName(): string
|
||||
{
|
||||
return $this->propertyName;
|
||||
}
|
||||
|
||||
public function getTargetClassName(): string
|
||||
{
|
||||
return $this->targetClassName;
|
||||
}
|
||||
|
||||
public function getTargetPropertyName(): ?string
|
||||
{
|
||||
return $this->targetPropertyName;
|
||||
}
|
||||
|
||||
public function isSelfReferencing(): bool
|
||||
{
|
||||
return $this->isSelfReferencing;
|
||||
}
|
||||
|
||||
public function getMapInverseRelation(): bool
|
||||
{
|
||||
return $this->mapInverseRelation;
|
||||
}
|
||||
|
||||
public function shouldAvoidSetter(): bool
|
||||
{
|
||||
return $this->avoidSetter;
|
||||
}
|
||||
|
||||
public function getCustomReturnType(): ?string
|
||||
{
|
||||
return $this->customReturnType;
|
||||
}
|
||||
|
||||
public function isCustomReturnTypeNullable(): bool
|
||||
{
|
||||
return $this->isCustomReturnTypeNullable;
|
||||
}
|
||||
|
||||
public function isOwning(): bool
|
||||
{
|
||||
return $this->isOwning;
|
||||
}
|
||||
|
||||
public function getOrphanRemoval(): bool
|
||||
{
|
||||
return $this->orphanRemoval;
|
||||
}
|
||||
|
||||
public function isNullable(): bool
|
||||
{
|
||||
return $this->isNullable;
|
||||
}
|
||||
}
|
||||
368
vendor/symfony/maker-bundle/src/Doctrine/DoctrineHelper.php
vendored
Normal file
368
vendor/symfony/maker-bundle/src/Doctrine/DoctrineHelper.php
vendored
Normal file
@@ -0,0 +1,368 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Doctrine;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
|
||||
use Doctrine\ORM\Mapping\MappingException as ORMMappingException;
|
||||
use Doctrine\ORM\Mapping\NamingStrategy;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
|
||||
use Doctrine\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriverChain;
|
||||
use Doctrine\Persistence\Mapping\MappingException as PersistenceMappingException;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassNameDetails;
|
||||
use Symfony\Component\Uid\Ulid;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
* @author Sadicov Vladimir <sadikoff@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class DoctrineHelper
|
||||
{
|
||||
public function __construct(
|
||||
private string $entityNamespace,
|
||||
private ?ManagerRegistry $registry = null,
|
||||
private ?array $mappingDriversByPrefix = null,
|
||||
) {
|
||||
$this->entityNamespace = trim($entityNamespace, '\\');
|
||||
}
|
||||
|
||||
public function getRegistry(): ManagerRegistry
|
||||
{
|
||||
// this should never happen: we will have checked for the
|
||||
// DoctrineBundle dependency before calling this
|
||||
if (null === $this->registry) {
|
||||
throw new \Exception('Somehow the doctrine service is missing. Is DoctrineBundle installed?');
|
||||
}
|
||||
|
||||
return $this->registry;
|
||||
}
|
||||
|
||||
private function isDoctrineInstalled(): bool
|
||||
{
|
||||
return null !== $this->registry;
|
||||
}
|
||||
|
||||
public function getEntityNamespace(): string
|
||||
{
|
||||
return $this->entityNamespace;
|
||||
}
|
||||
|
||||
public function doesClassUseDriver(string $className, string $driverClass): bool
|
||||
{
|
||||
try {
|
||||
/** @var EntityManagerInterface $em */
|
||||
$em = $this->getRegistry()->getManagerForClass($className);
|
||||
} catch (\ReflectionException) {
|
||||
// this exception will be thrown by the registry if the class isn't created yet.
|
||||
// an example case is the "make:entity" command, which needs to know which driver is used for the class to determine
|
||||
// if the class should be generated with attributes or annotations. If this exception is thrown, we will check based on the
|
||||
// namespaces for the given $className and compare it with the doctrine configuration to get the correct MappingDriver.
|
||||
|
||||
// extract the new class's namespace from the full $className to check the namespace of the new class against the doctrine configuration.
|
||||
$classNameComponents = explode('\\', $className);
|
||||
if (1 < \count($classNameComponents)) {
|
||||
array_pop($classNameComponents);
|
||||
}
|
||||
$classNamespace = implode('\\', $classNameComponents);
|
||||
|
||||
return $this->isInstanceOf($this->getMappingDriverForNamespace($classNamespace), $driverClass);
|
||||
}
|
||||
|
||||
if (null === $em) {
|
||||
throw new \InvalidArgumentException(\sprintf('Cannot find the entity manager for class "%s". Ensure entity uses attribute mapping.', $className));
|
||||
}
|
||||
|
||||
if (null === $this->mappingDriversByPrefix) {
|
||||
// doctrine-bundle <= 2.2
|
||||
$metadataDriver = $em->getConfiguration()->getMetadataDriverImpl();
|
||||
|
||||
if (!$this->isInstanceOf($metadataDriver, MappingDriverChain::class)) {
|
||||
return $this->isInstanceOf($metadataDriver, $driverClass);
|
||||
}
|
||||
|
||||
foreach ($metadataDriver->getDrivers() as $namespace => $driver) {
|
||||
if (str_starts_with($className, $namespace)) {
|
||||
return $this->isInstanceOf($driver, $driverClass);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->isInstanceOf($metadataDriver->getDefaultDriver(), $driverClass);
|
||||
}
|
||||
|
||||
$managerName = array_search($em, $this->getRegistry()->getManagers(), true);
|
||||
|
||||
foreach ($this->mappingDriversByPrefix[$managerName] as [$prefix, $prefixDriver]) {
|
||||
if (str_starts_with($className, $prefix)) {
|
||||
return $this->isInstanceOf($prefixDriver, $driverClass);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function doesClassUsesAttributes(string $className): bool
|
||||
{
|
||||
return $this->doesClassUseDriver($className, AttributeDriver::class);
|
||||
}
|
||||
|
||||
public function isDoctrineSupportingAttributes(): bool
|
||||
{
|
||||
return $this->isDoctrineInstalled();
|
||||
}
|
||||
|
||||
public function getEntitiesForAutocomplete(): array
|
||||
{
|
||||
$entities = [];
|
||||
|
||||
if ($this->isDoctrineInstalled()) {
|
||||
$allMetadata = $this->getMetadata();
|
||||
|
||||
foreach (array_keys($allMetadata) as $classname) {
|
||||
$entityClassDetails = new ClassNameDetails($classname, $this->entityNamespace);
|
||||
$entities[] = $entityClassDetails->getRelativeName();
|
||||
}
|
||||
}
|
||||
|
||||
sort($entities);
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
public function getMetadata(?string $classOrNamespace = null, bool $disconnected = false): array|ClassMetadata
|
||||
{
|
||||
// Invalidating the cached AttributeDriver::$classNames to find new Entity classes
|
||||
foreach ($this->mappingDriversByPrefix ?? [] as $managerName => $prefixes) {
|
||||
foreach ($prefixes as [$prefix, $attributeDriver]) {
|
||||
if ($attributeDriver instanceof AttributeDriver) {
|
||||
$classNames = (new \ReflectionClass(AttributeDriver::class))->getProperty('classNames');
|
||||
|
||||
$classNames->setAccessible(true);
|
||||
$classNames->setValue($attributeDriver, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$metadata = [];
|
||||
|
||||
/** @var EntityManagerInterface $em */
|
||||
foreach ($this->getRegistry()->getManagers() as $em) {
|
||||
$cmf = $em->getMetadataFactory();
|
||||
|
||||
if ($disconnected) {
|
||||
try {
|
||||
$loaded = $cmf->getAllMetadata();
|
||||
} catch (ORMMappingException|PersistenceMappingException) {
|
||||
$loaded = $this->isInstanceOf($cmf, AbstractClassMetadataFactory::class) ? $cmf->getLoadedMetadata() : [];
|
||||
}
|
||||
|
||||
// Set the reflection service that was used in the now removed DisconnectedClassMetadataFactory::class
|
||||
$cmf->setReflectionService(new StaticReflectionService());
|
||||
|
||||
foreach ($loaded as $m) {
|
||||
$cmf->setMetadataFor($m->getName(), $m);
|
||||
}
|
||||
|
||||
if (null === $this->mappingDriversByPrefix) {
|
||||
// Invalidating the cached AttributeDriver::$classNames to find new Entity classes
|
||||
$metadataDriver = $em->getConfiguration()->getMetadataDriverImpl();
|
||||
|
||||
if ($this->isInstanceOf($metadataDriver, MappingDriverChain::class)) {
|
||||
foreach ($metadataDriver->getDrivers() as $driver) {
|
||||
if ($this->isInstanceOf($driver, AttributeDriver::class)) {
|
||||
$classNames->setValue($driver, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($cmf->getAllMetadata() as $m) {
|
||||
if (null === $classOrNamespace) {
|
||||
$metadata[$m->getName()] = $m;
|
||||
} else {
|
||||
if ($m->getName() === $classOrNamespace) {
|
||||
return $m;
|
||||
}
|
||||
|
||||
if (str_starts_with($m->getName(), $classOrNamespace)) {
|
||||
$metadata[$m->getName()] = $m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
public function createDoctrineDetails(string $entityClassName): ?EntityDetails
|
||||
{
|
||||
$metadata = $this->getMetadata($entityClassName);
|
||||
|
||||
if ($this->isInstanceOf($metadata, ClassMetadata::class)) {
|
||||
return new EntityDetails($metadata);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isClassAMappedEntity(string $className): bool
|
||||
{
|
||||
if (!$this->isDoctrineInstalled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) $this->getMetadata($className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the property-type will make the column type redundant.
|
||||
*
|
||||
* See ClassMetadataInfo::validateAndCompleteTypedFieldMapping()
|
||||
*/
|
||||
public static function canColumnTypeBeInferredByPropertyType(string $columnType, string $propertyType): bool
|
||||
{
|
||||
// todo: guessing on enum's could be added
|
||||
|
||||
return match ($propertyType) {
|
||||
'\\'.\DateInterval::class => Types::DATEINTERVAL === $columnType,
|
||||
'\\'.\DateTime::class => Types::DATETIME_MUTABLE === $columnType,
|
||||
'\\'.\DateTimeImmutable::class => Types::DATETIME_IMMUTABLE === $columnType,
|
||||
'array' => Types::JSON === $columnType,
|
||||
'bool' => Types::BOOLEAN === $columnType,
|
||||
'float' => Types::FLOAT === $columnType,
|
||||
'int' => Types::INTEGER === $columnType,
|
||||
'string' => Types::STRING === $columnType,
|
||||
default => false,
|
||||
};
|
||||
}
|
||||
|
||||
public static function getPropertyTypeForColumn(string $columnType): ?string
|
||||
{
|
||||
$propertyType = match ($columnType) {
|
||||
Types::STRING, Types::TEXT, Types::GUID, Types::BIGINT, Types::DECIMAL => 'string',
|
||||
'array', Types::SIMPLE_ARRAY, Types::JSON => 'array',
|
||||
Types::BOOLEAN => 'bool',
|
||||
Types::INTEGER, Types::SMALLINT => 'int',
|
||||
Types::FLOAT => 'float',
|
||||
Types::DATETIME_MUTABLE, Types::DATETIMETZ_MUTABLE, Types::DATE_MUTABLE, Types::TIME_MUTABLE => '\\'.\DateTime::class,
|
||||
Types::DATETIME_IMMUTABLE, Types::DATETIMETZ_IMMUTABLE, Types::DATE_IMMUTABLE, Types::TIME_IMMUTABLE => '\\'.\DateTimeImmutable::class,
|
||||
Types::DATEINTERVAL => '\\'.\DateInterval::class,
|
||||
'object' => 'object',
|
||||
'uuid' => '\\'.Uuid::class,
|
||||
'ulid' => '\\'.Ulid::class,
|
||||
default => null,
|
||||
};
|
||||
|
||||
if (null !== $propertyType || !($registry = Type::getTypeRegistry())->has($columnType)) {
|
||||
return $propertyType;
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionClass(($registry->get($columnType))::class);
|
||||
|
||||
$returnType = $reflection->getMethod('convertToPHPValue')->getReturnType();
|
||||
|
||||
/*
|
||||
* we do not support union and intersection types
|
||||
*/
|
||||
if (!$returnType instanceof \ReflectionNamedType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $returnType->isBuiltin() ? $returnType->getName() : '\\'.$returnType->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the string "column type", this returns the "Types::STRING" constant.
|
||||
*
|
||||
* This is, effectively, a reverse lookup: given the final string, give us
|
||||
* the constant to be used in the generated code.
|
||||
*/
|
||||
public static function getTypeConstant(string $columnType): ?string
|
||||
{
|
||||
$reflection = new \ReflectionClass(Types::class);
|
||||
$constants = array_flip($reflection->getConstants());
|
||||
|
||||
if (!isset($constants[$columnType])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return \sprintf('Types::%s', $constants[$columnType]);
|
||||
}
|
||||
|
||||
private function isInstanceOf($object, string $class): bool
|
||||
{
|
||||
if (!\is_object($object)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $object instanceof $class;
|
||||
}
|
||||
|
||||
public function getPotentialTableName(string $className): string
|
||||
{
|
||||
$entityManager = $this->getRegistry()->getManager();
|
||||
|
||||
if (!$entityManager instanceof EntityManagerInterface) {
|
||||
throw new \RuntimeException('ObjectManager is not an EntityManagerInterface.');
|
||||
}
|
||||
|
||||
/** @var NamingStrategy $namingStrategy */
|
||||
$namingStrategy = $entityManager->getConfiguration()->getNamingStrategy();
|
||||
|
||||
return $namingStrategy->classToTableName($className);
|
||||
}
|
||||
|
||||
public function isKeyword(string $name): bool
|
||||
{
|
||||
/** @var Connection $connection */
|
||||
$connection = $this->getRegistry()->getConnection();
|
||||
|
||||
return $connection->getDatabasePlatform()->getReservedKeywordsList()->isKeyword($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* this method tries to find the correct MappingDriver for the given namespace/class
|
||||
* To determine which MappingDriver belongs to the class we check the prefixes configured in Doctrine and use the
|
||||
* prefix that has the closest match to the given $namespace.
|
||||
*
|
||||
* this helper function is needed to create entities with the configuration of doctrine if they are not yet been registered
|
||||
* in the ManagerRegistry
|
||||
*/
|
||||
private function getMappingDriverForNamespace(string $namespace): ?MappingDriver
|
||||
{
|
||||
$lowestCharacterDiff = null;
|
||||
$foundDriver = null;
|
||||
|
||||
foreach ($this->mappingDriversByPrefix ?? [] as $mappings) {
|
||||
foreach ($mappings as [$prefix, $driver]) {
|
||||
$diff = substr_compare($namespace, $prefix, 0);
|
||||
|
||||
if ($diff >= 0 && (null === $lowestCharacterDiff || $diff < $lowestCharacterDiff)) {
|
||||
$lowestCharacterDiff = $diff;
|
||||
$foundDriver = $driver;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $foundDriver;
|
||||
}
|
||||
}
|
||||
146
vendor/symfony/maker-bundle/src/Doctrine/EntityClassGenerator.php
vendored
Normal file
146
vendor/symfony/maker-bundle/src/Doctrine/EntityClassGenerator.php
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Doctrine;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Symfony\Bridge\Doctrine\Types\UlidType;
|
||||
use Symfony\Bridge\Doctrine\Types\UuidType;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\Maker\Common\EntityIdTypeEnum;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassNameDetails;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Uid\Ulid;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
use Symfony\UX\Turbo\Attribute\Broadcast;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class EntityClassGenerator
|
||||
{
|
||||
public function __construct(
|
||||
private Generator $generator,
|
||||
private DoctrineHelper $doctrineHelper,
|
||||
) {
|
||||
}
|
||||
|
||||
public function generateEntityClass(ClassNameDetails $entityClassDetails, bool $apiResource, bool $withPasswordUpgrade = false, bool $generateRepositoryClass = true, bool $broadcast = false, EntityIdTypeEnum $useUuidIdentifier = EntityIdTypeEnum::INT): string
|
||||
{
|
||||
$repoClassDetails = $this->generator->createClassNameDetails(
|
||||
$entityClassDetails->getRelativeName(),
|
||||
'Repository\\',
|
||||
'Repository'
|
||||
);
|
||||
|
||||
$tableName = $this->doctrineHelper->getPotentialTableName($entityClassDetails->getFullName());
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
$repoClassDetails->getFullName(),
|
||||
['Doctrine\\ORM\\Mapping' => 'ORM'],
|
||||
]);
|
||||
|
||||
if ($broadcast) {
|
||||
$useStatements->addUseStatement(Broadcast::class);
|
||||
}
|
||||
|
||||
if ($apiResource) {
|
||||
$useStatements->addUseStatement(ApiResource::class);
|
||||
}
|
||||
|
||||
if (EntityIdTypeEnum::UUID === $useUuidIdentifier) {
|
||||
$useStatements->addUseStatement([
|
||||
Uuid::class,
|
||||
UuidType::class,
|
||||
]);
|
||||
}
|
||||
|
||||
if (EntityIdTypeEnum::ULID === $useUuidIdentifier) {
|
||||
$useStatements->addUseStatement([
|
||||
Ulid::class,
|
||||
UlidType::class,
|
||||
]);
|
||||
}
|
||||
|
||||
$entityPath = $this->generator->generateClass(
|
||||
$entityClassDetails->getFullName(),
|
||||
'doctrine/Entity.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'repository_class_name' => $repoClassDetails->getShortName(),
|
||||
'api_resource' => $apiResource,
|
||||
'broadcast' => $broadcast,
|
||||
'should_escape_table_name' => $this->doctrineHelper->isKeyword($tableName),
|
||||
'table_name' => $tableName,
|
||||
'id_type' => $useUuidIdentifier,
|
||||
]
|
||||
);
|
||||
|
||||
if ($generateRepositoryClass) {
|
||||
$this->generateRepositoryClass(
|
||||
$repoClassDetails->getFullName(),
|
||||
$entityClassDetails->getFullName(),
|
||||
$withPasswordUpgrade,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
return $entityPath;
|
||||
}
|
||||
|
||||
public function generateRepositoryClass(string $repositoryClass, string $entityClass, bool $withPasswordUpgrade, bool $includeExampleComments = true): void
|
||||
{
|
||||
$shortEntityClass = Str::getShortClassName($entityClass);
|
||||
$entityAlias = strtolower($shortEntityClass[0]);
|
||||
|
||||
$passwordUserInterfaceName = UserInterface::class;
|
||||
|
||||
if (interface_exists(PasswordAuthenticatedUserInterface::class)) {
|
||||
$passwordUserInterfaceName = PasswordAuthenticatedUserInterface::class;
|
||||
}
|
||||
|
||||
$interfaceClassNameDetails = new ClassNameDetails($passwordUserInterfaceName, 'Symfony\Component\Security\Core\User');
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
$entityClass,
|
||||
ManagerRegistry::class,
|
||||
ServiceEntityRepository::class,
|
||||
]);
|
||||
|
||||
if ($withPasswordUpgrade) {
|
||||
$useStatements->addUseStatement([
|
||||
$interfaceClassNameDetails->getFullName(),
|
||||
PasswordUpgraderInterface::class,
|
||||
UnsupportedUserException::class,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->generator->generateClass(
|
||||
$repositoryClass,
|
||||
'doctrine/Repository.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'entity_class_name' => $shortEntityClass,
|
||||
'entity_alias' => $entityAlias,
|
||||
'with_password_upgrade' => $withPasswordUpgrade,
|
||||
'password_upgrade_user_interface' => $interfaceClassNameDetails,
|
||||
'include_example_comments' => $includeExampleComments,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
90
vendor/symfony/maker-bundle/src/Doctrine/EntityDetails.php
vendored
Normal file
90
vendor/symfony/maker-bundle/src/Doctrine/EntityDetails.php
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Doctrine;
|
||||
|
||||
use Doctrine\Persistence\Mapping\ClassMetadata;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
|
||||
/**
|
||||
* @author Sadicov Vladimir <sadikoff@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class EntityDetails
|
||||
{
|
||||
public function __construct(
|
||||
private ClassMetadata $metadata,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getRepositoryClass(): ?string
|
||||
{
|
||||
return $this->metadata->customRepositoryClassName;
|
||||
}
|
||||
|
||||
public function getIdentifier()
|
||||
{
|
||||
return $this->metadata->identifier[0];
|
||||
}
|
||||
|
||||
public function getDisplayFields(): array
|
||||
{
|
||||
return $this->metadata->fieldMappings;
|
||||
}
|
||||
|
||||
public function getFormFields(): array
|
||||
{
|
||||
$fields = (array) $this->metadata->fieldNames;
|
||||
// Remove the primary key field if it's not managed manually
|
||||
if (!$this->metadata->isIdentifierNatural()) {
|
||||
$fields = array_diff($fields, $this->metadata->identifier);
|
||||
}
|
||||
$fields = array_values($fields);
|
||||
|
||||
if (!empty($this->metadata->embeddedClasses)) {
|
||||
foreach (array_keys($this->metadata->embeddedClasses) as $embeddedClassKey) {
|
||||
$fields = array_filter($fields, static fn ($v) => !str_starts_with($v, $embeddedClassKey.'.'));
|
||||
}
|
||||
}
|
||||
|
||||
$fieldsWithTypes = [];
|
||||
foreach ($fields as $field) {
|
||||
$fieldsWithTypes[$field] = null;
|
||||
}
|
||||
|
||||
foreach ($this->metadata->fieldMappings as $fieldName => $fieldMapping) {
|
||||
$propType = DoctrineHelper::getPropertyTypeForColumn($fieldMapping['type']);
|
||||
if (($propType === '\\'.\DateTimeImmutable::class)
|
||||
|| ($propType === '\\'.\DateTimeInterface::class)) {
|
||||
$fieldsWithTypes[$fieldName] = [
|
||||
'type' => null,
|
||||
'options_code' => "'widget' => 'single_text'",
|
||||
];
|
||||
}
|
||||
}
|
||||
foreach ($this->metadata->associationMappings as $fieldName => $relation) {
|
||||
if (\Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_MANY === $relation['type']) {
|
||||
continue;
|
||||
}
|
||||
$fieldsWithTypes[$fieldName] = [
|
||||
'type' => EntityType::class,
|
||||
'options_code' => \sprintf('\'class\' => %s::class,', $relation['targetEntity'])."\n 'choice_label' => 'id',",
|
||||
'extra_use_classes' => [$relation['targetEntity']],
|
||||
];
|
||||
if (\Doctrine\ORM\Mapping\ClassMetadata::MANY_TO_MANY === $relation['type']) {
|
||||
$fieldsWithTypes[$fieldName]['options_code'] .= "\n 'multiple' => true,";
|
||||
}
|
||||
}
|
||||
|
||||
return $fieldsWithTypes;
|
||||
}
|
||||
}
|
||||
216
vendor/symfony/maker-bundle/src/Doctrine/EntityRegenerator.php
vendored
Normal file
216
vendor/symfony/maker-bundle/src/Doctrine/EntityRegenerator.php
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Doctrine;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\EmbeddedClassMapping;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Doctrine\Persistence\Mapping\MappingException as PersistenceMappingException;
|
||||
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
|
||||
use Symfony\Bundle\MakerBundle\FileManager;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassProperty;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class EntityRegenerator
|
||||
{
|
||||
public function __construct(
|
||||
private DoctrineHelper $doctrineHelper,
|
||||
private FileManager $fileManager,
|
||||
private Generator $generator,
|
||||
private EntityClassGenerator $entityClassGenerator,
|
||||
private bool $overwrite,
|
||||
) {
|
||||
}
|
||||
|
||||
public function regenerateEntities(string $classOrNamespace): void
|
||||
{
|
||||
try {
|
||||
$metadata = $this->doctrineHelper->getMetadata($classOrNamespace);
|
||||
} catch (MappingException|PersistenceMappingException) {
|
||||
$metadata = $this->doctrineHelper->getMetadata($classOrNamespace, true);
|
||||
}
|
||||
|
||||
if ($metadata instanceof ClassMetadata) {
|
||||
$metadata = [$metadata];
|
||||
} elseif (class_exists($classOrNamespace)) {
|
||||
throw new RuntimeCommandException(\sprintf('Could not find Doctrine metadata for "%s". Is it mapped as an entity?', $classOrNamespace));
|
||||
} elseif (empty($metadata)) {
|
||||
throw new RuntimeCommandException(\sprintf('No entities were found in the "%s" namespace.', $classOrNamespace));
|
||||
}
|
||||
|
||||
/** @var ClassSourceManipulator[] $operations */
|
||||
$operations = [];
|
||||
foreach ($metadata as $classMetadata) {
|
||||
if (!class_exists($classMetadata->name)) {
|
||||
// the class needs to be generated for the first time!
|
||||
$classPath = $this->generateClass($classMetadata);
|
||||
} else {
|
||||
$classPath = $this->getPathOfClass($classMetadata->name);
|
||||
}
|
||||
|
||||
$mappedFields = $this->getMappedFieldsInEntity($classMetadata);
|
||||
|
||||
if ($classMetadata->customRepositoryClassName) {
|
||||
$this->generateRepository($classMetadata);
|
||||
}
|
||||
|
||||
$manipulator = $this->createClassManipulator($classPath);
|
||||
$operations[$classPath] = $manipulator;
|
||||
|
||||
$embeddedClasses = [];
|
||||
|
||||
foreach ($classMetadata->embeddedClasses as $fieldName => $mapping) {
|
||||
if (str_contains($fieldName, '.')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @legacy - Remove conditional when ORM 2.x is no longer supported. */
|
||||
$className = ($mapping instanceof EmbeddedClassMapping) ? $mapping->class : $mapping['class'];
|
||||
|
||||
$embeddedClasses[$fieldName] = $this->getPathOfClass($className);
|
||||
|
||||
$operations[$embeddedClasses[$fieldName]] = $this->createClassManipulator($embeddedClasses[$fieldName]);
|
||||
|
||||
if (!\in_array($fieldName, $mappedFields)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$manipulator->addEmbeddedEntity($fieldName, $className);
|
||||
}
|
||||
|
||||
foreach ($classMetadata->fieldMappings as $fieldName => $mapping) {
|
||||
// skip embedded fields
|
||||
if (str_contains($fieldName, '.')) {
|
||||
[$fieldName, $embeddedFiledName] = explode('.', $fieldName);
|
||||
|
||||
$property = ClassProperty::createFromObject($mapping);
|
||||
$property->propertyName = $embeddedFiledName;
|
||||
|
||||
$operations[$embeddedClasses[$fieldName]]->addEntityField($property);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!\in_array($fieldName, $mappedFields)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$manipulator->addEntityField(ClassProperty::createFromObject($mapping));
|
||||
}
|
||||
|
||||
foreach ($classMetadata->associationMappings as $fieldName => $mapping) {
|
||||
if (!\in_array($fieldName, $mappedFields)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match ($mapping['type']) {
|
||||
ClassMetadata::MANY_TO_ONE => $manipulator->addManyToOneRelation(RelationManyToOne::createFromObject($mapping)),
|
||||
ClassMetadata::ONE_TO_MANY => $manipulator->addOneToManyRelation(RelationOneToMany::createFromObject($mapping)),
|
||||
ClassMetadata::MANY_TO_MANY => $manipulator->addManyToManyRelation(RelationManyToMany::createFromObject($mapping)),
|
||||
ClassMetadata::ONE_TO_ONE => $manipulator->addOneToOneRelation(RelationOneToOne::createFromObject($mapping)),
|
||||
default => throw new \Exception('Unknown association type.'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($operations as $filename => $manipulator) {
|
||||
$this->fileManager->dumpFile(
|
||||
$filename,
|
||||
$manipulator->getSourceCode()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function generateClass(ClassMetadata $metadata): string
|
||||
{
|
||||
$path = $this->generator->generateClass(
|
||||
$metadata->name,
|
||||
'Class.tpl.php',
|
||||
[]
|
||||
);
|
||||
$this->generator->writeChanges();
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
private function createClassManipulator(string $classPath): ClassSourceManipulator
|
||||
{
|
||||
return new ClassSourceManipulator(
|
||||
sourceCode: $this->fileManager->getFileContents($classPath),
|
||||
overwrite: $this->overwrite,
|
||||
// if properties need to be generated then, by definition,
|
||||
// some non-annotation config is being used (e.g. XML), and so, the
|
||||
// properties should not have annotations added to them
|
||||
useAttributesForDoctrineMapping: false
|
||||
);
|
||||
}
|
||||
|
||||
private function getPathOfClass(string $class): string
|
||||
{
|
||||
return (new \ReflectionClass($class))->getFileName();
|
||||
}
|
||||
|
||||
private function generateRepository(ClassMetadata $metadata): void
|
||||
{
|
||||
if (!$metadata->customRepositoryClassName) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (class_exists($metadata->customRepositoryClassName)) {
|
||||
// repository already exists
|
||||
return;
|
||||
}
|
||||
|
||||
$this->entityClassGenerator->generateRepositoryClass(
|
||||
$metadata->customRepositoryClassName,
|
||||
$metadata->name,
|
||||
false
|
||||
);
|
||||
|
||||
$this->generator->writeChanges();
|
||||
}
|
||||
|
||||
private function getMappedFieldsInEntity(ClassMetadata $classMetadata): array
|
||||
{
|
||||
/** @var \ReflectionClass $classReflection */
|
||||
$classReflection = $classMetadata->reflClass;
|
||||
|
||||
$targetFields = [
|
||||
...array_keys($classMetadata->fieldMappings),
|
||||
...array_keys($classMetadata->associationMappings),
|
||||
...array_keys($classMetadata->embeddedClasses),
|
||||
];
|
||||
|
||||
if ($classReflection) {
|
||||
// exclude traits
|
||||
$traitProperties = [];
|
||||
|
||||
foreach ($classReflection->getTraits() as $trait) {
|
||||
foreach ($trait->getProperties() as $property) {
|
||||
$traitProperties[] = $property->getName();
|
||||
}
|
||||
}
|
||||
|
||||
$targetFields = array_diff($targetFields, $traitProperties);
|
||||
|
||||
// exclude inherited properties
|
||||
$targetFields = array_filter($targetFields, static fn ($field) => $classReflection->hasProperty($field)
|
||||
&& $classReflection->getProperty($field)->getDeclaringClass()->getName() === $classReflection->getName());
|
||||
}
|
||||
|
||||
return $targetFields;
|
||||
}
|
||||
}
|
||||
189
vendor/symfony/maker-bundle/src/Doctrine/EntityRelation.php
vendored
Normal file
189
vendor/symfony/maker-bundle/src/Doctrine/EntityRelation.php
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Doctrine;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class EntityRelation
|
||||
{
|
||||
public const MANY_TO_ONE = 'ManyToOne';
|
||||
public const ONE_TO_MANY = 'OneToMany';
|
||||
public const MANY_TO_MANY = 'ManyToMany';
|
||||
public const ONE_TO_ONE = 'OneToOne';
|
||||
|
||||
private $owningProperty;
|
||||
private $inverseProperty;
|
||||
private bool $isNullable = false;
|
||||
private bool $isSelfReferencing = false;
|
||||
private bool $orphanRemoval = false;
|
||||
private bool $mapInverseRelation = true;
|
||||
|
||||
public function __construct(
|
||||
private string $type,
|
||||
private string $owningClass,
|
||||
private string $inverseClass,
|
||||
) {
|
||||
if (!\in_array($type, self::getValidRelationTypes())) {
|
||||
throw new \Exception(\sprintf('Invalid relation type "%s"', $type));
|
||||
}
|
||||
|
||||
if (self::ONE_TO_MANY === $type) {
|
||||
throw new \Exception('Use ManyToOne instead of OneToMany');
|
||||
}
|
||||
|
||||
$this->isSelfReferencing = $owningClass === $inverseClass;
|
||||
}
|
||||
|
||||
public function setOwningProperty(string $owningProperty): void
|
||||
{
|
||||
$this->owningProperty = $owningProperty;
|
||||
}
|
||||
|
||||
public function setInverseProperty(string $inverseProperty): void
|
||||
{
|
||||
if (!$this->mapInverseRelation) {
|
||||
throw new \Exception('Cannot call setInverseProperty() when the inverse relation will not be mapped.');
|
||||
}
|
||||
|
||||
$this->inverseProperty = $inverseProperty;
|
||||
}
|
||||
|
||||
public function setIsNullable(bool $isNullable): void
|
||||
{
|
||||
$this->isNullable = $isNullable;
|
||||
}
|
||||
|
||||
public function setOrphanRemoval(bool $orphanRemoval): void
|
||||
{
|
||||
$this->orphanRemoval = $orphanRemoval;
|
||||
}
|
||||
|
||||
public static function getValidRelationTypes(): array
|
||||
{
|
||||
return [
|
||||
self::MANY_TO_ONE,
|
||||
self::ONE_TO_MANY,
|
||||
self::MANY_TO_MANY,
|
||||
self::ONE_TO_ONE,
|
||||
];
|
||||
}
|
||||
|
||||
public function getOwningRelation(): RelationManyToMany|RelationOneToOne|RelationManyToOne
|
||||
{
|
||||
return match ($this->getType()) {
|
||||
self::MANY_TO_ONE => (new RelationManyToOne(
|
||||
propertyName: $this->owningProperty,
|
||||
targetClassName: $this->inverseClass,
|
||||
targetPropertyName: $this->inverseProperty,
|
||||
isSelfReferencing: $this->isSelfReferencing,
|
||||
mapInverseRelation: $this->mapInverseRelation,
|
||||
isOwning: true,
|
||||
isNullable: $this->isNullable,
|
||||
)),
|
||||
self::MANY_TO_MANY => (new RelationManyToMany(
|
||||
propertyName: $this->owningProperty,
|
||||
targetClassName: $this->inverseClass,
|
||||
targetPropertyName: $this->inverseProperty,
|
||||
isSelfReferencing: $this->isSelfReferencing,
|
||||
mapInverseRelation: $this->mapInverseRelation,
|
||||
isOwning: true,
|
||||
)),
|
||||
self::ONE_TO_ONE => (new RelationOneToOne(
|
||||
propertyName: $this->owningProperty,
|
||||
targetClassName: $this->inverseClass,
|
||||
targetPropertyName: $this->inverseProperty,
|
||||
isSelfReferencing: $this->isSelfReferencing,
|
||||
mapInverseRelation: $this->mapInverseRelation,
|
||||
isOwning: true,
|
||||
isNullable: $this->isNullable,
|
||||
)),
|
||||
default => throw new \InvalidArgumentException('Invalid type'),
|
||||
};
|
||||
}
|
||||
|
||||
public function getInverseRelation(): RelationManyToMany|RelationOneToOne|RelationOneToMany
|
||||
{
|
||||
return match ($this->getType()) {
|
||||
self::MANY_TO_ONE => (new RelationOneToMany(
|
||||
propertyName: $this->inverseProperty,
|
||||
targetClassName: $this->owningClass,
|
||||
targetPropertyName: $this->owningProperty,
|
||||
isSelfReferencing: $this->isSelfReferencing,
|
||||
orphanRemoval: $this->orphanRemoval,
|
||||
)),
|
||||
self::MANY_TO_MANY => (new RelationManyToMany(
|
||||
propertyName: $this->inverseProperty,
|
||||
targetClassName: $this->owningClass,
|
||||
targetPropertyName: $this->owningProperty,
|
||||
isSelfReferencing: $this->isSelfReferencing
|
||||
)),
|
||||
self::ONE_TO_ONE => (new RelationOneToOne(
|
||||
propertyName: $this->inverseProperty,
|
||||
targetClassName: $this->owningClass,
|
||||
targetPropertyName: $this->owningProperty,
|
||||
isSelfReferencing: $this->isSelfReferencing,
|
||||
isNullable: $this->isNullable,
|
||||
)),
|
||||
default => throw new \InvalidArgumentException('Invalid type'),
|
||||
};
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getOwningClass(): string
|
||||
{
|
||||
return $this->owningClass;
|
||||
}
|
||||
|
||||
public function getInverseClass(): string
|
||||
{
|
||||
return $this->inverseClass;
|
||||
}
|
||||
|
||||
public function getOwningProperty(): string
|
||||
{
|
||||
return $this->owningProperty;
|
||||
}
|
||||
|
||||
public function getInverseProperty(): string
|
||||
{
|
||||
return $this->inverseProperty;
|
||||
}
|
||||
|
||||
public function isNullable(): bool
|
||||
{
|
||||
return $this->isNullable;
|
||||
}
|
||||
|
||||
public function isSelfReferencing(): bool
|
||||
{
|
||||
return $this->isSelfReferencing;
|
||||
}
|
||||
|
||||
public function getMapInverseRelation(): bool
|
||||
{
|
||||
return $this->mapInverseRelation;
|
||||
}
|
||||
|
||||
public function setMapInverseRelation(bool $mapInverseRelation): void
|
||||
{
|
||||
if ($mapInverseRelation && $this->inverseProperty) {
|
||||
throw new \Exception('Cannot set setMapInverseRelation() to true when the inverse relation property is set.');
|
||||
}
|
||||
|
||||
$this->mapInverseRelation = $mapInverseRelation;
|
||||
}
|
||||
}
|
||||
42
vendor/symfony/maker-bundle/src/Doctrine/ORMDependencyBuilder.php
vendored
Normal file
42
vendor/symfony/maker-bundle/src/Doctrine/ORMDependencyBuilder.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Doctrine;
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ORMDependencyBuilder
|
||||
{
|
||||
/**
|
||||
* Central method to add dependencies needed for Doctrine ORM.
|
||||
*/
|
||||
public static function buildDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$classes = [
|
||||
// guarantee DoctrineBundle
|
||||
DoctrineBundle::class,
|
||||
// guarantee ORM
|
||||
Column::class,
|
||||
];
|
||||
|
||||
foreach ($classes as $class) {
|
||||
$dependencies->addClassDependency(
|
||||
$class,
|
||||
'orm'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
64
vendor/symfony/maker-bundle/src/Doctrine/RelationManyToMany.php
vendored
Normal file
64
vendor/symfony/maker-bundle/src/Doctrine/RelationManyToMany.php
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Doctrine;
|
||||
|
||||
use Doctrine\ORM\Mapping\ManyToManyInverseSideMapping;
|
||||
use Doctrine\ORM\Mapping\ManyToManyOwningSideMapping;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class RelationManyToMany extends BaseCollectionRelation
|
||||
{
|
||||
public function getTargetSetterMethodName(): string
|
||||
{
|
||||
return 'add'.Str::asCamelCase(Str::pluralCamelCaseToSingular($this->getTargetPropertyName()));
|
||||
}
|
||||
|
||||
public function getTargetRemoverMethodName(): string
|
||||
{
|
||||
return 'remove'.Str::asCamelCase(Str::pluralCamelCaseToSingular($this->getTargetPropertyName()));
|
||||
}
|
||||
|
||||
public static function createFromObject(ManyToManyInverseSideMapping|ManyToManyOwningSideMapping|array $mapping): self
|
||||
{
|
||||
/* @legacy Remove conditional when ORM 2.x is no longer supported! */
|
||||
if (\is_array($mapping)) {
|
||||
return new self(
|
||||
propertyName: $mapping['fieldName'],
|
||||
targetClassName: $mapping['targetEntity'],
|
||||
targetPropertyName: $mapping['mappedBy'],
|
||||
mapInverseRelation: !$mapping['isOwningSide'] || null !== $mapping['inversedBy'],
|
||||
isOwning: $mapping['isOwningSide'],
|
||||
);
|
||||
}
|
||||
|
||||
if ($mapping instanceof ManyToManyOwningSideMapping) {
|
||||
return new self(
|
||||
propertyName: $mapping->fieldName,
|
||||
targetClassName: $mapping->targetEntity,
|
||||
targetPropertyName: $mapping->inversedBy,
|
||||
mapInverseRelation: (null !== $mapping->inversedBy),
|
||||
isOwning: $mapping->isOwningSide(),
|
||||
);
|
||||
}
|
||||
|
||||
return new self(
|
||||
propertyName: $mapping->fieldName,
|
||||
targetClassName: $mapping->targetEntity,
|
||||
targetPropertyName: $mapping->mappedBy,
|
||||
mapInverseRelation: true,
|
||||
isOwning: $mapping->isOwningSide(),
|
||||
);
|
||||
}
|
||||
}
|
||||
44
vendor/symfony/maker-bundle/src/Doctrine/RelationManyToOne.php
vendored
Normal file
44
vendor/symfony/maker-bundle/src/Doctrine/RelationManyToOne.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Doctrine;
|
||||
|
||||
use Doctrine\ORM\Mapping\ManyToOneAssociationMapping;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class RelationManyToOne extends BaseRelation
|
||||
{
|
||||
public static function createFromObject(ManyToOneAssociationMapping|array $mapping): self
|
||||
{
|
||||
/* @legacy Remove conditional when ORM 2.x is no longer supported! */
|
||||
if (\is_array($mapping)) {
|
||||
return new self(
|
||||
propertyName: $mapping['fieldName'],
|
||||
targetClassName: $mapping['targetEntity'],
|
||||
targetPropertyName: $mapping['inversedBy'],
|
||||
mapInverseRelation: null !== $mapping['inversedBy'],
|
||||
isOwning: true,
|
||||
isNullable: $mapping['joinColumns'][0]['nullable'] ?? true,
|
||||
);
|
||||
}
|
||||
|
||||
return new self(
|
||||
propertyName: $mapping->fieldName,
|
||||
targetClassName: $mapping->targetEntity,
|
||||
targetPropertyName: $mapping->inversedBy,
|
||||
mapInverseRelation: null !== $mapping->inversedBy,
|
||||
isOwning: true,
|
||||
isNullable: $mapping->joinColumns[0]->nullable ?? true,
|
||||
);
|
||||
}
|
||||
}
|
||||
56
vendor/symfony/maker-bundle/src/Doctrine/RelationOneToMany.php
vendored
Normal file
56
vendor/symfony/maker-bundle/src/Doctrine/RelationOneToMany.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Doctrine;
|
||||
|
||||
use Doctrine\ORM\Mapping\OneToManyAssociationMapping;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class RelationOneToMany extends BaseCollectionRelation
|
||||
{
|
||||
public function getTargetGetterMethodName(): string
|
||||
{
|
||||
return 'get'.Str::asCamelCase($this->getTargetPropertyName());
|
||||
}
|
||||
|
||||
public function getTargetSetterMethodName(): string
|
||||
{
|
||||
return 'set'.Str::asCamelCase($this->getTargetPropertyName());
|
||||
}
|
||||
|
||||
public function isMapInverseRelation(): bool
|
||||
{
|
||||
throw new \Exception('OneToMany IS the inverse side!');
|
||||
}
|
||||
|
||||
public static function createFromObject(OneToManyAssociationMapping|array $mapping): self
|
||||
{
|
||||
/* @legacy Remove conditional when ORM 2.x is no longer supported! */
|
||||
if (\is_array($mapping)) {
|
||||
return new self(
|
||||
propertyName: $mapping['fieldName'],
|
||||
targetClassName: $mapping['targetEntity'],
|
||||
targetPropertyName: $mapping['mappedBy'],
|
||||
orphanRemoval: $mapping['orphanRemoval'],
|
||||
);
|
||||
}
|
||||
|
||||
return new self(
|
||||
propertyName: $mapping->fieldName,
|
||||
targetClassName: $mapping->targetEntity,
|
||||
targetPropertyName: $mapping->mappedBy,
|
||||
orphanRemoval: $mapping->orphanRemoval,
|
||||
);
|
||||
}
|
||||
}
|
||||
67
vendor/symfony/maker-bundle/src/Doctrine/RelationOneToOne.php
vendored
Normal file
67
vendor/symfony/maker-bundle/src/Doctrine/RelationOneToOne.php
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Doctrine;
|
||||
|
||||
use Doctrine\ORM\Mapping\OneToOneInverseSideMapping;
|
||||
use Doctrine\ORM\Mapping\OneToOneOwningSideMapping;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class RelationOneToOne extends BaseRelation
|
||||
{
|
||||
public function getTargetGetterMethodName(): string
|
||||
{
|
||||
return 'get'.Str::asCamelCase($this->getTargetPropertyName());
|
||||
}
|
||||
|
||||
public function getTargetSetterMethodName(): string
|
||||
{
|
||||
return 'set'.Str::asCamelCase($this->getTargetPropertyName());
|
||||
}
|
||||
|
||||
public static function createFromObject(OneToOneInverseSideMapping|OneToOneOwningSideMapping|array $mapping): self
|
||||
{
|
||||
/* @legacy Remove conditional when ORM 2.x is no longer supported! */
|
||||
if (\is_array($mapping)) {
|
||||
return new self(
|
||||
propertyName: $mapping['fieldName'],
|
||||
targetClassName: $mapping['targetEntity'],
|
||||
targetPropertyName: $mapping['isOwningSide'] ? $mapping['inversedBy'] : $mapping['mappedBy'],
|
||||
mapInverseRelation: !$mapping['isOwningSide'] || null !== $mapping['inversedBy'],
|
||||
isOwning: $mapping['isOwningSide'],
|
||||
isNullable: $mapping['joinColumns'][0]['nullable'] ?? true,
|
||||
);
|
||||
}
|
||||
|
||||
if ($mapping instanceof OneToOneOwningSideMapping) {
|
||||
return new self(
|
||||
propertyName: $mapping->fieldName,
|
||||
targetClassName: $mapping->targetEntity,
|
||||
targetPropertyName: $mapping->inversedBy,
|
||||
mapInverseRelation: (null !== $mapping->inversedBy),
|
||||
isOwning: true,
|
||||
isNullable: $mapping->joinColumns[0]->nullable ?? true,
|
||||
);
|
||||
}
|
||||
|
||||
return new self(
|
||||
propertyName: $mapping->fieldName,
|
||||
targetClassName: $mapping->targetEntity,
|
||||
targetPropertyName: $mapping->mappedBy,
|
||||
mapInverseRelation: true,
|
||||
isOwning: false,
|
||||
isNullable: $mapping->joinColumns[0]->nullable ?? true,
|
||||
);
|
||||
}
|
||||
}
|
||||
62
vendor/symfony/maker-bundle/src/Doctrine/StaticReflectionService.php
vendored
Normal file
62
vendor/symfony/maker-bundle/src/Doctrine/StaticReflectionService.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Doctrine;
|
||||
|
||||
use Doctrine\Persistence\Mapping\ReflectionService;
|
||||
|
||||
/**
|
||||
* @internal replacing removed Doctrine\Persistence\Mapping\StaticReflectionService
|
||||
*/
|
||||
final class StaticReflectionService implements ReflectionService
|
||||
{
|
||||
public function getParentClasses($class): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getClassShortName($class): string
|
||||
{
|
||||
$nsSeparatorLastPosition = strrpos($class, '\\');
|
||||
|
||||
if (false !== $nsSeparatorLastPosition) {
|
||||
$class = substr($class, $nsSeparatorLastPosition + 1);
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
public function getClassNamespace($class): string
|
||||
{
|
||||
$namespace = '';
|
||||
|
||||
if (str_contains($class, '\\')) {
|
||||
$namespace = strrev(substr(strrev($class), (int) strpos(strrev($class), '\\') + 1));
|
||||
}
|
||||
|
||||
return $namespace;
|
||||
}
|
||||
|
||||
public function getClass($class): \ReflectionClass
|
||||
{
|
||||
return new \ReflectionClass($class);
|
||||
}
|
||||
|
||||
public function getAccessibleProperty($class, $property): ?\ReflectionProperty
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function hasPublicMethod($class, $method): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
63
vendor/symfony/maker-bundle/src/Event/ConsoleErrorSubscriber.php
vendored
Normal file
63
vendor/symfony/maker-bundle/src/Event/ConsoleErrorSubscriber.php
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Event;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
|
||||
use Symfony\Component\Console\ConsoleEvents;
|
||||
use Symfony\Component\Console\Event\ConsoleErrorEvent;
|
||||
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Prints certain exceptions in a pretty way and silences normal exception handling.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*/
|
||||
final class ConsoleErrorSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
private bool $setExitCode = false;
|
||||
|
||||
public function onConsoleError(ConsoleErrorEvent $event): void
|
||||
{
|
||||
if (!$event->getError() instanceof RuntimeCommandException) {
|
||||
return;
|
||||
}
|
||||
|
||||
// prevent any visual logging from appearing
|
||||
$event->stopPropagation();
|
||||
// prevent the exception from actually being thrown
|
||||
$event->setExitCode(0);
|
||||
$this->setExitCode = true;
|
||||
|
||||
$io = new SymfonyStyle($event->getInput(), $event->getOutput());
|
||||
$io->error($event->getError()->getMessage());
|
||||
}
|
||||
|
||||
public function onConsoleTerminate(ConsoleTerminateEvent $event): void
|
||||
{
|
||||
if (!$this->setExitCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// finally set a non-zero exit code
|
||||
$event->setExitCode(1);
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
ConsoleEvents::ERROR => 'onConsoleError',
|
||||
ConsoleEvents::TERMINATE => 'onConsoleTerminate',
|
||||
];
|
||||
}
|
||||
}
|
||||
124
vendor/symfony/maker-bundle/src/EventRegistry.php
vendored
Normal file
124
vendor/symfony/maker-bundle/src/EventRegistry.php
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle;
|
||||
|
||||
use Symfony\Component\Console\ConsoleEvents;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\Security\Core\AuthenticationEvents;
|
||||
use Symfony\Component\Security\Http\SecurityEvents;
|
||||
use Symfony\Component\Workflow\WorkflowEvents;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class EventRegistry
|
||||
{
|
||||
private static array $eventsMap = [];
|
||||
|
||||
public function __construct(
|
||||
private EventDispatcherInterface $eventDispatcher,
|
||||
) {
|
||||
self::$eventsMap = array_flip([
|
||||
...ConsoleEvents::ALIASES,
|
||||
...KernelEvents::ALIASES,
|
||||
...(class_exists(AuthenticationEvents::class) ? AuthenticationEvents::ALIASES : []),
|
||||
...(class_exists(SecurityEvents::class) ? SecurityEvents::ALIASES : []),
|
||||
...(class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : []),
|
||||
...(class_exists(FormEvents::class) ? FormEvents::ALIASES : []),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all known event names in the system.
|
||||
*/
|
||||
public function getAllActiveEvents(): array
|
||||
{
|
||||
$activeEvents = [];
|
||||
foreach (self::$eventsMap as $eventName => $eventClass) {
|
||||
if (!class_exists($eventClass)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$activeEvents[] = $eventName;
|
||||
}
|
||||
|
||||
$listeners = $this->eventDispatcher->getListeners();
|
||||
|
||||
foreach (array_keys($listeners) as $listenerKey) {
|
||||
if (!isset(self::$eventsMap[$listenerKey])) {
|
||||
self::$eventsMap[$listenerKey] = $this->getEventClassName($listenerKey);
|
||||
}
|
||||
}
|
||||
|
||||
$activeEvents = array_unique(array_merge($activeEvents, array_keys($listeners)));
|
||||
|
||||
asort($activeEvents);
|
||||
|
||||
return $activeEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to get the event class for a given event.
|
||||
*/
|
||||
public function getEventClassName(string $event): ?string
|
||||
{
|
||||
// if the event is already a class name, use it
|
||||
if (class_exists($event)) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
if (isset(self::$eventsMap[$event])) {
|
||||
return self::$eventsMap[$event];
|
||||
}
|
||||
|
||||
$listeners = $this->eventDispatcher->getListeners($event);
|
||||
if (empty($listeners)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($listeners as $listener) {
|
||||
if (!\is_array($listener) || 2 !== \count($listener)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$reflectionMethod = new \ReflectionMethod($listener[0], $listener[1]);
|
||||
$args = $reflectionMethod->getParameters();
|
||||
if (!$args) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (null !== $type = $args[0]->getType()) {
|
||||
$type = $type instanceof \ReflectionNamedType ? $type->getName() : null;
|
||||
|
||||
// ignore an "object" type-hint
|
||||
if ('object' === $type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function listActiveEvents(array $events): array
|
||||
{
|
||||
foreach ($events as $key => $event) {
|
||||
$events[$key] = \sprintf('%s (<fg=yellow>%s</>)', $event, self::$eventsMap[$event]);
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
}
|
||||
23
vendor/symfony/maker-bundle/src/Exception/RuntimeCommandException.php
vendored
Normal file
23
vendor/symfony/maker-bundle/src/Exception/RuntimeCommandException.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Exception;
|
||||
|
||||
use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
|
||||
/**
|
||||
* An exception whose output is displayed as a clean error.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*/
|
||||
final class RuntimeCommandException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
207
vendor/symfony/maker-bundle/src/FileManager.php
vendored
Normal file
207
vendor/symfony/maker-bundle/src/FileManager.php
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\Util\AutoloaderUtil;
|
||||
use Symfony\Bundle\MakerBundle\Util\MakerFileLinkFormatter;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
/**
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class FileManager
|
||||
{
|
||||
private ?SymfonyStyle $io = null;
|
||||
|
||||
public function __construct(
|
||||
private Filesystem $fs,
|
||||
private AutoloaderUtil $autoloaderUtil,
|
||||
private MakerFileLinkFormatter $makerFileLinkFormatter,
|
||||
private string $rootDirectory,
|
||||
private ?string $twigDefaultPath = null,
|
||||
) {
|
||||
$this->rootDirectory = rtrim($this->realPath($this->normalizeSlashes($rootDirectory)), '/');
|
||||
$this->twigDefaultPath = $twigDefaultPath ? rtrim($this->relativizePath($twigDefaultPath), '/') : null;
|
||||
}
|
||||
|
||||
public function setIO(SymfonyStyle $io): void
|
||||
{
|
||||
$this->io = $io;
|
||||
}
|
||||
|
||||
public function parseTemplate(string $templatePath, array $parameters): string
|
||||
{
|
||||
ob_start();
|
||||
extract($parameters, \EXTR_SKIP);
|
||||
include $templatePath;
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
public function dumpFile(string $filename, string $content): void
|
||||
{
|
||||
$absolutePath = $this->absolutizePath($filename);
|
||||
$newFile = !$this->fileExists($filename);
|
||||
$existingContent = $newFile ? '' : file_get_contents($absolutePath);
|
||||
|
||||
$comment = $newFile ? '<fg=blue>created</>' : '<fg=yellow>updated</>';
|
||||
if ($existingContent === $content) {
|
||||
$comment = '<fg=green>no change</>';
|
||||
}
|
||||
|
||||
$this->fs->dumpFile($absolutePath, $content);
|
||||
$relativePath = $this->relativizePath($filename);
|
||||
|
||||
$this->io?->comment(\sprintf(
|
||||
'%s: %s',
|
||||
$comment,
|
||||
$this->makerFileLinkFormatter->makeLinkedPath($absolutePath, $relativePath)
|
||||
));
|
||||
}
|
||||
|
||||
public function fileExists($path): bool
|
||||
{
|
||||
return file_exists($this->absolutizePath($path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to make the path relative to the root directory.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function relativizePath(string $absolutePath): string
|
||||
{
|
||||
$absolutePath = $this->normalizeSlashes($absolutePath);
|
||||
|
||||
// see if the path is even in the root
|
||||
if (!str_contains($absolutePath, $this->rootDirectory)) {
|
||||
return $absolutePath;
|
||||
}
|
||||
|
||||
$absolutePath = $this->realPath($absolutePath);
|
||||
|
||||
// str_replace but only the first occurrence
|
||||
$relativePath = ltrim(implode('', explode($this->rootDirectory, $absolutePath, 2)), '/');
|
||||
if (str_starts_with($relativePath, './')) {
|
||||
$relativePath = substr($relativePath, 2);
|
||||
}
|
||||
|
||||
return is_dir($absolutePath) ? rtrim($relativePath, '/').'/' : $relativePath;
|
||||
}
|
||||
|
||||
public function getFileContents(string $path): string
|
||||
{
|
||||
if (!$this->fileExists($path)) {
|
||||
throw new \InvalidArgumentException(\sprintf('Cannot find file "%s"', $path));
|
||||
}
|
||||
|
||||
return file_get_contents($this->absolutizePath($path));
|
||||
}
|
||||
|
||||
public function isPathInVendor(string $path): bool
|
||||
{
|
||||
return str_starts_with(
|
||||
$this->normalizeSlashes($path),
|
||||
$this->normalizeSlashes($this->rootDirectory.'/vendor/')
|
||||
);
|
||||
}
|
||||
|
||||
public function absolutizePath($path): string
|
||||
{
|
||||
if (str_starts_with($path, '/')) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
// support windows drive paths: C:\ or C:/
|
||||
if (1 === strpos($path, ':\\') || 1 === strpos($path, ':/')) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
return \sprintf('%s/%s', $this->rootDirectory, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getRelativePathForFutureClass(string $className): ?string
|
||||
{
|
||||
$path = $this->autoloaderUtil->getPathForFutureClass($className);
|
||||
|
||||
return null === $path ? null : $this->relativizePath($path);
|
||||
}
|
||||
|
||||
public function getNamespacePrefixForClass(string $className): string
|
||||
{
|
||||
return $this->autoloaderUtil->getNamespacePrefixForClass($className);
|
||||
}
|
||||
|
||||
public function isNamespaceConfiguredToAutoload(string $namespace): bool
|
||||
{
|
||||
return $this->autoloaderUtil->isNamespaceConfiguredToAutoload($namespace);
|
||||
}
|
||||
|
||||
public function getRootDirectory(): string
|
||||
{
|
||||
return $this->rootDirectory;
|
||||
}
|
||||
|
||||
public function getPathForTemplate(string $filename): string
|
||||
{
|
||||
if (null === $this->twigDefaultPath) {
|
||||
throw new \RuntimeException('Cannot get path for template: is Twig installed?');
|
||||
}
|
||||
|
||||
return $this->twigDefaultPath.'/'.$filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve '../' in paths (like real_path), but for non-existent files.
|
||||
*/
|
||||
private function realPath(string $absolutePath): string
|
||||
{
|
||||
$finalParts = [];
|
||||
$currentIndex = -1;
|
||||
|
||||
$absolutePath = $this->normalizeSlashes($absolutePath);
|
||||
foreach (explode('/', $absolutePath) as $pathPart) {
|
||||
if ('..' === $pathPart) {
|
||||
// we need to remove the previous entry
|
||||
if (-1 === $currentIndex) {
|
||||
throw new \Exception(\sprintf('Problem making path relative - is the path "%s" absolute?', $absolutePath));
|
||||
}
|
||||
|
||||
unset($finalParts[$currentIndex]);
|
||||
--$currentIndex;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$finalParts[] = $pathPart;
|
||||
++$currentIndex;
|
||||
}
|
||||
|
||||
$finalPath = implode('/', $finalParts);
|
||||
|
||||
// Normalize: // => /
|
||||
// Normalize: /./ => /
|
||||
return str_replace(['//', '/./'], '/', $finalPath);
|
||||
}
|
||||
|
||||
private function normalizeSlashes(string $path): string
|
||||
{
|
||||
return str_replace('\\', '/', $path);
|
||||
}
|
||||
}
|
||||
341
vendor/symfony/maker-bundle/src/Generator.php
vendored
Normal file
341
vendor/symfony/maker-bundle/src/Generator.php
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassNameDetails;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassData;
|
||||
use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil;
|
||||
use Symfony\Bundle\MakerBundle\Util\TemplateComponentGenerator;
|
||||
|
||||
/**
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*/
|
||||
class Generator
|
||||
{
|
||||
private GeneratorTwigHelper $twigHelper;
|
||||
private array $pendingOperations = [];
|
||||
private array $generatedFiles = [];
|
||||
|
||||
public function __construct(
|
||||
private FileManager $fileManager,
|
||||
private string $namespacePrefix,
|
||||
?PhpCompatUtil $phpCompatUtil = null,
|
||||
private ?TemplateComponentGenerator $templateComponentGenerator = null,
|
||||
) {
|
||||
$this->twigHelper = new GeneratorTwigHelper($fileManager);
|
||||
$this->namespacePrefix = trim($namespacePrefix, '\\');
|
||||
|
||||
if (null !== $phpCompatUtil) {
|
||||
trigger_deprecation('symfony/maker-bundle', 'v1.44.0', 'Initializing Generator while providing an instance of PhpCompatUtil is deprecated.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new file for a class from a template.
|
||||
*
|
||||
* @param string $className The fully-qualified class name
|
||||
* @param string $templateName Template name in Resources/skeleton to use
|
||||
* @param array $variables Array of variables to pass to the template
|
||||
*
|
||||
* @return string The path where the file will be created
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function generateClass(string $className, string $templateName, array $variables = []): string
|
||||
{
|
||||
if (\array_key_exists('class_data', $variables) && $variables['class_data'] instanceof ClassData) {
|
||||
$classData = $this->templateComponentGenerator->configureClass($variables['class_data']);
|
||||
$className = $classData->getFullClassName();
|
||||
}
|
||||
|
||||
$targetPath = $this->fileManager->getRelativePathForFutureClass($className);
|
||||
|
||||
if (null === $targetPath) {
|
||||
throw new \LogicException(\sprintf('Could not determine where to locate the new class "%s", maybe try with a full namespace like "My\\Full\\Namespace\\%s"', $className, Str::getShortClassName($className)));
|
||||
}
|
||||
|
||||
$variables = array_merge($variables, [
|
||||
'class_name' => Str::getShortClassName($className),
|
||||
'namespace' => Str::getNamespace($className),
|
||||
]);
|
||||
|
||||
$this->addOperation($targetPath, $templateName, $variables);
|
||||
|
||||
return $targetPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Future replacement for generateClass().
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @param string $templateName Template name in the templates/ dir to use
|
||||
* @param array $variables Array of variables to pass to the template
|
||||
* @param bool $isController Set to true if generating a Controller that needs
|
||||
* access to the TemplateComponentGenerator ("generator") in
|
||||
* the Twig template. e.g. to create route attributes for a route method
|
||||
*
|
||||
* @return string The path where the file will be created
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
final public function generateClassFromClassData(ClassData $classData, string $templateName, array $variables = [], bool $isController = false): string
|
||||
{
|
||||
$classData = $this->templateComponentGenerator->configureClass($classData);
|
||||
$targetPath = $this->fileManager->getRelativePathForFutureClass($classData->getFullClassName());
|
||||
|
||||
if (null === $targetPath) {
|
||||
throw new \LogicException(\sprintf('Could not determine where to locate the new class "%s", maybe try with a full namespace like "My\\Full\\Namespace\\%s"', $classData->getFullClassName(), $classData->getClassName()));
|
||||
}
|
||||
|
||||
$globalTemplateVars = ['class_data' => $classData];
|
||||
|
||||
if ($isController) {
|
||||
$globalTemplateVars['generator'] = $this->templateComponentGenerator;
|
||||
}
|
||||
|
||||
$this->addOperation($targetPath, $templateName, array_merge($variables, $globalTemplateVars));
|
||||
|
||||
return $targetPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a normal file from a template.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function generateFile(string $targetPath, string $templateName, array $variables = [])
|
||||
{
|
||||
$variables = array_merge($variables, [
|
||||
'helper' => $this->twigHelper,
|
||||
]);
|
||||
|
||||
$this->addOperation($targetPath, $templateName, $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function dumpFile(string $targetPath, string $contents)
|
||||
{
|
||||
$this->pendingOperations[$targetPath] = [
|
||||
'contents' => $contents,
|
||||
];
|
||||
}
|
||||
|
||||
public function getFileContentsForPendingOperation(string $targetPath): string
|
||||
{
|
||||
if (!isset($this->pendingOperations[$targetPath])) {
|
||||
throw new RuntimeCommandException(\sprintf('File "%s" is not in the Generator\'s pending operations', $targetPath));
|
||||
}
|
||||
|
||||
$templatePath = $this->pendingOperations[$targetPath]['template'];
|
||||
$parameters = $this->pendingOperations[$targetPath]['variables'];
|
||||
|
||||
$templateParameters = array_merge($parameters, [
|
||||
'relative_path' => $this->fileManager->relativizePath($targetPath),
|
||||
]);
|
||||
|
||||
return $this->fileManager->parseTemplate($templatePath, $templateParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a helper object to get data about a class name.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* // App\Entity\FeaturedProduct
|
||||
* $gen->createClassNameDetails('FeaturedProduct', 'Entity');
|
||||
* $gen->createClassNameDetails('featured product', 'Entity');
|
||||
*
|
||||
* // App\Controller\FooController
|
||||
* $gen->createClassNameDetails('foo', 'Controller', 'Controller');
|
||||
*
|
||||
* // App\Controller\Foo\AdminController
|
||||
* $gen->createClassNameDetails('Foo\\Admin', 'Controller', 'Controller');
|
||||
*
|
||||
* // App\Security\Voter\CoolVoter
|
||||
* $gen->createClassNameDetails('Cool', 'Security\Voter', 'Voter');
|
||||
*
|
||||
* // Full class names can also be passed. Imagine the user has an autoload
|
||||
* // rule where Cool\Stuff lives in a "lib/" directory
|
||||
* // Cool\Stuff\BalloonController
|
||||
* $gen->createClassNameDetails('Cool\\Stuff\\Balloon', 'Controller', 'Controller');
|
||||
*
|
||||
* @param string $name The short "name" that will be turned into the class name
|
||||
* @param string $namespacePrefix Recommended namespace where this class should live, but *without* the "App\\" part
|
||||
* @param string $suffix Optional suffix to guarantee is on the end of the class
|
||||
*/
|
||||
public function createClassNameDetails(string $name, string $namespacePrefix, string $suffix = '', string $validationErrorMessage = ''): ClassNameDetails
|
||||
{
|
||||
$fullNamespacePrefix = $this->namespacePrefix.'\\'.$namespacePrefix;
|
||||
if ('\\' === $name[0]) {
|
||||
// class is already "absolute" - leave it alone (but strip opening \)
|
||||
$className = substr($name, 1);
|
||||
} else {
|
||||
$className = Str::asClassName($name, $suffix);
|
||||
|
||||
try {
|
||||
Validator::classDoesNotExist($className);
|
||||
$className = rtrim($fullNamespacePrefix, '\\').'\\'.$className;
|
||||
} catch (RuntimeCommandException) {
|
||||
}
|
||||
}
|
||||
|
||||
Validator::validateClassName($className, $validationErrorMessage);
|
||||
|
||||
// if this is a custom class, we may be completely different than the namespace prefix
|
||||
// the best way can do, is find the PSR4 prefix and use that
|
||||
if (!str_starts_with($className, $fullNamespacePrefix)) {
|
||||
$fullNamespacePrefix = $this->fileManager->getNamespacePrefixForClass($className);
|
||||
}
|
||||
|
||||
return new ClassNameDetails($className, $fullNamespacePrefix, $suffix);
|
||||
}
|
||||
|
||||
public function getRootDirectory(): string
|
||||
{
|
||||
return $this->fileManager->getRootDirectory();
|
||||
}
|
||||
|
||||
public function hasPendingOperations(): bool
|
||||
{
|
||||
return !empty($this->pendingOperations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually writes and file changes that are pending.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function writeChanges()
|
||||
{
|
||||
foreach ($this->pendingOperations as $targetPath => $templateData) {
|
||||
$this->generatedFiles[] = $targetPath;
|
||||
|
||||
if (isset($templateData['contents'])) {
|
||||
$this->fileManager->dumpFile($targetPath, $templateData['contents']);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->fileManager->dumpFile(
|
||||
$targetPath,
|
||||
$this->getFileContentsForPendingOperation($targetPath)
|
||||
);
|
||||
}
|
||||
|
||||
$this->pendingOperations = [];
|
||||
}
|
||||
|
||||
public function getRootNamespace(): string
|
||||
{
|
||||
return $this->namespacePrefix;
|
||||
}
|
||||
|
||||
public function generateController(string $controllerClassName, string $controllerTemplatePath, array $parameters = []): string
|
||||
{
|
||||
return $this->generateClass(
|
||||
$controllerClassName,
|
||||
$controllerTemplatePath,
|
||||
$parameters +
|
||||
[
|
||||
'generator' => $this->templateComponentGenerator,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a template file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function generateTemplate(string $targetPath, string $templateName, array $variables = [])
|
||||
{
|
||||
$this->generateFile(
|
||||
$this->fileManager->getPathForTemplate($targetPath),
|
||||
$templateName,
|
||||
$variables
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full path of each file created by the Generator.
|
||||
*/
|
||||
public function getGeneratedFiles(): array
|
||||
{
|
||||
return $this->generatedFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated MakerBundle only supports AbstractController::class. This method will be removed in the future.
|
||||
*/
|
||||
public static function getControllerBaseClass(): ClassNameDetails
|
||||
{
|
||||
trigger_deprecation('symfony/maker-bundle', 'v1.41.0', 'MakerBundle only supports AbstractController. This method will be removed in the future.');
|
||||
|
||||
return new ClassNameDetails(AbstractController::class, '\\');
|
||||
}
|
||||
|
||||
private function addOperation(string $targetPath, string $templateName, array $variables): void
|
||||
{
|
||||
if ($this->fileManager->fileExists($targetPath)) {
|
||||
throw new RuntimeCommandException(\sprintf('The file "%s" can\'t be generated because it already exists.', $this->fileManager->relativizePath($targetPath)));
|
||||
}
|
||||
|
||||
$variables['relative_path'] = $this->fileManager->relativizePath($targetPath);
|
||||
|
||||
$templatePath = $templateName;
|
||||
if (!file_exists($templatePath)) {
|
||||
$templatePath = \sprintf('%s/templates/%s', \dirname(__DIR__), $templateName);
|
||||
|
||||
if (!file_exists($templatePath)) {
|
||||
$templatePath = $this->getTemplateFromLegacySkeletonPath($templateName);
|
||||
}
|
||||
|
||||
if (!file_exists($templatePath)) {
|
||||
throw new \Exception(\sprintf('Cannot find template "%s" in the templates/ dir.', $templateName));
|
||||
}
|
||||
}
|
||||
|
||||
$this->pendingOperations[$targetPath] = [
|
||||
'template' => $templatePath,
|
||||
'variables' => $variables,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @legacy - Remove when public generate methods become "internal" to MakerBundle in v2
|
||||
*/
|
||||
private function getTemplateFromLegacySkeletonPath(string $templateName): string
|
||||
{
|
||||
$templatePath = $templateName;
|
||||
if (!file_exists($templatePath)) {
|
||||
$templatePath = __DIR__.'/Resources/skeleton/'.$templateName;
|
||||
|
||||
if (!file_exists($templatePath)) {
|
||||
throw new \Exception(\sprintf('Cannot find template "%s"', $templateName));
|
||||
}
|
||||
}
|
||||
|
||||
@trigger_deprecation(
|
||||
'symfony/maker-bundle',
|
||||
'1.62.0',
|
||||
'Storing templates in src/Resources/skeleton is deprecated. Store MakerBundle templates in the "~/templates/" dir instead.',
|
||||
);
|
||||
|
||||
return $templatePath;
|
||||
}
|
||||
}
|
||||
71
vendor/symfony/maker-bundle/src/GeneratorTwigHelper.php
vendored
Normal file
71
vendor/symfony/maker-bundle/src/GeneratorTwigHelper.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle;
|
||||
|
||||
/**
|
||||
* @author Sadicov Vladimir <sadikoff@gmail.com>
|
||||
*/
|
||||
final class GeneratorTwigHelper
|
||||
{
|
||||
public function __construct(
|
||||
private FileManager $fileManager,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getEntityFieldPrintCode($entity, $field): string
|
||||
{
|
||||
$twigField = preg_replace_callback('/(?!^)_([a-z0-9])/', static fn ($s) => strtoupper($s[1]), $field['fieldName']);
|
||||
$printCode = $entity.'.'.str_replace('_', '', $twigField);
|
||||
|
||||
match ($field['type']) {
|
||||
'datetimetz_immutable', 'datetimetz' => $printCode .= ' ? '.$printCode.'|date(\'Y-m-d H:i:s T\') : \'\'',
|
||||
'datetime_immutable', 'datetime' => $printCode .= ' ? '.$printCode.'|date(\'Y-m-d H:i:s\') : \'\'',
|
||||
'dateinterval' => $printCode .= ' ? '.$printCode.'.format(\'%y year(s), %m month(s), %d day(s)\') : \'\'',
|
||||
'date_immutable', 'date' => $printCode .= ' ? '.$printCode.'|date(\'Y-m-d\') : \'\'',
|
||||
'time_immutable', 'time' => $printCode .= ' ? '.$printCode.'|date(\'H:i:s\') : \'\'',
|
||||
'json' => $printCode .= ' ? '.$printCode.'|json_encode : \'\'',
|
||||
'array' => $printCode .= ' ? '.$printCode.'|join(\', \') : \'\'',
|
||||
'boolean' => $printCode .= ' ? \'Yes\' : \'No\'',
|
||||
default => $printCode,
|
||||
};
|
||||
|
||||
return $printCode;
|
||||
}
|
||||
|
||||
public function getHeadPrintCode($title): string
|
||||
{
|
||||
if ($this->fileManager->fileExists($this->fileManager->getPathForTemplate('base.html.twig'))) {
|
||||
return <<<TWIG
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}$title{% endblock %}
|
||||
|
||||
TWIG;
|
||||
}
|
||||
|
||||
return <<<HTML
|
||||
<!DOCTYPE html>
|
||||
|
||||
<title>$title</title>
|
||||
|
||||
HTML;
|
||||
}
|
||||
|
||||
public function getFileLink($path, $text = null, $line = 0): string
|
||||
{
|
||||
trigger_deprecation('symfony/maker-bundle', 'v1.53.0', 'getFileLink() is deprecated and will be removed in the future.');
|
||||
|
||||
$text = $text ?: $path;
|
||||
|
||||
return "<a href=\"{{ '$path'|file_link($line) }}\">$text</a>";
|
||||
}
|
||||
}
|
||||
24
vendor/symfony/maker-bundle/src/InputAwareMakerInterface.php
vendored
Normal file
24
vendor/symfony/maker-bundle/src/InputAwareMakerInterface.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
/**
|
||||
* Lets the configureDependencies method access to the command's input.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface InputAwareMakerInterface extends MakerInterface
|
||||
{
|
||||
public function configureDependencies(DependencyBuilder $dependencies, ?InputInterface $input = null);
|
||||
}
|
||||
31
vendor/symfony/maker-bundle/src/InputConfiguration.php
vendored
Normal file
31
vendor/symfony/maker-bundle/src/InputConfiguration.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle;
|
||||
|
||||
final class InputConfiguration
|
||||
{
|
||||
private array $nonInteractiveArguments = [];
|
||||
|
||||
/**
|
||||
* Call in MakerInterface::configureCommand() to disable the automatic interactive
|
||||
* prompt for an argument.
|
||||
*/
|
||||
public function setArgumentAsNonInteractive(string $argumentName): void
|
||||
{
|
||||
$this->nonInteractiveArguments[] = $argumentName;
|
||||
}
|
||||
|
||||
public function getNonInteractiveArguments(): array
|
||||
{
|
||||
return $this->nonInteractiveArguments;
|
||||
}
|
||||
}
|
||||
71
vendor/symfony/maker-bundle/src/Maker/AbstractMaker.php
vendored
Normal file
71
vendor/symfony/maker-bundle/src/Maker/AbstractMaker.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\MakerInterface;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
/**
|
||||
* Convenient abstract class for makers.
|
||||
*/
|
||||
abstract class AbstractMaker implements MakerInterface
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function writeSuccessMessage(ConsoleStyle $io)
|
||||
{
|
||||
$io->newLine();
|
||||
$io->writeln(' <bg=green;fg=white> </>');
|
||||
$io->writeln(' <bg=green;fg=white> Success! </>');
|
||||
$io->writeln(' <bg=green;fg=white> </>');
|
||||
$io->newLine();
|
||||
}
|
||||
|
||||
/** @param array<class-string, string> $dependencies */
|
||||
protected function addDependencies(array $dependencies, ?string $message = null): string
|
||||
{
|
||||
$dependencyBuilder = new DependencyBuilder();
|
||||
|
||||
foreach ($dependencies as $class => $name) {
|
||||
$dependencyBuilder->addClassDependency($class, $name);
|
||||
}
|
||||
|
||||
return $dependencyBuilder->getMissingPackagesMessage(
|
||||
static::getCommandName(),
|
||||
$message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the help file contents needed for "setHelp()" of a maker.
|
||||
*
|
||||
* @param string $helpFileName the filename (omit path) of the help file located in config/help/
|
||||
* e.g. MakeController.txt
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final protected function getHelpFileContents(string $helpFileName): string
|
||||
{
|
||||
return file_get_contents(\sprintf('%s/config/help/%s', \dirname(__DIR__, 2), $helpFileName));
|
||||
}
|
||||
}
|
||||
60
vendor/symfony/maker-bundle/src/Maker/Common/CanGenerateTestsTrait.php
vendored
Normal file
60
vendor/symfony/maker-bundle/src/Maker/Common/CanGenerateTestsTrait.php
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker\Common;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* @author Jesse Rushlow <jr@rushlow.dev>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait CanGenerateTestsTrait
|
||||
{
|
||||
private bool $generateTests = false;
|
||||
|
||||
public function configureCommandWithTestsOption(Command $command): Command
|
||||
{
|
||||
$testsHelp = file_get_contents(\dirname(__DIR__, 3).'/config/help/_WithTests.txt');
|
||||
$help = $command->getHelp()."\n".$testsHelp;
|
||||
|
||||
$command
|
||||
->addOption(name: 'with-tests', mode: InputOption::VALUE_NONE, description: 'Generate PHPUnit Tests')
|
||||
->setHelp($help)
|
||||
;
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
public function interactSetGenerateTests(InputInterface $input, ConsoleStyle $io): void
|
||||
{
|
||||
// Sanity check for maker dev's - End user should never see this.
|
||||
if (!$input->hasOption('with-tests')) {
|
||||
throw new RuntimeCommandException('Whoops! "--with-tests" option does not exist. Call "addWithTestsOptions()" in the makers "configureCommand().');
|
||||
}
|
||||
|
||||
$this->generateTests = $input->getOption('with-tests');
|
||||
|
||||
if (!$this->generateTests) {
|
||||
$this->generateTests = $io->confirm('Do you want to generate PHPUnit tests? [Experimental]', false);
|
||||
}
|
||||
}
|
||||
|
||||
public function shouldGenerateTests(): bool
|
||||
{
|
||||
return $this->generateTests;
|
||||
}
|
||||
}
|
||||
24
vendor/symfony/maker-bundle/src/Maker/Common/EntityIdTypeEnum.php
vendored
Normal file
24
vendor/symfony/maker-bundle/src/Maker/Common/EntityIdTypeEnum.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker\Common;
|
||||
|
||||
/**
|
||||
* @author Jesse Rushlow <jr@rushlow.dev>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
enum EntityIdTypeEnum
|
||||
{
|
||||
case INT;
|
||||
case UUID;
|
||||
case ULID;
|
||||
}
|
||||
42
vendor/symfony/maker-bundle/src/Maker/Common/InstallDependencyTrait.php
vendored
Normal file
42
vendor/symfony/maker-bundle/src/Maker/Common/InstallDependencyTrait.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker\Common;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* @author Jesse Rushlow <jr@rushlow.dev>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait InstallDependencyTrait
|
||||
{
|
||||
/**
|
||||
* @param string $composerPackage Fully qualified composer package to install e.g. symfony/maker-bundle
|
||||
*/
|
||||
public function installDependencyIfNeeded(ConsoleStyle $io, string $expectedClassToExist, string $composerPackage): ConsoleStyle
|
||||
{
|
||||
if (class_exists($expectedClassToExist)) {
|
||||
return $io;
|
||||
}
|
||||
|
||||
$io->writeln(\sprintf('Running: composer require %s', $composerPackage));
|
||||
|
||||
Process::fromShellCommandline(\sprintf('composer require %s', $composerPackage))->run();
|
||||
|
||||
$io->writeln(\sprintf('%s successfully installed!', $composerPackage));
|
||||
$io->newLine();
|
||||
|
||||
return $io;
|
||||
}
|
||||
}
|
||||
79
vendor/symfony/maker-bundle/src/Maker/Common/UidTrait.php
vendored
Normal file
79
vendor/symfony/maker-bundle/src/Maker/Common/UidTrait.php
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker\Common;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Uid\Ulid;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
/**
|
||||
* @author Jesse Rushlow<jr@rushlow.dev>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait UidTrait
|
||||
{
|
||||
private bool $usesUuid = false;
|
||||
private bool $usesUlid = false;
|
||||
|
||||
/**
|
||||
* Call this in a maker's configure() to consistently allow entity's with UUID's.
|
||||
* This should be called after you calling "setHelp()" in the maker.
|
||||
*/
|
||||
protected function addWithUuidOption(Command $command): Command
|
||||
{
|
||||
$uidHelp = file_get_contents(\dirname(__DIR__, 3).'/config/help/_WithUid.txt');
|
||||
$help = $command->getHelp()."\n".$uidHelp;
|
||||
|
||||
$command
|
||||
->addOption(name: 'with-uuid', mode: InputOption::VALUE_NONE, description: 'Use UUID for entity "id"')
|
||||
->addOption('with-ulid', mode: InputOption::VALUE_NONE, description: 'Use ULID for entity "id"')
|
||||
->setHelp($help)
|
||||
;
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this as early as possible in a maker's interact().
|
||||
*/
|
||||
protected function checkIsUsingUid(InputInterface $input): void
|
||||
{
|
||||
if (($this->usesUuid = $input->getOption('with-uuid')) && !class_exists(Uuid::class)) {
|
||||
throw new RuntimeCommandException('You must install symfony/uid to use Uuid\'s as "id" (composer require symfony/uid)');
|
||||
}
|
||||
|
||||
if (($this->usesUlid = $input->getOption('with-ulid')) && !class_exists(Ulid::class)) {
|
||||
throw new RuntimeCommandException('You must install symfony/uid to use Ulid\'s as "id" (composer require symfony/uid)');
|
||||
}
|
||||
|
||||
if ($this->usesUuid && $this->usesUlid) {
|
||||
throw new RuntimeCommandException('Setting --with-uuid & --with-ulid at the same time is not allowed. Please choose only one.');
|
||||
}
|
||||
}
|
||||
|
||||
protected function getIdType(): EntityIdTypeEnum
|
||||
{
|
||||
if ($this->usesUuid) {
|
||||
return EntityIdTypeEnum::UUID;
|
||||
}
|
||||
|
||||
if ($this->usesUlid) {
|
||||
return EntityIdTypeEnum::ULID;
|
||||
}
|
||||
|
||||
return EntityIdTypeEnum::INT;
|
||||
}
|
||||
}
|
||||
472
vendor/symfony/maker-bundle/src/Maker/MakeAuthenticator.php
vendored
Normal file
472
vendor/symfony/maker-bundle/src/Maker/MakeAuthenticator.php
vendored
Normal file
@@ -0,0 +1,472 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
|
||||
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
|
||||
use Symfony\Bundle\MakerBundle\FileManager;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Security\InteractiveSecurityHelper;
|
||||
use Symfony\Bundle\MakerBundle\Security\SecurityConfigUpdater;
|
||||
use Symfony\Bundle\MakerBundle\Security\SecurityControllerBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Bundle\MakerBundle\Util\YamlManipulationFailedException;
|
||||
use Symfony\Bundle\MakerBundle\Util\YamlSourceManipulator;
|
||||
use Symfony\Bundle\MakerBundle\Validator;
|
||||
use Symfony\Bundle\SecurityBundle\SecurityBundle;
|
||||
use Symfony\Bundle\TwigBundle\TwigBundle;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
|
||||
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||
use Symfony\Component\Security\Http\SecurityRequestAttributes;
|
||||
use Symfony\Component\Security\Http\Util\TargetPathTrait;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* @deprecated since MakerBundle v1.59.0, use any of the Security/Make* instead.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@symfonycasts.com>
|
||||
* @author Jesse Rushlow <jr@rushlow.dev>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class MakeAuthenticator extends AbstractMaker
|
||||
{
|
||||
private const AUTH_TYPE_EMPTY_AUTHENTICATOR = 'empty-authenticator';
|
||||
private const AUTH_TYPE_FORM_LOGIN = 'form-login';
|
||||
|
||||
private const REMEMBER_ME_TYPE_ALWAYS = 'always';
|
||||
private const REMEMBER_ME_TYPE_CHECKBOX = 'checkbox';
|
||||
|
||||
public function __construct(
|
||||
private FileManager $fileManager,
|
||||
private SecurityConfigUpdater $configUpdater,
|
||||
private Generator $generator,
|
||||
private DoctrineHelper $doctrineHelper,
|
||||
private SecurityControllerBuilder $securityControllerBuilder,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:auth';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a Guard authenticator of different flavors';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->setHelp($this->getHelpFileContents('MakeAuth.txt'))
|
||||
;
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
trigger_deprecation('symfony/maker-bundle', 'v1.59.0', 'The "%s" class is deprecated, use any of the Security\Make* commands instead.', self::class);
|
||||
|
||||
$io->caution('"make:auth" is deprecated, use any of the "make:security" commands instead.');
|
||||
|
||||
if (!$this->fileManager->fileExists($path = 'config/packages/security.yaml')) {
|
||||
throw new RuntimeCommandException('The file "config/packages/security.yaml" does not exist. PHP & XML configuration formats are currently not supported.');
|
||||
}
|
||||
$manipulator = new YamlSourceManipulator($this->fileManager->getFileContents($path));
|
||||
$securityData = $manipulator->getData();
|
||||
|
||||
// authenticator type
|
||||
$authenticatorTypeValues = [
|
||||
'Empty authenticator' => self::AUTH_TYPE_EMPTY_AUTHENTICATOR,
|
||||
'Login form authenticator' => self::AUTH_TYPE_FORM_LOGIN,
|
||||
];
|
||||
$command->addArgument('authenticator-type', InputArgument::REQUIRED);
|
||||
$authenticatorType = $io->choice(
|
||||
'What style of authentication do you want?',
|
||||
array_keys($authenticatorTypeValues),
|
||||
key($authenticatorTypeValues)
|
||||
);
|
||||
$input->setArgument(
|
||||
'authenticator-type',
|
||||
$authenticatorTypeValues[$authenticatorType]
|
||||
);
|
||||
|
||||
if (self::AUTH_TYPE_FORM_LOGIN === $input->getArgument('authenticator-type')) {
|
||||
$neededDependencies = [TwigBundle::class => 'twig'];
|
||||
$missingPackagesMessage = $this->addDependencies($neededDependencies, 'Twig must be installed to display the login form.');
|
||||
|
||||
if ($missingPackagesMessage) {
|
||||
throw new RuntimeCommandException($missingPackagesMessage);
|
||||
}
|
||||
|
||||
if (!isset($securityData['security']['providers']) || !$securityData['security']['providers']) {
|
||||
throw new RuntimeCommandException('To generate a form login authentication, you must configure at least one entry under "providers" in "security.yaml".');
|
||||
}
|
||||
}
|
||||
|
||||
// authenticator class
|
||||
$command->addArgument('authenticator-class', InputArgument::REQUIRED);
|
||||
$questionAuthenticatorClass = new Question('The class name of the authenticator to create (e.g. <fg=yellow>AppCustomAuthenticator</>)');
|
||||
$questionAuthenticatorClass->setValidator(
|
||||
function ($answer) {
|
||||
Validator::notBlank($answer);
|
||||
|
||||
return Validator::classDoesNotExist(
|
||||
$this->generator->createClassNameDetails($answer, 'Security\\', 'Authenticator')->getFullName()
|
||||
);
|
||||
}
|
||||
);
|
||||
$input->setArgument('authenticator-class', $io->askQuestion($questionAuthenticatorClass));
|
||||
|
||||
$interactiveSecurityHelper = new InteractiveSecurityHelper();
|
||||
$command->addOption('firewall-name', null, InputOption::VALUE_OPTIONAL);
|
||||
$input->setOption('firewall-name', $interactiveSecurityHelper->guessFirewallName($io, $securityData));
|
||||
|
||||
$command->addOption('entry-point', null, InputOption::VALUE_OPTIONAL);
|
||||
|
||||
if (self::AUTH_TYPE_FORM_LOGIN === $input->getArgument('authenticator-type')) {
|
||||
$command->addArgument('controller-class', InputArgument::REQUIRED);
|
||||
$input->setArgument(
|
||||
'controller-class',
|
||||
$io->ask(
|
||||
'Choose a name for the controller class (e.g. <fg=yellow>SecurityController</>)',
|
||||
'SecurityController',
|
||||
Validator::validateClassName(...)
|
||||
)
|
||||
);
|
||||
|
||||
$command->addArgument('user-class', InputArgument::REQUIRED);
|
||||
$input->setArgument(
|
||||
'user-class',
|
||||
$userClass = $interactiveSecurityHelper->guessUserClass($io, $securityData['security']['providers'])
|
||||
);
|
||||
|
||||
$command->addArgument('username-field', InputArgument::REQUIRED);
|
||||
$input->setArgument(
|
||||
'username-field',
|
||||
$interactiveSecurityHelper->guessUserNameField($io, $userClass, $securityData['security']['providers'])
|
||||
);
|
||||
|
||||
$command->addArgument('logout-setup', InputArgument::REQUIRED);
|
||||
$input->setArgument(
|
||||
'logout-setup',
|
||||
$io->confirm(
|
||||
'Do you want to generate a \'/logout\' URL?',
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
$command->addArgument('support-remember-me', InputArgument::REQUIRED);
|
||||
$input->setArgument(
|
||||
'support-remember-me',
|
||||
$io->confirm(
|
||||
'Do you want to support remember me?',
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
if ($input->getArgument('support-remember-me')) {
|
||||
$supportRememberMeValues = [
|
||||
'Activate when the user checks a box' => self::REMEMBER_ME_TYPE_CHECKBOX,
|
||||
'Always activate remember me' => self::REMEMBER_ME_TYPE_ALWAYS,
|
||||
];
|
||||
$command->addArgument('always-remember-me', InputArgument::REQUIRED);
|
||||
|
||||
$supportRememberMeType = $io->choice(
|
||||
'How should remember me be activated?',
|
||||
array_keys($supportRememberMeValues),
|
||||
key($supportRememberMeValues)
|
||||
);
|
||||
|
||||
$input->setArgument(
|
||||
'always-remember-me',
|
||||
$supportRememberMeValues[$supportRememberMeType]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$manipulator = new YamlSourceManipulator($this->fileManager->getFileContents('config/packages/security.yaml'));
|
||||
$securityData = $manipulator->getData();
|
||||
|
||||
$supportRememberMe = $input->hasArgument('support-remember-me') ? $input->getArgument('support-remember-me') : false;
|
||||
$alwaysRememberMe = $input->hasArgument('always-remember-me') && self::REMEMBER_ME_TYPE_ALWAYS === $input->getArgument('always-remember-me');
|
||||
|
||||
$this->generateAuthenticatorClass(
|
||||
$securityData,
|
||||
$input->getArgument('authenticator-type'),
|
||||
$input->getArgument('authenticator-class'),
|
||||
$input->hasArgument('user-class') ? $input->getArgument('user-class') : null,
|
||||
$input->hasArgument('username-field') ? $input->getArgument('username-field') : null,
|
||||
$supportRememberMe,
|
||||
);
|
||||
|
||||
// update security.yaml with guard config
|
||||
$securityYamlUpdated = false;
|
||||
|
||||
$entryPoint = $input->getOption('entry-point');
|
||||
|
||||
if (self::AUTH_TYPE_FORM_LOGIN !== $input->getArgument('authenticator-type')) {
|
||||
$entryPoint = false;
|
||||
}
|
||||
|
||||
try {
|
||||
$newYaml = $this->configUpdater->updateForAuthenticator(
|
||||
$this->fileManager->getFileContents($path = 'config/packages/security.yaml'),
|
||||
$input->getOption('firewall-name'),
|
||||
$entryPoint,
|
||||
$input->getArgument('authenticator-class'),
|
||||
$input->hasArgument('logout-setup') ? $input->getArgument('logout-setup') : false,
|
||||
$supportRememberMe,
|
||||
$alwaysRememberMe
|
||||
);
|
||||
$generator->dumpFile($path, $newYaml);
|
||||
$securityYamlUpdated = true;
|
||||
} catch (YamlManipulationFailedException) {
|
||||
}
|
||||
|
||||
if (self::AUTH_TYPE_FORM_LOGIN === $input->getArgument('authenticator-type')) {
|
||||
$this->generateFormLoginFiles(
|
||||
$input->getArgument('controller-class'),
|
||||
$input->getArgument('username-field'),
|
||||
$input->getArgument('logout-setup'),
|
||||
$supportRememberMe,
|
||||
$alwaysRememberMe,
|
||||
);
|
||||
}
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text(
|
||||
$this->generateNextMessage(
|
||||
$securityYamlUpdated,
|
||||
$input->getArgument('authenticator-type'),
|
||||
$input->getArgument('authenticator-class'),
|
||||
$input->hasArgument('user-class') ? $input->getArgument('user-class') : null,
|
||||
$input->hasArgument('logout-setup') ? $input->getArgument('logout-setup') : false,
|
||||
$supportRememberMe,
|
||||
$alwaysRememberMe
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/** @param array<mixed> $securityData */
|
||||
private function generateAuthenticatorClass(array $securityData, string $authenticatorType, string $authenticatorClass, ?string $userClass, ?string $userNameField, bool $supportRememberMe): void
|
||||
{
|
||||
$useStatements = new UseStatementGenerator([
|
||||
Request::class,
|
||||
Response::class,
|
||||
TokenInterface::class,
|
||||
Passport::class,
|
||||
]);
|
||||
|
||||
// generate authenticator class
|
||||
if (self::AUTH_TYPE_EMPTY_AUTHENTICATOR === $authenticatorType) {
|
||||
$useStatements->addUseStatement([
|
||||
AuthenticationException::class,
|
||||
AbstractAuthenticator::class,
|
||||
]);
|
||||
|
||||
$this->generator->generateClass(
|
||||
$authenticatorClass,
|
||||
'authenticator/EmptyAuthenticator.tpl.php',
|
||||
['use_statements' => $useStatements]
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$useStatements->addUseStatement([
|
||||
RedirectResponse::class,
|
||||
UrlGeneratorInterface::class,
|
||||
AbstractLoginFormAuthenticator::class,
|
||||
CsrfTokenBadge::class,
|
||||
UserBadge::class,
|
||||
PasswordCredentials::class,
|
||||
TargetPathTrait::class,
|
||||
SecurityRequestAttributes::class,
|
||||
]);
|
||||
|
||||
if ($supportRememberMe) {
|
||||
$useStatements->addUseStatement(RememberMeBadge::class);
|
||||
}
|
||||
|
||||
$userClassNameDetails = $this->generator->createClassNameDetails(
|
||||
'\\'.$userClass,
|
||||
'Entity\\'
|
||||
);
|
||||
|
||||
$this->generator->generateClass(
|
||||
$authenticatorClass,
|
||||
'authenticator/LoginFormAuthenticator.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'user_fully_qualified_class_name' => trim($userClassNameDetails->getFullName(), '\\'),
|
||||
'user_class_name' => $userClassNameDetails->getShortName(),
|
||||
'username_field' => $userNameField,
|
||||
'username_field_label' => Str::asHumanWords($userNameField),
|
||||
'username_field_var' => Str::asLowerCamelCase($userNameField),
|
||||
'user_needs_encoder' => $this->userClassHasEncoder($securityData, $userClass),
|
||||
'user_is_entity' => $this->doctrineHelper->isClassAMappedEntity($userClass),
|
||||
'remember_me_badge' => $supportRememberMe,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function generateFormLoginFiles(string $controllerClass, string $userNameField, bool $logoutSetup, bool $supportRememberMe, bool $alwaysRememberMe): void
|
||||
{
|
||||
$controllerClassNameDetails = $this->generator->createClassNameDetails(
|
||||
$controllerClass,
|
||||
'Controller\\',
|
||||
'Controller'
|
||||
);
|
||||
|
||||
if (!class_exists($controllerClassNameDetails->getFullName())) {
|
||||
$useStatements = new UseStatementGenerator([
|
||||
AbstractController::class,
|
||||
Route::class,
|
||||
AuthenticationUtils::class,
|
||||
]);
|
||||
|
||||
$controllerPath = $this->generator->generateController(
|
||||
$controllerClassNameDetails->getFullName(),
|
||||
'authenticator/EmptySecurityController.tpl.php',
|
||||
['use_statements' => $useStatements]
|
||||
);
|
||||
|
||||
$controllerSourceCode = $this->generator->getFileContentsForPendingOperation($controllerPath);
|
||||
} else {
|
||||
$controllerPath = $this->fileManager->getRelativePathForFutureClass($controllerClassNameDetails->getFullName());
|
||||
$controllerSourceCode = $this->fileManager->getFileContents($controllerPath);
|
||||
}
|
||||
|
||||
if (method_exists($controllerClassNameDetails->getFullName(), 'login')) {
|
||||
throw new RuntimeCommandException(\sprintf('Method "login" already exists on class %s', $controllerClassNameDetails->getFullName()));
|
||||
}
|
||||
|
||||
$manipulator = new ClassSourceManipulator(
|
||||
sourceCode: $controllerSourceCode,
|
||||
overwrite: true
|
||||
);
|
||||
|
||||
$this->securityControllerBuilder->addLoginMethod($manipulator);
|
||||
|
||||
if ($logoutSetup) {
|
||||
$this->securityControllerBuilder->addLogoutMethod($manipulator);
|
||||
}
|
||||
|
||||
$this->generator->dumpFile($controllerPath, $manipulator->getSourceCode());
|
||||
|
||||
// create login form template
|
||||
$this->generator->generateTemplate(
|
||||
'security/login.html.twig',
|
||||
'authenticator/login_form.tpl.php',
|
||||
[
|
||||
'username_field' => $userNameField,
|
||||
'username_is_email' => false !== stripos($userNameField, 'email'),
|
||||
'username_label' => ucfirst(Str::asHumanWords($userNameField)),
|
||||
'logout_setup' => $logoutSetup,
|
||||
'support_remember_me' => $supportRememberMe,
|
||||
'always_remember_me' => $alwaysRememberMe,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
private function generateNextMessage(bool $securityYamlUpdated, string $authenticatorType, string $authenticatorClass, ?string $userClass, bool $logoutSetup, bool $supportRememberMe, bool $alwaysRememberMe): array
|
||||
{
|
||||
$nextTexts = ['Next:'];
|
||||
$nextTexts[] = '- Customize your new authenticator.';
|
||||
|
||||
if (!$securityYamlUpdated) {
|
||||
$yamlExample = $this->configUpdater->updateForAuthenticator(
|
||||
'security: {}',
|
||||
'main',
|
||||
null,
|
||||
$authenticatorClass,
|
||||
$logoutSetup,
|
||||
$supportRememberMe,
|
||||
$alwaysRememberMe
|
||||
);
|
||||
$nextTexts[] = "- Your <info>security.yaml</info> could not be updated automatically. You'll need to add the following config manually:\n\n".$yamlExample;
|
||||
}
|
||||
|
||||
if (self::AUTH_TYPE_FORM_LOGIN === $authenticatorType) {
|
||||
$nextTexts[] = \sprintf('- Finish the redirect "TODO" in the <info>%s::onAuthenticationSuccess()</info> method.', $authenticatorClass);
|
||||
|
||||
if (!$this->doctrineHelper->isClassAMappedEntity($userClass)) {
|
||||
$nextTexts[] = \sprintf('- Review <info>%s::getUser()</info> to make sure it matches your needs.', $authenticatorClass);
|
||||
}
|
||||
|
||||
$nextTexts[] = '- Review & adapt the login template: <info>'.$this->fileManager->getPathForTemplate('security/login.html.twig').'</info>.';
|
||||
}
|
||||
|
||||
return $nextTexts;
|
||||
}
|
||||
|
||||
/** @param array<mixed> $securityData */
|
||||
private function userClassHasEncoder(array $securityData, string $userClass): bool
|
||||
{
|
||||
$userNeedsEncoder = false;
|
||||
$hashersData = $securityData['security']['encoders'] ?? [];
|
||||
|
||||
foreach ($hashersData as $userClassWithEncoder => $encoder) {
|
||||
if ($userClass === $userClassWithEncoder || is_subclass_of($userClass, $userClassWithEncoder) || class_implements($userClass, $userClassWithEncoder)) {
|
||||
$userNeedsEncoder = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $userNeedsEncoder;
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies, ?InputInterface $input = null): void
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
SecurityBundle::class,
|
||||
'security'
|
||||
);
|
||||
|
||||
// needed to update the YAML files
|
||||
$dependencies->addClassDependency(
|
||||
Yaml::class,
|
||||
'yaml'
|
||||
);
|
||||
}
|
||||
}
|
||||
113
vendor/symfony/maker-bundle/src/Maker/MakeCommand.php
vendored
Normal file
113
vendor/symfony/maker-bundle/src/Maker/MakeCommand.php
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Command\LazyCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*/
|
||||
final class MakeCommand extends AbstractMaker
|
||||
{
|
||||
public function __construct(private ?PhpCompatUtil $phpCompatUtil = null)
|
||||
{
|
||||
if (null !== $phpCompatUtil) {
|
||||
@trigger_deprecation(
|
||||
'symfony/maker-bundle',
|
||||
'1.55.0',
|
||||
\sprintf('Initializing MakeCommand while providing an instance of "%s" is deprecated. The $phpCompatUtil param will be removed in a future version.', PhpCompatUtil::class),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:command';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new console command class';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, \sprintf('Choose a command name (e.g. <fg=yellow>app:%s</>)', Str::asCommand(Str::getRandomTerm())))
|
||||
->setHelp($this->getHelpFileContents('MakeCommand.txt'))
|
||||
;
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$commandName = trim($input->getArgument('name'));
|
||||
$commandNameHasAppPrefix = str_starts_with($commandName, 'app:');
|
||||
|
||||
$commandClassNameDetails = $generator->createClassNameDetails(
|
||||
$commandNameHasAppPrefix ? substr($commandName, 4) : $commandName,
|
||||
'Command\\',
|
||||
'Command',
|
||||
\sprintf('The "%s" command name is not valid because it would be implemented by "%s" class, which is not valid as a PHP class name (it must start with a letter or underscore, followed by any number of letters, numbers, or underscores).', $commandName, Str::asClassName($commandName, 'Command'))
|
||||
);
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
Command::class,
|
||||
InputArgument::class,
|
||||
InputInterface::class,
|
||||
InputOption::class,
|
||||
OutputInterface::class,
|
||||
SymfonyStyle::class,
|
||||
AsCommand::class,
|
||||
]);
|
||||
|
||||
$generator->generateClass(
|
||||
$commandClassNameDetails->getFullName(),
|
||||
'command/Command.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'command_name' => $commandName,
|
||||
'set_description' => !class_exists(LazyCommand::class),
|
||||
]
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
$io->text([
|
||||
'Next: open your new command class and customize it!',
|
||||
'Find the documentation at <fg=yellow>https://symfony.com/doc/current/console.html</>',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
Command::class,
|
||||
'console'
|
||||
);
|
||||
}
|
||||
}
|
||||
170
vendor/symfony/maker-bundle/src/Maker/MakeController.php
vendored
Normal file
170
vendor/symfony/maker-bundle/src/Maker/MakeController.php
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Maker\Common\CanGenerateTestsTrait;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassData;
|
||||
use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil;
|
||||
use Symfony\Bundle\TwigBundle\TwigBundle;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
/**
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*/
|
||||
final class MakeController extends AbstractMaker
|
||||
{
|
||||
use CanGenerateTestsTrait;
|
||||
|
||||
private bool $isInvokable;
|
||||
private ClassData $controllerClassData;
|
||||
private bool $usesTwigTemplate;
|
||||
private string $twigTemplatePath;
|
||||
|
||||
public function __construct(private ?PhpCompatUtil $phpCompatUtil = null)
|
||||
{
|
||||
if (null !== $phpCompatUtil) {
|
||||
@trigger_deprecation(
|
||||
'symfony/maker-bundle',
|
||||
'1.55.0',
|
||||
\sprintf('Initializing MakeCommand while providing an instance of "%s" is deprecated. The $phpCompatUtil param will be removed in a future version.', PhpCompatUtil::class)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:controller';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new controller class';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('controller-class', InputArgument::OPTIONAL, \sprintf('Choose a name for your controller class (e.g. <fg=yellow>%sController</>)', Str::asClassName(Str::getRandomTerm())))
|
||||
->addOption('no-template', null, InputOption::VALUE_NONE, 'Use this option to disable template generation')
|
||||
->addOption('invokable', 'i', InputOption::VALUE_NONE, 'Use this option to create an invokable controller')
|
||||
->setHelp($this->getHelpFileContents('MakeController.txt'))
|
||||
;
|
||||
|
||||
$this->configureCommandWithTestsOption($command);
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
$this->usesTwigTemplate = $this->isTwigInstalled() && !$input->getOption('no-template');
|
||||
$this->isInvokable = (bool) $input->getOption('invokable');
|
||||
|
||||
$controllerClass = $input->getArgument('controller-class');
|
||||
$controllerClassName = \sprintf('Controller\%s', $controllerClass);
|
||||
|
||||
// If the class name provided is absolute, we do not assume it will live in src/Controller
|
||||
// e.g. src/Custom/Location/For/MyController instead of src/Controller/MyController
|
||||
if ($isAbsoluteNamespace = '\\' === $controllerClass[0]) {
|
||||
$controllerClassName = substr($controllerClass, 1);
|
||||
}
|
||||
|
||||
$this->controllerClassData = ClassData::create(
|
||||
class: $controllerClassName,
|
||||
suffix: 'Controller',
|
||||
extendsClass: AbstractController::class,
|
||||
useStatements: [
|
||||
$this->usesTwigTemplate ? Response::class : JsonResponse::class,
|
||||
Route::class,
|
||||
]
|
||||
);
|
||||
|
||||
// Again if the class name is absolute, lets not make assumptions about where the Twig template
|
||||
// should live. E.g. templates/custom/location/for/my_controller.html.twig instead of
|
||||
// templates/my/controller.html.twig. We do however remove the root_namespace prefix in either case
|
||||
// so we don't end up with templates/app/my/controller.html.twig
|
||||
$templateName = $isAbsoluteNamespace ?
|
||||
$this->controllerClassData->getFullClassName(withoutRootNamespace: true, withoutSuffix: true) :
|
||||
$this->controllerClassData->getClassName(relative: true, withoutSuffix: true)
|
||||
;
|
||||
|
||||
// Convert the Twig template name into a file path where it will be generated.
|
||||
$this->twigTemplatePath = \sprintf('%s%s', Str::asFilePath($templateName), $this->isInvokable ? '.html.twig' : '/index.html.twig');
|
||||
|
||||
$this->interactSetGenerateTests($input, $io);
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$controllerPath = $generator->generateClassFromClassData($this->controllerClassData, 'controller/Controller.tpl.php', [
|
||||
'route_path' => Str::asRoutePath($this->controllerClassData->getClassName(relative: true, withoutSuffix: true)),
|
||||
'route_name' => Str::AsRouteName($this->controllerClassData->getClassName(relative: true, withoutSuffix: true)),
|
||||
'method_name' => $this->isInvokable ? '__invoke' : 'index',
|
||||
'with_template' => $this->usesTwigTemplate,
|
||||
'template_name' => $this->twigTemplatePath,
|
||||
], true);
|
||||
|
||||
if ($this->usesTwigTemplate) {
|
||||
$generator->generateTemplate(
|
||||
$this->twigTemplatePath,
|
||||
'controller/twig_template.tpl.php',
|
||||
[
|
||||
'controller_path' => $controllerPath,
|
||||
'root_directory' => $generator->getRootDirectory(),
|
||||
'class_name' => $this->controllerClassData->getClassName(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->shouldGenerateTests()) {
|
||||
$testClassData = ClassData::create(
|
||||
class: \sprintf('Tests\Controller\%s', $this->controllerClassData->getClassName(relative: true, withoutSuffix: true)),
|
||||
suffix: 'ControllerTest',
|
||||
extendsClass: WebTestCase::class,
|
||||
);
|
||||
|
||||
$generator->generateClassFromClassData($testClassData, 'controller/test/Test.tpl.php', [
|
||||
'route_path' => Str::asRoutePath($this->controllerClassData->getClassName(relative: true, withoutSuffix: true)),
|
||||
]);
|
||||
|
||||
if (!class_exists(WebTestCase::class)) {
|
||||
$io->caution('You\'ll need to install the `symfony/test-pack` to execute the tests for your new controller.');
|
||||
}
|
||||
}
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
$io->text('Next: Open your new controller class and add some pages!');
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
}
|
||||
|
||||
private function isTwigInstalled(): bool
|
||||
{
|
||||
return class_exists(TwigBundle::class);
|
||||
}
|
||||
}
|
||||
327
vendor/symfony/maker-bundle/src/Maker/MakeCrud.php
vendored
Normal file
327
vendor/symfony/maker-bundle/src/Maker/MakeCrud.php
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
|
||||
use Doctrine\Inflector\Inflector;
|
||||
use Doctrine\Inflector\InflectorFactory;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Maker\Common\CanGenerateTestsTrait;
|
||||
use Symfony\Bundle\MakerBundle\Renderer\FormTypeRenderer;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassData;
|
||||
use Symfony\Bundle\MakerBundle\Validator;
|
||||
use Symfony\Bundle\TwigBundle\TwigBundle;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Csrf\CsrfTokenManager;
|
||||
use Symfony\Component\Validator\Validation;
|
||||
|
||||
/**
|
||||
* @author Sadicov Vladimir <sadikoff@gmail.com>
|
||||
*/
|
||||
final class MakeCrud extends AbstractMaker
|
||||
{
|
||||
use CanGenerateTestsTrait;
|
||||
|
||||
private Inflector $inflector;
|
||||
private string $controllerClassName;
|
||||
private bool $generateTests = false;
|
||||
|
||||
public function __construct(private DoctrineHelper $doctrineHelper, private FormTypeRenderer $formTypeRenderer)
|
||||
{
|
||||
$this->inflector = InflectorFactory::create()->build();
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:crud';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create CRUD for Doctrine entity class';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('entity-class', InputArgument::OPTIONAL, \sprintf('The class name of the entity to create CRUD (e.g. <fg=yellow>%s</>)', Str::asClassName(Str::getRandomTerm())))
|
||||
->setHelp($this->getHelpFileContents('MakeCrud.txt'))
|
||||
;
|
||||
|
||||
$inputConfig->setArgumentAsNonInteractive('entity-class');
|
||||
$this->configureCommandWithTestsOption($command);
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
if (null === $input->getArgument('entity-class')) {
|
||||
$argument = $command->getDefinition()->getArgument('entity-class');
|
||||
|
||||
$entities = $this->doctrineHelper->getEntitiesForAutocomplete();
|
||||
|
||||
$question = new Question($argument->getDescription());
|
||||
$question->setAutocompleterValues($entities);
|
||||
|
||||
$value = $io->askQuestion($question);
|
||||
|
||||
$input->setArgument('entity-class', $value);
|
||||
}
|
||||
|
||||
$defaultControllerClass = Str::asClassName(\sprintf('%s Controller', $input->getArgument('entity-class')));
|
||||
|
||||
$this->controllerClassName = $io->ask(
|
||||
\sprintf('Choose a name for your controller class (e.g. <fg=yellow>%s</>)', $defaultControllerClass),
|
||||
$defaultControllerClass
|
||||
);
|
||||
|
||||
$this->interactSetGenerateTests($input, $io);
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$entityClassDetails = $generator->createClassNameDetails(
|
||||
Validator::entityExists($input->getArgument('entity-class'), $this->doctrineHelper->getEntitiesForAutocomplete()),
|
||||
'Entity\\'
|
||||
);
|
||||
|
||||
$entityDoctrineDetails = $this->doctrineHelper->createDoctrineDetails($entityClassDetails->getFullName());
|
||||
|
||||
$repositoryVars = [];
|
||||
$repositoryClassName = EntityManagerInterface::class;
|
||||
|
||||
if (null !== $entityDoctrineDetails->getRepositoryClass()) {
|
||||
$repositoryClassDetails = $generator->createClassNameDetails(
|
||||
'\\'.$entityDoctrineDetails->getRepositoryClass(),
|
||||
'Repository\\',
|
||||
'Repository'
|
||||
);
|
||||
|
||||
$repositoryClassName = $repositoryClassDetails->getFullName();
|
||||
|
||||
$repositoryVars = [
|
||||
'repository_full_class_name' => $repositoryClassName,
|
||||
'repository_class_name' => $repositoryClassDetails->getShortName(),
|
||||
'repository_var' => lcfirst($this->inflector->singularize($repositoryClassDetails->getShortName())),
|
||||
];
|
||||
}
|
||||
|
||||
$controllerClassDetails = $generator->createClassNameDetails(
|
||||
$this->controllerClassName,
|
||||
'Controller\\',
|
||||
'Controller'
|
||||
);
|
||||
|
||||
$iter = 0;
|
||||
do {
|
||||
$formClassDetails = $generator->createClassNameDetails(
|
||||
$entityClassDetails->getRelativeNameWithoutSuffix().($iter ?: '').'Type',
|
||||
'Form\\',
|
||||
'Type'
|
||||
);
|
||||
++$iter;
|
||||
} while (class_exists($formClassDetails->getFullName()));
|
||||
|
||||
$controllerClassData = ClassData::create(
|
||||
class: \sprintf('Controller\%s', $this->controllerClassName),
|
||||
suffix: 'Controller',
|
||||
extendsClass: AbstractController::class,
|
||||
useStatements: [
|
||||
$entityClassDetails->getFullName(),
|
||||
$formClassDetails->getFullName(),
|
||||
$repositoryClassName,
|
||||
AbstractController::class,
|
||||
Request::class,
|
||||
Response::class,
|
||||
Route::class,
|
||||
],
|
||||
);
|
||||
|
||||
$entityVarPlural = lcfirst($this->inflector->pluralize($entityClassDetails->getShortName()));
|
||||
$entityVarSingular = lcfirst($this->inflector->singularize($entityClassDetails->getShortName()));
|
||||
|
||||
$entityTwigVarPlural = Str::asTwigVariable($entityVarPlural);
|
||||
$entityTwigVarSingular = Str::asTwigVariable($entityVarSingular);
|
||||
|
||||
$routeName = Str::asRouteName($controllerClassDetails->getRelativeNameWithoutSuffix());
|
||||
$templatesPath = Str::asFilePath($controllerClassDetails->getRelativeNameWithoutSuffix());
|
||||
|
||||
if (EntityManagerInterface::class !== $repositoryClassName) {
|
||||
$controllerClassData->addUseStatement(EntityManagerInterface::class);
|
||||
}
|
||||
|
||||
$generator->generateController(
|
||||
$controllerClassData->getFullClassName(),
|
||||
'crud/controller/Controller.tpl.php',
|
||||
array_merge([
|
||||
'class_data' => $controllerClassData,
|
||||
'entity_class_name' => $entityClassDetails->getShortName(),
|
||||
'form_class_name' => $formClassDetails->getShortName(),
|
||||
'route_path' => Str::asRoutePath($controllerClassDetails->getRelativeNameWithoutSuffix()),
|
||||
'route_name' => $routeName,
|
||||
'templates_path' => $templatesPath,
|
||||
'entity_var_plural' => $entityVarPlural,
|
||||
'entity_twig_var_plural' => $entityTwigVarPlural,
|
||||
'entity_var_singular' => $entityVarSingular,
|
||||
'entity_twig_var_singular' => $entityTwigVarSingular,
|
||||
'entity_identifier' => $entityDoctrineDetails->getIdentifier(),
|
||||
],
|
||||
$repositoryVars
|
||||
)
|
||||
);
|
||||
|
||||
$this->formTypeRenderer->render(
|
||||
$formClassDetails,
|
||||
$entityDoctrineDetails->getFormFields(),
|
||||
$entityClassDetails
|
||||
);
|
||||
|
||||
$templates = [
|
||||
'_delete_form' => [
|
||||
'route_name' => $routeName,
|
||||
'entity_twig_var_singular' => $entityTwigVarSingular,
|
||||
'entity_identifier' => $entityDoctrineDetails->getIdentifier(),
|
||||
],
|
||||
'_form' => [],
|
||||
'edit' => [
|
||||
'entity_class_name' => $entityClassDetails->getShortName(),
|
||||
'entity_twig_var_singular' => $entityTwigVarSingular,
|
||||
'entity_identifier' => $entityDoctrineDetails->getIdentifier(),
|
||||
'route_name' => $routeName,
|
||||
'templates_path' => $templatesPath,
|
||||
],
|
||||
'index' => [
|
||||
'entity_class_name' => $entityClassDetails->getShortName(),
|
||||
'entity_twig_var_plural' => $entityTwigVarPlural,
|
||||
'entity_twig_var_singular' => $entityTwigVarSingular,
|
||||
'entity_identifier' => $entityDoctrineDetails->getIdentifier(),
|
||||
'entity_fields' => $entityDoctrineDetails->getDisplayFields(),
|
||||
'route_name' => $routeName,
|
||||
],
|
||||
'new' => [
|
||||
'entity_class_name' => $entityClassDetails->getShortName(),
|
||||
'route_name' => $routeName,
|
||||
'templates_path' => $templatesPath,
|
||||
],
|
||||
'show' => [
|
||||
'entity_class_name' => $entityClassDetails->getShortName(),
|
||||
'entity_twig_var_singular' => $entityTwigVarSingular,
|
||||
'entity_identifier' => $entityDoctrineDetails->getIdentifier(),
|
||||
'entity_fields' => $entityDoctrineDetails->getDisplayFields(),
|
||||
'route_name' => $routeName,
|
||||
'templates_path' => $templatesPath,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($templates as $template => $variables) {
|
||||
$generator->generateTemplate(
|
||||
$templatesPath.'/'.$template.'.html.twig',
|
||||
'crud/templates/'.$template.'.tpl.php',
|
||||
$variables
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->shouldGenerateTests()) {
|
||||
$testClassData = ClassData::create(
|
||||
class: \sprintf('Tests\Controller\%s', $entityClassDetails->getRelativeNameWithoutSuffix()),
|
||||
suffix: 'ControllerTest',
|
||||
extendsClass: WebTestCase::class,
|
||||
useStatements: [
|
||||
$entityClassDetails->getFullName(),
|
||||
WebTestCase::class,
|
||||
KernelBrowser::class,
|
||||
$repositoryClassName,
|
||||
EntityRepository::class,
|
||||
],
|
||||
);
|
||||
|
||||
if (EntityManagerInterface::class !== $repositoryClassName) {
|
||||
$testClassData->addUseStatement(EntityManagerInterface::class);
|
||||
}
|
||||
|
||||
$generator->generateClass(
|
||||
$testClassData->getFullClassName(),
|
||||
'crud/test/Test.EntityManager.tpl.php',
|
||||
[
|
||||
'class_data' => $testClassData,
|
||||
'entity_full_class_name' => $entityClassDetails->getFullName(),
|
||||
'entity_class_name' => $entityClassDetails->getShortName(),
|
||||
'entity_var_singular' => $entityVarSingular,
|
||||
'route_path' => Str::asRoutePath($controllerClassDetails->getRelativeNameWithoutSuffix()),
|
||||
'route_name' => $routeName,
|
||||
'form_fields' => $entityDoctrineDetails->getFormFields(),
|
||||
'repository_class_name' => EntityManagerInterface::class,
|
||||
'form_field_prefix' => strtolower(Str::asSnakeCase($entityTwigVarSingular)),
|
||||
]
|
||||
);
|
||||
|
||||
if (!class_exists(WebTestCase::class)) {
|
||||
$io->caution('You\'ll need to install the `symfony/test-pack` to execute the tests for your new controller.');
|
||||
}
|
||||
}
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text(\sprintf('Next: Check your new CRUD by going to <fg=yellow>%s/</>', Str::asRoutePath($controllerClassDetails->getRelativeNameWithoutSuffix())));
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
Route::class,
|
||||
'router'
|
||||
);
|
||||
|
||||
$dependencies->addClassDependency(
|
||||
AbstractType::class,
|
||||
'form'
|
||||
);
|
||||
|
||||
$dependencies->addClassDependency(
|
||||
Validation::class,
|
||||
'validator'
|
||||
);
|
||||
|
||||
$dependencies->addClassDependency(
|
||||
TwigBundle::class,
|
||||
'twig-bundle'
|
||||
);
|
||||
|
||||
$dependencies->addClassDependency(
|
||||
DoctrineBundle::class,
|
||||
'orm'
|
||||
);
|
||||
|
||||
$dependencies->addClassDependency(
|
||||
CsrfTokenManager::class,
|
||||
'security-csrf'
|
||||
);
|
||||
}
|
||||
}
|
||||
200
vendor/symfony/maker-bundle/src/Maker/MakeDockerDatabase.php
vendored
Normal file
200
vendor/symfony/maker-bundle/src/Maker/MakeDockerDatabase.php
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Docker\DockerDatabaseServices;
|
||||
use Symfony\Bundle\MakerBundle\FileManager;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Util\ComposeFileManipulator;
|
||||
use Symfony\Bundle\MakerBundle\Validator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* @author Jesse Rushlow <jr@rushlow.dev>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class MakeDockerDatabase extends AbstractMaker
|
||||
{
|
||||
private string $composeFilePath;
|
||||
private ?ComposeFileManipulator $composeFileManipulator = null;
|
||||
|
||||
/**
|
||||
* @var ?string type of database selected by the user
|
||||
*/
|
||||
private ?string $databaseChoice = null;
|
||||
|
||||
/**
|
||||
* @var string Service identifier to be set in compose.yaml
|
||||
*/
|
||||
private string $serviceName = 'database';
|
||||
|
||||
/**
|
||||
* @var string Version set in compose.yaml for the service. e.g. latest
|
||||
*/
|
||||
private string $serviceVersion = 'latest';
|
||||
|
||||
public function __construct(private FileManager $fileManager)
|
||||
{
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:docker:database';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Add a database container to your compose.yaml file';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->setHelp($this->getHelpFileContents('MakeDockerDatabase.txt'))
|
||||
;
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
$io->section('- Docker Compose Setup-');
|
||||
|
||||
$this->composeFileManipulator = new ComposeFileManipulator($this->getComposeFileContents($io));
|
||||
|
||||
$io->newLine();
|
||||
|
||||
$this->databaseChoice = strtolower($io->choice(
|
||||
'Which database service will you be creating?',
|
||||
['MySQL', 'MariaDB', 'Postgres']
|
||||
));
|
||||
|
||||
$io->text([\sprintf(
|
||||
'For a list of supported versions, check out https://hub.docker.com/_/%s',
|
||||
$this->databaseChoice
|
||||
)]);
|
||||
|
||||
$this->serviceVersion = $io->ask('What version would you like to use?', DockerDatabaseServices::getSuggestedServiceVersion($this->databaseChoice));
|
||||
|
||||
if ($this->composeFileManipulator->serviceExists($this->serviceName)) {
|
||||
$io->comment(\sprintf('A <fg=yellow>"%s"</> service is already defined.', $this->serviceName));
|
||||
$io->newLine();
|
||||
|
||||
$serviceNameMsg[] = 'If you are using the Symfony Binary, it will expose the connection config for';
|
||||
$serviceNameMsg[] = 'this service as environment variables. The name of the service determines the';
|
||||
$serviceNameMsg[] = 'name of those environment variables.';
|
||||
$serviceNameMsg[] = '';
|
||||
$serviceNameMsg[] = 'For example, if you name the service <fg=yellow>database_alt</>, the binary will expose a';
|
||||
$serviceNameMsg[] = '<fg=yellow>DATABASE_ALT_URL</> environment variable.';
|
||||
|
||||
$io->text($serviceNameMsg);
|
||||
|
||||
$this->serviceName = $io->ask(\sprintf('What name should we call the new %s service? (e.g. <fg=yellow>database</>)', $this->serviceName), null, Validator::notBlank(...));
|
||||
}
|
||||
|
||||
$this->checkForPDOSupport($this->databaseChoice, $io);
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$io->newLine();
|
||||
|
||||
$service = DockerDatabaseServices::getDatabaseSkeleton($this->databaseChoice, $this->serviceVersion);
|
||||
|
||||
$this->composeFileManipulator->addDockerService($this->serviceName, $service);
|
||||
$this->composeFileManipulator->exposePorts($this->serviceName, DockerDatabaseServices::getDefaultPorts($this->databaseChoice));
|
||||
|
||||
$generator->dumpFile($this->composeFilePath, $this->composeFileManipulator->getDataString());
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text(\sprintf('The new <fg=yellow>"%s"</> service is now ready!', $this->serviceName));
|
||||
$io->newLine();
|
||||
|
||||
$ports = DockerDatabaseServices::getDefaultPorts($this->databaseChoice);
|
||||
$closing[] = 'Next:';
|
||||
$closing[] = \sprintf(' A) Run <fg=yellow>docker-compose up -d %s</> to start your database container', $this->serviceName);
|
||||
$closing[] = ' or <fg=yellow>docker-compose up -d</> to start all of them.';
|
||||
$closing[] = '';
|
||||
$closing[] = ' B) If you are using the Symfony Binary, it will detect the new service automatically.';
|
||||
$closing[] = ' Run <fg=yellow>symfony var:export --multiline</> to see the environment variables the binary is exposing.';
|
||||
$closing[] = ' These will override any values you have in your .env files.';
|
||||
$closing[] = '';
|
||||
$closing[] = ' C) Run <fg=yellow>docker-compose stop</> will stop all the containers in compose.yaml.';
|
||||
$closing[] = ' <fg=yellow>docker-compose down</> will stop and destroy the containers.';
|
||||
$closing[] = '';
|
||||
$closing[] = \sprintf(
|
||||
'Port%s %s will be exposed to %s random port%s on your host machine.',
|
||||
1 === \count($ports) ? '' : 's',
|
||||
implode(' ', $ports),
|
||||
1 === \count($ports) ? 'a' : '',
|
||||
1 === \count($ports) ? '' : 's'
|
||||
);
|
||||
|
||||
$io->text($closing);
|
||||
$io->newLine();
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
Yaml::class,
|
||||
'yaml'
|
||||
);
|
||||
}
|
||||
|
||||
private function checkForPDOSupport(string $databaseType, ConsoleStyle $io): void
|
||||
{
|
||||
$extension = DockerDatabaseServices::getMissingExtensionName($databaseType);
|
||||
|
||||
if (null !== $extension) {
|
||||
$io->note(
|
||||
\sprintf('Cannot find PHP\'s pdo_%s extension. Be sure it\'s installed & enabled to talk to the database.', $extension)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines and sets the correct Compose File Path and retrieves its contents
|
||||
* if the file exists else an empty string.
|
||||
*/
|
||||
private function getComposeFileContents(ConsoleStyle $io): string
|
||||
{
|
||||
$this->composeFilePath = \sprintf('%s/compose.yaml', $this->fileManager->getRootDirectory());
|
||||
|
||||
$composeFileExists = false;
|
||||
$statusMessage = 'Existing compose.yaml not found: a new one will be generated!';
|
||||
$contents = '';
|
||||
|
||||
foreach (['.yml', '.yaml'] as $extension) {
|
||||
$composeFilePath = \sprintf('%s/compose%s', $this->fileManager->getRootDirectory(), $extension);
|
||||
|
||||
if (!$composeFileExists && $this->fileManager->fileExists($composeFilePath)) {
|
||||
$composeFileExists = true;
|
||||
|
||||
$statusMessage = \sprintf('We found your existing compose%s: Let\'s update it!', $extension);
|
||||
|
||||
$this->composeFilePath = $composeFilePath;
|
||||
$contents = $this->fileManager->getFileContents($composeFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
$io->text($statusMessage);
|
||||
|
||||
return $contents;
|
||||
}
|
||||
}
|
||||
899
vendor/symfony/maker-bundle/src/Maker/MakeEntity.php
vendored
Normal file
899
vendor/symfony/maker-bundle/src/Maker/MakeEntity.php
vendored
Normal file
@@ -0,0 +1,899 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\EntityClassGenerator;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\EntityRegenerator;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\EntityRelation;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\ORMDependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
|
||||
use Symfony\Bundle\MakerBundle\FileManager;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputAwareMakerInterface;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Maker\Common\UidTrait;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassDetails;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassProperty;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator;
|
||||
use Symfony\Bundle\MakerBundle\Util\CliOutputHelper;
|
||||
use Symfony\Bundle\MakerBundle\Validator;
|
||||
use Symfony\Bundle\MercureBundle\DependencyInjection\MercureExtension;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Symfony\UX\Turbo\Attribute\Broadcast;
|
||||
|
||||
/**
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
final class MakeEntity extends AbstractMaker implements InputAwareMakerInterface
|
||||
{
|
||||
use UidTrait;
|
||||
|
||||
private Generator $generator;
|
||||
private EntityClassGenerator $entityClassGenerator;
|
||||
|
||||
public function __construct(
|
||||
private FileManager $fileManager,
|
||||
private DoctrineHelper $doctrineHelper,
|
||||
?string $projectDirectory = null,
|
||||
?Generator $generator = null,
|
||||
?EntityClassGenerator $entityClassGenerator = null,
|
||||
) {
|
||||
if (null !== $projectDirectory) {
|
||||
@trigger_error('The $projectDirectory constructor argument is no longer used since 1.41.0', \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if (null === $generator) {
|
||||
@trigger_error(\sprintf('Passing a "%s" instance as 4th argument is mandatory since version 1.5.', Generator::class), \E_USER_DEPRECATED);
|
||||
$this->generator = new Generator($fileManager, 'App\\');
|
||||
} else {
|
||||
$this->generator = $generator;
|
||||
}
|
||||
|
||||
if (null === $entityClassGenerator) {
|
||||
@trigger_error(\sprintf('Passing a "%s" instance as 5th argument is mandatory since version 1.15.1', EntityClassGenerator::class), \E_USER_DEPRECATED);
|
||||
$this->entityClassGenerator = new EntityClassGenerator($generator, $this->doctrineHelper);
|
||||
} else {
|
||||
$this->entityClassGenerator = $entityClassGenerator;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:entity';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create or update a Doctrine entity class, and optionally an API Platform resource';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, \sprintf('Class name of the entity to create or update (e.g. <fg=yellow>%s</>)', Str::asClassName(Str::getRandomTerm())))
|
||||
->addOption('api-resource', 'a', InputOption::VALUE_NONE, 'Mark this class as an API Platform resource (expose a CRUD API for it)')
|
||||
->addOption('broadcast', 'b', InputOption::VALUE_NONE, 'Add the ability to broadcast entity updates using Symfony UX Turbo?')
|
||||
->addOption('regenerate', null, InputOption::VALUE_NONE, 'Instead of adding new fields, simply generate the methods (e.g. getter/setter) for existing fields')
|
||||
->addOption('overwrite', null, InputOption::VALUE_NONE, 'Overwrite any existing getter/setter methods')
|
||||
->setHelp($this->getHelpFileContents('MakeEntity.txt'))
|
||||
;
|
||||
|
||||
$this->addWithUuidOption($command);
|
||||
|
||||
$inputConfig->setArgumentAsNonInteractive('name');
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
if (($entityClassName = $input->getArgument('name')) && empty($this->verifyEntityName($entityClassName))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($input->getOption('regenerate')) {
|
||||
$io->block([
|
||||
'This command will generate any missing methods (e.g. getters & setters) for a class or all classes in a namespace.',
|
||||
'To overwrite any existing methods, re-run this command with the --overwrite flag',
|
||||
], null, 'fg=yellow');
|
||||
$classOrNamespace = $io->ask('Enter a class or namespace to regenerate', $this->getEntityNamespace(), Validator::notBlank(...));
|
||||
|
||||
$input->setArgument('name', $classOrNamespace);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->checkIsUsingUid($input);
|
||||
|
||||
$argument = $command->getDefinition()->getArgument('name');
|
||||
$question = $this->createEntityClassQuestion($argument->getDescription());
|
||||
$entityClassName ??= $io->askQuestion($question);
|
||||
|
||||
while ($dangerous = $this->verifyEntityName($entityClassName)) {
|
||||
if ($io->confirm(\sprintf('"%s" contains one or more non-ASCII characters, which are potentially problematic with some database. It is recommended to use only ASCII characters for entity names. Continue anyway?', $entityClassName), false)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$entityClassName = $io->askQuestion($question);
|
||||
}
|
||||
|
||||
$input->setArgument('name', $entityClassName);
|
||||
|
||||
if (
|
||||
!$input->getOption('api-resource')
|
||||
&& class_exists(ApiResource::class)
|
||||
&& !class_exists($this->generator->createClassNameDetails($entityClassName, 'Entity\\')->getFullName())
|
||||
) {
|
||||
$description = $command->getDefinition()->getOption('api-resource')->getDescription();
|
||||
$question = new ConfirmationQuestion($description, false);
|
||||
$isApiResource = $io->askQuestion($question);
|
||||
|
||||
$input->setOption('api-resource', $isApiResource);
|
||||
}
|
||||
|
||||
if (
|
||||
!$input->getOption('broadcast')
|
||||
&& class_exists(Broadcast::class)
|
||||
&& !class_exists($this->generator->createClassNameDetails($entityClassName, 'Entity\\')->getFullName())
|
||||
) {
|
||||
$description = $command->getDefinition()->getOption('broadcast')->getDescription();
|
||||
$question = new ConfirmationQuestion($description, false);
|
||||
$isBroadcast = $io->askQuestion($question);
|
||||
|
||||
// Mercure is needed
|
||||
if ($isBroadcast && !class_exists(MercureExtension::class)) {
|
||||
throw new RuntimeCommandException('Please run "composer require symfony/mercure-bundle". It is needed to broadcast entities.');
|
||||
}
|
||||
|
||||
$input->setOption('broadcast', $isBroadcast);
|
||||
}
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$overwrite = $input->getOption('overwrite');
|
||||
|
||||
// the regenerate option has entirely custom behavior
|
||||
if ($input->getOption('regenerate')) {
|
||||
$this->regenerateEntities($input->getArgument('name'), $overwrite, $generator);
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$entityClassDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('name'),
|
||||
'Entity\\'
|
||||
);
|
||||
|
||||
$classExists = class_exists($entityClassDetails->getFullName());
|
||||
if (!$classExists) {
|
||||
$broadcast = $input->getOption('broadcast');
|
||||
$entityPath = $this->entityClassGenerator->generateEntityClass(
|
||||
entityClassDetails: $entityClassDetails,
|
||||
apiResource: $input->getOption('api-resource'),
|
||||
broadcast: $broadcast,
|
||||
useUuidIdentifier: $this->getIdType(),
|
||||
);
|
||||
|
||||
if ($broadcast) {
|
||||
$shortName = $entityClassDetails->getShortName();
|
||||
$generator->generateTemplate(
|
||||
\sprintf('broadcast/%s.stream.html.twig', $shortName),
|
||||
'doctrine/broadcast_twig_template.tpl.php',
|
||||
[
|
||||
'class_name' => Str::asSnakeCase($shortName),
|
||||
'class_name_plural' => Str::asSnakeCase(Str::singularCamelCaseToPluralCamelCase($shortName)),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$generator->writeChanges();
|
||||
}
|
||||
|
||||
if ($classExists) {
|
||||
$entityPath = $this->getPathOfClass($entityClassDetails->getFullName());
|
||||
$io->text([
|
||||
'Your entity already exists! So let\'s add some new fields!',
|
||||
]);
|
||||
} else {
|
||||
$io->text([
|
||||
'',
|
||||
'Entity generated! Now let\'s add some fields!',
|
||||
'You can always add more fields later manually or by re-running this command.',
|
||||
]);
|
||||
}
|
||||
|
||||
$currentFields = $this->getPropertyNames($entityClassDetails->getFullName());
|
||||
$manipulator = $this->createClassManipulator($entityPath, $io, $overwrite);
|
||||
|
||||
$isFirstField = true;
|
||||
while (true) {
|
||||
$newField = $this->askForNextField($io, $currentFields, $entityClassDetails->getFullName(), $isFirstField);
|
||||
$isFirstField = false;
|
||||
|
||||
if (null === $newField) {
|
||||
break;
|
||||
}
|
||||
|
||||
$fileManagerOperations = [];
|
||||
$fileManagerOperations[$entityPath] = $manipulator;
|
||||
|
||||
if ($newField instanceof ClassProperty) {
|
||||
$manipulator->addEntityField($newField);
|
||||
|
||||
$currentFields[] = $newField->propertyName;
|
||||
} elseif ($newField instanceof EntityRelation) {
|
||||
// both overridden below for OneToMany
|
||||
$newFieldName = $newField->getOwningProperty();
|
||||
if ($newField->isSelfReferencing()) {
|
||||
$otherManipulatorFilename = $entityPath;
|
||||
$otherManipulator = $manipulator;
|
||||
} else {
|
||||
$otherManipulatorFilename = $this->getPathOfClass($newField->getInverseClass());
|
||||
$otherManipulator = $this->createClassManipulator($otherManipulatorFilename, $io, $overwrite);
|
||||
}
|
||||
switch ($newField->getType()) {
|
||||
case EntityRelation::MANY_TO_ONE:
|
||||
if ($newField->getOwningClass() === $entityClassDetails->getFullName()) {
|
||||
// THIS class will receive the ManyToOne
|
||||
$manipulator->addManyToOneRelation($newField->getOwningRelation());
|
||||
|
||||
if ($newField->getMapInverseRelation()) {
|
||||
$otherManipulator->addOneToManyRelation($newField->getInverseRelation());
|
||||
}
|
||||
} else {
|
||||
// the new field being added to THIS entity is the inverse
|
||||
$newFieldName = $newField->getInverseProperty();
|
||||
$otherManipulatorFilename = $this->getPathOfClass($newField->getOwningClass());
|
||||
$otherManipulator = $this->createClassManipulator($otherManipulatorFilename, $io, $overwrite);
|
||||
|
||||
// The *other* class will receive the ManyToOne
|
||||
$otherManipulator->addManyToOneRelation($newField->getOwningRelation());
|
||||
if (!$newField->getMapInverseRelation()) {
|
||||
throw new \Exception('Somehow a OneToMany relationship is being created, but the inverse side will not be mapped?');
|
||||
}
|
||||
$manipulator->addOneToManyRelation($newField->getInverseRelation());
|
||||
}
|
||||
|
||||
break;
|
||||
case EntityRelation::MANY_TO_MANY:
|
||||
$manipulator->addManyToManyRelation($newField->getOwningRelation());
|
||||
if ($newField->getMapInverseRelation()) {
|
||||
$otherManipulator->addManyToManyRelation($newField->getInverseRelation());
|
||||
}
|
||||
|
||||
break;
|
||||
case EntityRelation::ONE_TO_ONE:
|
||||
$manipulator->addOneToOneRelation($newField->getOwningRelation());
|
||||
if ($newField->getMapInverseRelation()) {
|
||||
$otherManipulator->addOneToOneRelation($newField->getInverseRelation());
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new \Exception('Invalid relation type');
|
||||
}
|
||||
|
||||
// save the inverse side if it's being mapped
|
||||
if ($newField->getMapInverseRelation()) {
|
||||
$fileManagerOperations[$otherManipulatorFilename] = $otherManipulator;
|
||||
}
|
||||
$currentFields[] = $newFieldName;
|
||||
} else {
|
||||
throw new \Exception('Invalid value');
|
||||
}
|
||||
|
||||
foreach ($fileManagerOperations as $path => $manipulatorOrMessage) {
|
||||
if (\is_string($manipulatorOrMessage)) { /* @phpstan-ignore-line - https://github.com/symfony/maker-bundle/issues/1509 */
|
||||
$io->comment($manipulatorOrMessage);
|
||||
} else {
|
||||
$this->fileManager->dumpFile($path, $manipulatorOrMessage->getSourceCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
$io->text([
|
||||
\sprintf('Next: When you\'re ready, create a migration with <info>%s make:migration</info>', CliOutputHelper::getCommandPrefix()),
|
||||
'',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies, ?InputInterface $input = null): void
|
||||
{
|
||||
if (null !== $input && $input->getOption('api-resource')) {
|
||||
$dependencies->addClassDependency(
|
||||
ApiResource::class,
|
||||
'api'
|
||||
);
|
||||
}
|
||||
|
||||
if (null !== $input && $input->getOption('broadcast')) {
|
||||
$dependencies->addClassDependency(
|
||||
Broadcast::class,
|
||||
'symfony/ux-turbo'
|
||||
);
|
||||
}
|
||||
|
||||
ORMDependencyBuilder::buildDependencies($dependencies);
|
||||
}
|
||||
|
||||
/** @param string[] $fields */
|
||||
private function askForNextField(ConsoleStyle $io, array $fields, string $entityClass, bool $isFirstField): EntityRelation|ClassProperty|null
|
||||
{
|
||||
$io->writeln('');
|
||||
|
||||
if ($isFirstField) {
|
||||
$questionText = 'New property name (press <return> to stop adding fields)';
|
||||
} else {
|
||||
$questionText = 'Add another property? Enter the property name (or press <return> to stop adding fields)';
|
||||
}
|
||||
|
||||
$fieldName = $io->ask($questionText, null, function ($name) use ($fields) {
|
||||
// allow it to be empty
|
||||
if (!$name) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
if (\in_array($name, $fields)) {
|
||||
throw new \InvalidArgumentException(\sprintf('The "%s" property already exists.', $name));
|
||||
}
|
||||
|
||||
return Validator::validateDoctrineFieldName($name, $this->doctrineHelper->getRegistry());
|
||||
});
|
||||
|
||||
if (!$fieldName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$defaultType = 'string';
|
||||
// try to guess the type by the field name prefix/suffix
|
||||
// convert to snake case for simplicity
|
||||
$snakeCasedField = Str::asSnakeCase($fieldName);
|
||||
|
||||
if ('_at' === $suffix = substr($snakeCasedField, -3)) {
|
||||
$defaultType = 'datetime_immutable';
|
||||
} elseif ('_id' === $suffix) {
|
||||
$defaultType = 'integer';
|
||||
} elseif (str_starts_with($snakeCasedField, 'is_')) {
|
||||
$defaultType = 'boolean';
|
||||
} elseif (str_starts_with($snakeCasedField, 'has_')) {
|
||||
$defaultType = 'boolean';
|
||||
} elseif ('uuid' === $snakeCasedField) {
|
||||
$defaultType = Type::hasType('uuid') ? 'uuid' : 'guid';
|
||||
} elseif ('guid' === $snakeCasedField) {
|
||||
$defaultType = 'guid';
|
||||
}
|
||||
|
||||
$type = null;
|
||||
$types = $this->getTypesMap();
|
||||
|
||||
$allValidTypes = array_merge(
|
||||
array_keys($types),
|
||||
EntityRelation::getValidRelationTypes(),
|
||||
['relation', 'enum']
|
||||
);
|
||||
while (null === $type) {
|
||||
$question = new Question('Field type (enter <comment>?</comment> to see all types)', $defaultType);
|
||||
$question->setAutocompleterValues($allValidTypes);
|
||||
$type = $io->askQuestion($question);
|
||||
|
||||
if ('?' === $type) {
|
||||
$this->printAvailableTypes($io);
|
||||
$io->writeln('');
|
||||
|
||||
$type = null;
|
||||
} elseif (!\in_array($type, $allValidTypes)) {
|
||||
$this->printAvailableTypes($io);
|
||||
$io->error(\sprintf('Invalid type "%s".', $type));
|
||||
$io->writeln('');
|
||||
|
||||
$type = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ('relation' === $type || \in_array($type, EntityRelation::getValidRelationTypes())) {
|
||||
return $this->askRelationDetails($io, $entityClass, $type, $fieldName);
|
||||
}
|
||||
|
||||
// this is a normal field
|
||||
$classProperty = new ClassProperty(propertyName: $fieldName, type: $type);
|
||||
|
||||
if ('string' === $type) {
|
||||
// default to 255, avoid the question
|
||||
$classProperty->length = $io->ask('Field length', '255', Validator::validateLength(...));
|
||||
} elseif ('decimal' === $type) {
|
||||
// 10 is the default value given in \Doctrine\DBAL\Schema\Column::$_precision
|
||||
$classProperty->precision = $io->ask('Precision (total number of digits stored: 100.00 would be 5)', '10', Validator::validatePrecision(...));
|
||||
|
||||
// 0 is the default value given in \Doctrine\DBAL\Schema\Column::$_scale
|
||||
$classProperty->scale = $io->ask('Scale (number of decimals to store: 100.00 would be 2)', '0', Validator::validateScale(...));
|
||||
} elseif ('enum' === $type) {
|
||||
// ask for valid backed enum class
|
||||
$classProperty->enumType = $io->ask('Enum class', null, Validator::classIsBackedEnum(...));
|
||||
|
||||
// set type according to user decision
|
||||
$classProperty->type = $io->confirm('Can this field store multiple enum values', false) ? 'simple_array' : 'string';
|
||||
}
|
||||
|
||||
if ($io->confirm('Can this field be null in the database (nullable)', false)) {
|
||||
$classProperty->nullable = true;
|
||||
}
|
||||
|
||||
return $classProperty;
|
||||
}
|
||||
|
||||
private function printAvailableTypes(ConsoleStyle $io): void
|
||||
{
|
||||
$allTypes = $this->getTypesMap();
|
||||
|
||||
$typesTable = [
|
||||
'main' => [
|
||||
'string' => ['ascii_string'],
|
||||
'text' => [],
|
||||
'boolean' => [],
|
||||
'integer' => ['smallint', 'bigint'],
|
||||
'float' => [],
|
||||
],
|
||||
'array_object' => [
|
||||
'array' => ['simple_array'],
|
||||
'json' => [],
|
||||
'object' => [],
|
||||
'binary' => [],
|
||||
'blob' => [],
|
||||
],
|
||||
'date_time' => [
|
||||
'datetime' => ['datetime_immutable'],
|
||||
'datetimetz' => ['datetimetz_immutable'],
|
||||
'date' => ['date_immutable'],
|
||||
'time' => ['time_immutable'],
|
||||
'dateinterval' => [],
|
||||
],
|
||||
'other' => [
|
||||
'enum' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$printSection = static function (array $sectionTypes) use ($io, &$allTypes) {
|
||||
foreach ($sectionTypes as $mainType => $subTypes) {
|
||||
if (!\array_key_exists($mainType, $allTypes)) {
|
||||
// The type is not a valid DBAL Type - don't show it as an option
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($subTypes as $key => $potentialType) {
|
||||
if (!\array_key_exists($potentialType, $allTypes)) {
|
||||
// The type is not a valid DBAL Type - don't show it as an "or" option
|
||||
unset($subTypes[$key]);
|
||||
}
|
||||
|
||||
// Remove type as not to show it again in "Other Types"
|
||||
unset($allTypes[$potentialType]);
|
||||
}
|
||||
|
||||
// Remove type as not to show it again in "Other Types"
|
||||
unset($allTypes[$mainType]);
|
||||
|
||||
$line = \sprintf(' * <comment>%s</comment>', $mainType);
|
||||
|
||||
if (!empty($subTypes)) {
|
||||
$line .= \sprintf(' or %s', implode(' or ', array_map(
|
||||
static fn ($subType) => \sprintf('<comment>%s</comment>', $subType), $subTypes))
|
||||
);
|
||||
}
|
||||
|
||||
$io->writeln($line);
|
||||
}
|
||||
|
||||
$io->writeln('');
|
||||
};
|
||||
|
||||
$printRelationsSection = static function () use ($io) {
|
||||
if ('Hyper' === getenv('TERM_PROGRAM')) {
|
||||
$wizard = 'wizard 🧙';
|
||||
} else {
|
||||
$wizard = '\\' === \DIRECTORY_SEPARATOR ? 'wizard' : 'wizard 🧙';
|
||||
}
|
||||
|
||||
$io->writeln(\sprintf(' * <comment>relation</comment> a %s will help you build the relation', $wizard));
|
||||
|
||||
$relations = [EntityRelation::MANY_TO_ONE, EntityRelation::ONE_TO_MANY, EntityRelation::MANY_TO_MANY, EntityRelation::ONE_TO_ONE];
|
||||
foreach ($relations as $relation) {
|
||||
$line = \sprintf(' * <comment>%s</comment>', $relation);
|
||||
|
||||
$io->writeln($line);
|
||||
}
|
||||
|
||||
$io->writeln('');
|
||||
};
|
||||
|
||||
$io->writeln('<info>Main Types</info>');
|
||||
$printSection($typesTable['main']);
|
||||
|
||||
$io->writeln('<info>Relationships/Associations</info>');
|
||||
$printRelationsSection();
|
||||
|
||||
$io->writeln('<info>Array/Object Types</info>');
|
||||
$printSection($typesTable['array_object']);
|
||||
|
||||
$io->writeln('<info>Date/Time Types</info>');
|
||||
$printSection($typesTable['date_time']);
|
||||
|
||||
$io->writeln('<info>Other Types</info>');
|
||||
// empty the values
|
||||
$allTypes = array_map(static fn () => [], $allTypes);
|
||||
$allTypes = [...$typesTable['other'], ...$allTypes];
|
||||
$printSection($allTypes);
|
||||
}
|
||||
|
||||
private function createEntityClassQuestion(string $questionText): Question
|
||||
{
|
||||
$question = new Question($questionText);
|
||||
$question->setValidator(Validator::notBlank(...));
|
||||
$question->setAutocompleterValues($this->doctrineHelper->getEntitiesForAutocomplete());
|
||||
|
||||
return $question;
|
||||
}
|
||||
|
||||
private function askRelationDetails(ConsoleStyle $io, string $generatedEntityClass, string $type, string $newFieldName): EntityRelation
|
||||
{
|
||||
// ask the targetEntity
|
||||
$targetEntityClass = null;
|
||||
while (null === $targetEntityClass) {
|
||||
$question = $this->createEntityClassQuestion('What class should this entity be related to?');
|
||||
|
||||
$answeredEntityClass = $io->askQuestion($question);
|
||||
|
||||
// find the correct class name - but give priority over looking
|
||||
// in the Entity namespace versus just checking the full class
|
||||
// name to avoid issues with classes like "Directory" that exist
|
||||
// in PHP's core.
|
||||
if (class_exists($this->getEntityNamespace().'\\'.$answeredEntityClass)) {
|
||||
$targetEntityClass = $this->getEntityNamespace().'\\'.$answeredEntityClass;
|
||||
} elseif (class_exists($answeredEntityClass)) {
|
||||
$targetEntityClass = $answeredEntityClass;
|
||||
} else {
|
||||
$io->error(\sprintf('Unknown class "%s"', $answeredEntityClass));
|
||||
}
|
||||
}
|
||||
|
||||
// help the user select the type
|
||||
if ('relation' === $type) {
|
||||
$type = $this->askRelationType($io, $generatedEntityClass, $targetEntityClass);
|
||||
}
|
||||
|
||||
$askFieldName = fn (string $targetClass, string $defaultValue) => $io->ask(
|
||||
\sprintf('New field name inside %s', Str::getShortClassName($targetClass)),
|
||||
$defaultValue,
|
||||
function ($name) use ($targetClass) {
|
||||
// it's still *possible* to create duplicate properties - by
|
||||
// trying to generate the same property 2 times during the
|
||||
// same make:entity run. property_exists() only knows about
|
||||
// properties that *originally* existed on this class.
|
||||
if (property_exists($targetClass, $name)) {
|
||||
throw new \InvalidArgumentException(\sprintf('The "%s" class already has a "%s" property.', $targetClass, $name));
|
||||
}
|
||||
|
||||
return Validator::validateDoctrineFieldName($name, $this->doctrineHelper->getRegistry());
|
||||
}
|
||||
);
|
||||
|
||||
$askIsNullable = static fn (string $propertyName, string $targetClass) => $io->confirm(\sprintf(
|
||||
'Is the <comment>%s</comment>.<comment>%s</comment> property allowed to be null (nullable)?',
|
||||
Str::getShortClassName($targetClass),
|
||||
$propertyName
|
||||
));
|
||||
|
||||
$askOrphanRemoval = static function (string $owningClass, string $inverseClass) use ($io) {
|
||||
$io->text([
|
||||
'Do you want to activate <comment>orphanRemoval</comment> on your relationship?',
|
||||
\sprintf(
|
||||
'A <comment>%s</comment> is "orphaned" when it is removed from its related <comment>%s</comment>.',
|
||||
Str::getShortClassName($owningClass),
|
||||
Str::getShortClassName($inverseClass)
|
||||
),
|
||||
\sprintf(
|
||||
'e.g. <comment>$%s->remove%s($%s)</comment>',
|
||||
Str::asLowerCamelCase(Str::getShortClassName($inverseClass)),
|
||||
Str::asCamelCase(Str::getShortClassName($owningClass)),
|
||||
Str::asLowerCamelCase(Str::getShortClassName($owningClass))
|
||||
),
|
||||
'',
|
||||
\sprintf(
|
||||
'NOTE: If a <comment>%s</comment> may *change* from one <comment>%s</comment> to another, answer "no".',
|
||||
Str::getShortClassName($owningClass),
|
||||
Str::getShortClassName($inverseClass)
|
||||
),
|
||||
]);
|
||||
|
||||
return $io->confirm(\sprintf('Do you want to automatically delete orphaned <comment>%s</comment> objects (orphanRemoval)?', $owningClass), false);
|
||||
};
|
||||
|
||||
$askInverseSide = function (EntityRelation $relation) use ($io) {
|
||||
if ($this->isClassInVendor($relation->getInverseClass())) {
|
||||
$relation->setMapInverseRelation(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// recommend an inverse side, except for OneToOne, where it's inefficient
|
||||
$recommendMappingInverse = EntityRelation::ONE_TO_ONE !== $relation->getType();
|
||||
|
||||
$getterMethodName = 'get'.Str::asCamelCase(Str::getShortClassName($relation->getOwningClass()));
|
||||
if (EntityRelation::ONE_TO_ONE !== $relation->getType()) {
|
||||
// pluralize!
|
||||
$getterMethodName = Str::singularCamelCaseToPluralCamelCase($getterMethodName);
|
||||
}
|
||||
$mapInverse = $io->confirm(
|
||||
\sprintf(
|
||||
'Do you want to add a new property to <comment>%s</comment> so that you can access/update <comment>%s</comment> objects from it - e.g. <comment>$%s->%s()</comment>?',
|
||||
Str::getShortClassName($relation->getInverseClass()),
|
||||
Str::getShortClassName($relation->getOwningClass()),
|
||||
Str::asLowerCamelCase(Str::getShortClassName($relation->getInverseClass())),
|
||||
$getterMethodName
|
||||
),
|
||||
$recommendMappingInverse
|
||||
);
|
||||
$relation->setMapInverseRelation($mapInverse);
|
||||
};
|
||||
|
||||
switch ($type) {
|
||||
case EntityRelation::MANY_TO_ONE:
|
||||
$relation = new EntityRelation(
|
||||
EntityRelation::MANY_TO_ONE,
|
||||
$generatedEntityClass,
|
||||
$targetEntityClass
|
||||
);
|
||||
$relation->setOwningProperty($newFieldName);
|
||||
|
||||
$relation->setIsNullable($askIsNullable(
|
||||
$relation->getOwningProperty(),
|
||||
$relation->getOwningClass()
|
||||
));
|
||||
|
||||
$askInverseSide($relation);
|
||||
if ($relation->getMapInverseRelation()) {
|
||||
$io->comment(\sprintf(
|
||||
'A new property will also be added to the <comment>%s</comment> class so that you can access the related <comment>%s</comment> objects from it.',
|
||||
Str::getShortClassName($relation->getInverseClass()),
|
||||
Str::getShortClassName($relation->getOwningClass())
|
||||
));
|
||||
$relation->setInverseProperty($askFieldName(
|
||||
$relation->getInverseClass(),
|
||||
Str::singularCamelCaseToPluralCamelCase(Str::getShortClassName($relation->getOwningClass()))
|
||||
));
|
||||
|
||||
// orphan removal only applies if the inverse relation is set
|
||||
if (!$relation->isNullable()) {
|
||||
$relation->setOrphanRemoval($askOrphanRemoval(
|
||||
$relation->getOwningClass(),
|
||||
$relation->getInverseClass()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case EntityRelation::ONE_TO_MANY:
|
||||
// we *actually* create a ManyToOne, but populate it differently
|
||||
$relation = new EntityRelation(
|
||||
EntityRelation::MANY_TO_ONE,
|
||||
$targetEntityClass,
|
||||
$generatedEntityClass
|
||||
);
|
||||
$relation->setInverseProperty($newFieldName);
|
||||
|
||||
$io->comment(\sprintf(
|
||||
'A new property will also be added to the <comment>%s</comment> class so that you can access and set the related <comment>%s</comment> object from it.',
|
||||
Str::getShortClassName($relation->getOwningClass()),
|
||||
Str::getShortClassName($relation->getInverseClass())
|
||||
));
|
||||
$relation->setOwningProperty($askFieldName(
|
||||
$relation->getOwningClass(),
|
||||
Str::asLowerCamelCase(Str::getShortClassName($relation->getInverseClass()))
|
||||
));
|
||||
|
||||
$relation->setIsNullable($askIsNullable(
|
||||
$relation->getOwningProperty(),
|
||||
$relation->getOwningClass()
|
||||
));
|
||||
|
||||
if (!$relation->isNullable()) {
|
||||
$relation->setOrphanRemoval($askOrphanRemoval(
|
||||
$relation->getOwningClass(),
|
||||
$relation->getInverseClass()
|
||||
));
|
||||
}
|
||||
|
||||
break;
|
||||
case EntityRelation::MANY_TO_MANY:
|
||||
$relation = new EntityRelation(
|
||||
EntityRelation::MANY_TO_MANY,
|
||||
$generatedEntityClass,
|
||||
$targetEntityClass
|
||||
);
|
||||
$relation->setOwningProperty($newFieldName);
|
||||
|
||||
$askInverseSide($relation);
|
||||
if ($relation->getMapInverseRelation()) {
|
||||
$io->comment(\sprintf(
|
||||
'A new property will also be added to the <comment>%s</comment> class so that you can access the related <comment>%s</comment> objects from it.',
|
||||
Str::getShortClassName($relation->getInverseClass()),
|
||||
Str::getShortClassName($relation->getOwningClass())
|
||||
));
|
||||
$relation->setInverseProperty($askFieldName(
|
||||
$relation->getInverseClass(),
|
||||
Str::singularCamelCaseToPluralCamelCase(Str::getShortClassName($relation->getOwningClass()))
|
||||
));
|
||||
}
|
||||
|
||||
break;
|
||||
case EntityRelation::ONE_TO_ONE:
|
||||
$relation = new EntityRelation(
|
||||
EntityRelation::ONE_TO_ONE,
|
||||
$generatedEntityClass,
|
||||
$targetEntityClass
|
||||
);
|
||||
$relation->setOwningProperty($newFieldName);
|
||||
|
||||
$relation->setIsNullable($askIsNullable(
|
||||
$relation->getOwningProperty(),
|
||||
$relation->getOwningClass()
|
||||
));
|
||||
|
||||
$askInverseSide($relation);
|
||||
if ($relation->getMapInverseRelation()) {
|
||||
$io->comment(\sprintf(
|
||||
'A new property will also be added to the <comment>%s</comment> class so that you can access the related <comment>%s</comment> object from it.',
|
||||
Str::getShortClassName($relation->getInverseClass()),
|
||||
Str::getShortClassName($relation->getOwningClass())
|
||||
));
|
||||
$relation->setInverseProperty($askFieldName(
|
||||
$relation->getInverseClass(),
|
||||
Str::asLowerCamelCase(Str::getShortClassName($relation->getOwningClass()))
|
||||
));
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException('Invalid type: '.$type);
|
||||
}
|
||||
|
||||
return $relation;
|
||||
}
|
||||
|
||||
private function askRelationType(ConsoleStyle $io, string $entityClass, string $targetEntityClass): string
|
||||
{
|
||||
$io->writeln('What type of relationship is this?');
|
||||
|
||||
$originalEntityShort = Str::getShortClassName($entityClass);
|
||||
$targetEntityShort = Str::getShortClassName($targetEntityClass);
|
||||
if ($originalEntityShort === $targetEntityShort) {
|
||||
[$originalDiscriminator, $targetDiscriminator] = Str::getHumanDiscriminatorBetweenTwoClasses($entityClass, $targetEntityClass);
|
||||
$originalEntityShort = trim($originalDiscriminator.'\\'.$originalEntityShort, '\\');
|
||||
$targetEntityShort = trim($targetDiscriminator.'\\'.$targetEntityShort, '\\');
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
$rows[] = [
|
||||
EntityRelation::MANY_TO_ONE,
|
||||
\sprintf("Each <comment>%s</comment> relates to (has) <info>one</info> <comment>%s</comment>.\nEach <comment>%s</comment> can relate to (can have) <info>many</info> <comment>%s</comment> objects.", $originalEntityShort, $targetEntityShort, $targetEntityShort, $originalEntityShort),
|
||||
];
|
||||
$rows[] = ['', ''];
|
||||
$rows[] = [
|
||||
EntityRelation::ONE_TO_MANY,
|
||||
\sprintf("Each <comment>%s</comment> can relate to (can have) <info>many</info> <comment>%s</comment> objects.\nEach <comment>%s</comment> relates to (has) <info>one</info> <comment>%s</comment>.", $originalEntityShort, $targetEntityShort, $targetEntityShort, $originalEntityShort),
|
||||
];
|
||||
$rows[] = ['', ''];
|
||||
$rows[] = [
|
||||
EntityRelation::MANY_TO_MANY,
|
||||
\sprintf("Each <comment>%s</comment> can relate to (can have) <info>many</info> <comment>%s</comment> objects.\nEach <comment>%s</comment> can also relate to (can also have) <info>many</info> <comment>%s</comment> objects.", $originalEntityShort, $targetEntityShort, $targetEntityShort, $originalEntityShort),
|
||||
];
|
||||
$rows[] = ['', ''];
|
||||
$rows[] = [
|
||||
EntityRelation::ONE_TO_ONE,
|
||||
\sprintf("Each <comment>%s</comment> relates to (has) exactly <info>one</info> <comment>%s</comment>.\nEach <comment>%s</comment> also relates to (has) exactly <info>one</info> <comment>%s</comment>.", $originalEntityShort, $targetEntityShort, $targetEntityShort, $originalEntityShort),
|
||||
];
|
||||
|
||||
$io->table([
|
||||
'Type',
|
||||
'Description',
|
||||
], $rows);
|
||||
|
||||
$question = new Question(\sprintf(
|
||||
'Relation type? [%s]',
|
||||
implode(', ', EntityRelation::getValidRelationTypes())
|
||||
));
|
||||
$question->setAutocompleterValues(EntityRelation::getValidRelationTypes());
|
||||
$question->setValidator(function ($type) {
|
||||
if (!\in_array($type, EntityRelation::getValidRelationTypes())) {
|
||||
throw new \InvalidArgumentException(\sprintf('Invalid type: use one of: %s', implode(', ', EntityRelation::getValidRelationTypes())));
|
||||
}
|
||||
|
||||
return $type;
|
||||
});
|
||||
|
||||
return $io->askQuestion($question);
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
private function verifyEntityName(string $entityName): array
|
||||
{
|
||||
preg_match('/([^\x00-\x7F]+)/u', $entityName, $matches);
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
private function createClassManipulator(string $path, ConsoleStyle $io, bool $overwrite): ClassSourceManipulator
|
||||
{
|
||||
$manipulator = new ClassSourceManipulator(
|
||||
sourceCode: $this->fileManager->getFileContents($path),
|
||||
overwrite: $overwrite,
|
||||
);
|
||||
|
||||
$manipulator->setIo($io);
|
||||
|
||||
return $manipulator;
|
||||
}
|
||||
|
||||
private function getPathOfClass(string $class): string
|
||||
{
|
||||
return (new ClassDetails($class))->getPath();
|
||||
}
|
||||
|
||||
private function isClassInVendor(string $class): bool
|
||||
{
|
||||
$path = $this->getPathOfClass($class);
|
||||
|
||||
return $this->fileManager->isPathInVendor($path);
|
||||
}
|
||||
|
||||
private function regenerateEntities(string $classOrNamespace, bool $overwrite, Generator $generator): void
|
||||
{
|
||||
$regenerator = new EntityRegenerator($this->doctrineHelper, $this->fileManager, $generator, $this->entityClassGenerator, $overwrite);
|
||||
$regenerator->regenerateEntities($classOrNamespace);
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
private function getPropertyNames(string $class): array
|
||||
{
|
||||
if (!class_exists($class)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$reflClass = new \ReflectionClass($class);
|
||||
|
||||
return array_map(static fn (\ReflectionProperty $prop) => $prop->getName(), $reflClass->getProperties());
|
||||
}
|
||||
|
||||
private function getEntityNamespace(): string
|
||||
{
|
||||
return $this->doctrineHelper->getEntityNamespace();
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
private function getTypesMap(): array
|
||||
{
|
||||
return Type::getTypesMap();
|
||||
}
|
||||
}
|
||||
97
vendor/symfony/maker-bundle/src/Maker/MakeFixtures.php
vendored
Normal file
97
vendor/symfony/maker-bundle/src/Maker/MakeFixtures.php
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Doctrine\Bundle\FixturesBundle\Fixture;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
/**
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*/
|
||||
final class MakeFixtures extends AbstractMaker
|
||||
{
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:fixtures';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new class to load Doctrine fixtures';
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConf)
|
||||
{
|
||||
$command
|
||||
->addArgument('fixtures-class', InputArgument::OPTIONAL, 'The class name of the fixtures to create (e.g. <fg=yellow>AppFixtures</>)')
|
||||
->setHelp($this->getHelpFileContents('MakeFixture.txt'))
|
||||
;
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
|
||||
{
|
||||
$fixturesClassNameDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('fixtures-class'),
|
||||
'DataFixtures\\'
|
||||
);
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
Fixture::class,
|
||||
ObjectManager::class,
|
||||
]);
|
||||
|
||||
$generator->generateClass(
|
||||
$fixturesClassNameDetails->getFullName(),
|
||||
'doctrine/Fixtures.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
]
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next: Open your new fixtures class and start customizing it.',
|
||||
\sprintf('Load your fixtures by running: <comment>php %s doctrine:fixtures:load</comment>', $_SERVER['PHP_SELF']),
|
||||
'Docs: <fg=yellow>https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html</>',
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function configureDependencies(DependencyBuilder $dependencies)
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
Column::class,
|
||||
'doctrine'
|
||||
);
|
||||
$dependencies->addClassDependency(
|
||||
Fixture::class,
|
||||
'orm-fixtures',
|
||||
true,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
144
vendor/symfony/maker-bundle/src/Maker/MakeForm.php
vendored
Normal file
144
vendor/symfony/maker-bundle/src/Maker/MakeForm.php
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Renderer\FormTypeRenderer;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassDetails;
|
||||
use Symfony\Bundle\MakerBundle\Validator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Validator\Validation;
|
||||
|
||||
/**
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*/
|
||||
final class MakeForm extends AbstractMaker
|
||||
{
|
||||
public function __construct(private DoctrineHelper $entityHelper, private FormTypeRenderer $formTypeRenderer)
|
||||
{
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:form';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new form class';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, \sprintf('The name of the form class (e.g. <fg=yellow>%sType</>)', Str::asClassName(Str::getRandomTerm())))
|
||||
->addArgument('bound-class', InputArgument::OPTIONAL, 'The name of Entity or fully qualified model class name that the new form will be bound to (empty for none)')
|
||||
->setHelp($this->getHelpFileContents('MakeForm.txt'))
|
||||
;
|
||||
|
||||
$inputConfig->setArgumentAsNonInteractive('bound-class');
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
if (null === $input->getArgument('bound-class')) {
|
||||
$argument = $command->getDefinition()->getArgument('bound-class');
|
||||
|
||||
$entities = $this->entityHelper->getEntitiesForAutocomplete();
|
||||
|
||||
$question = new Question($argument->getDescription());
|
||||
$question->setValidator(fn ($answer) => Validator::existsOrNull($answer, $entities));
|
||||
$question->setAutocompleterValues($entities);
|
||||
$question->setMaxAttempts(3);
|
||||
|
||||
$input->setArgument('bound-class', $io->askQuestion($question));
|
||||
}
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$formClassNameDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('name'),
|
||||
'Form\\',
|
||||
'Type'
|
||||
);
|
||||
|
||||
$formFields = ['field_name' => null];
|
||||
|
||||
$boundClass = $input->getArgument('bound-class');
|
||||
$boundClassDetails = null;
|
||||
|
||||
if (null !== $boundClass) {
|
||||
$boundClassDetails = $generator->createClassNameDetails(
|
||||
$boundClass,
|
||||
'Entity\\'
|
||||
);
|
||||
|
||||
$doctrineEntityDetails = $this->entityHelper->createDoctrineDetails($boundClassDetails->getFullName());
|
||||
|
||||
if (null !== $doctrineEntityDetails) {
|
||||
$formFields = $doctrineEntityDetails->getFormFields();
|
||||
} else {
|
||||
$classDetails = new ClassDetails($boundClassDetails->getFullName());
|
||||
$formFields = $classDetails->getFormFields();
|
||||
}
|
||||
}
|
||||
|
||||
$this->formTypeRenderer->render(
|
||||
$formClassNameDetails,
|
||||
$formFields,
|
||||
$boundClassDetails
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next: Add fields to your form and start using it.',
|
||||
'Find the documentation at <fg=yellow>https://symfony.com/doc/current/forms.html</>',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
AbstractType::class,
|
||||
// technically only form is needed, but the user will *probably* also want validation
|
||||
'form'
|
||||
);
|
||||
|
||||
$dependencies->addClassDependency(
|
||||
Validation::class,
|
||||
'validator',
|
||||
// add as an optional dependency: the user *probably* wants validation
|
||||
false
|
||||
);
|
||||
|
||||
$dependencies->addClassDependency(
|
||||
DoctrineBundle::class,
|
||||
'orm',
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
106
vendor/symfony/maker-bundle/src/Maker/MakeFunctionalTest.php
vendored
Normal file
106
vendor/symfony/maker-bundle/src/Maker/MakeFunctionalTest.php
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestAssertionsTrait;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Component\BrowserKit\History;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\CssSelector\CssSelectorConverter;
|
||||
use Symfony\Component\Panther\PantherTestCase;
|
||||
use Symfony\Component\Panther\PantherTestCaseTrait;
|
||||
|
||||
trigger_deprecation('symfony/maker-bundle', '1.29', 'The "%s" class is deprecated, use "%s" instead.', MakeFunctionalTest::class, MakeTest::class);
|
||||
|
||||
/**
|
||||
* @deprecated since MakerBundle 1.29, use Symfony\Bundle\MakerBundle\Maker\MakeTest instead.
|
||||
*
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*/
|
||||
class MakeFunctionalTest extends AbstractMaker
|
||||
{
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:functional-test';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new functional test class';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'The name of the functional test class (e.g. <fg=yellow>DefaultControllerTest</>)')
|
||||
->setHelp($this->getHelpFileContents('MakeFunctionalTest.txt'))
|
||||
;
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$testClassNameDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('name'),
|
||||
'Tests\\',
|
||||
'Test'
|
||||
);
|
||||
|
||||
$pantherAvailable = trait_exists(PantherTestCaseTrait::class);
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
$pantherAvailable ? PantherTestCase::class : WebTestCase::class,
|
||||
]);
|
||||
|
||||
$generator->generateClass(
|
||||
$testClassNameDetails->getFullName(),
|
||||
'test/Functional.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'web_assertions_are_available' => trait_exists(WebTestAssertionsTrait::class),
|
||||
'panther_is_available' => $pantherAvailable,
|
||||
]
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next: Open your new test class and start customizing it.',
|
||||
'Find the documentation at <fg=yellow>https://symfony.com/doc/current/testing.html#functional-tests</>',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
History::class,
|
||||
'browser-kit',
|
||||
true,
|
||||
true
|
||||
);
|
||||
$dependencies->addClassDependency(
|
||||
CssSelectorConverter::class,
|
||||
'css-selector',
|
||||
true,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
264
vendor/symfony/maker-bundle/src/Maker/MakeListener.php
vendored
Normal file
264
vendor/symfony/maker-bundle/src/Maker/MakeListener.php
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\EventRegistry;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Bundle\MakerBundle\Validator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
/**
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
* @author Steven Renaux <steven.renaux8000@gmail.com>
|
||||
*/
|
||||
final class MakeListener extends AbstractMaker
|
||||
{
|
||||
private const ALL_TYPES = ['Listener', 'Subscriber'];
|
||||
private bool $isSubscriber = false;
|
||||
|
||||
public function __construct(private readonly EventRegistry $eventRegistry)
|
||||
{
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:listener';
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated remove this method when removing make:subscriber
|
||||
*/
|
||||
public static function getCommandAlias(): string
|
||||
{
|
||||
return 'make:subscriber';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Creates a new event subscriber class or a new event listener class';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'Choose a class name for your event listener or subscriber (e.g. <fg=yellow>ExceptionListener</> or <fg=yellow>ExceptionSubscriber</>)')
|
||||
->addArgument('event', InputArgument::OPTIONAL, 'What event do you want to listen to?')
|
||||
->setHelp($this->getHelpFileContents('MakeListener.txt'))
|
||||
;
|
||||
|
||||
$inputConfig->setArgumentAsNonInteractive('event');
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
/* @deprecated remove the following block when removing make:subscriber */
|
||||
$this->handleDeprecatedMakerCommands($input, $io);
|
||||
|
||||
$io->writeln('');
|
||||
|
||||
$name = $input->getArgument('name');
|
||||
|
||||
if (!str_ends_with($name, 'Subscriber') && !str_ends_with($name, 'Listener')) {
|
||||
$question = new ChoiceQuestion('Do you want to generate an event listener or subscriber?', self::ALL_TYPES, 0);
|
||||
$classToGenerate = $io->askQuestion($question);
|
||||
|
||||
$input->setArgument('name', $name.$classToGenerate);
|
||||
}
|
||||
|
||||
if (str_ends_with($input->getArgument('name'), 'Subscriber')) {
|
||||
$this->isSubscriber = true;
|
||||
}
|
||||
|
||||
if (!$input->getArgument('event')) {
|
||||
$events = $this->eventRegistry->getAllActiveEvents();
|
||||
|
||||
$io->writeln(' <fg=green>Suggested Events:</>');
|
||||
$io->listing($this->eventRegistry->listActiveEvents($events));
|
||||
$question = new Question(\sprintf(' <fg=green>%s</>', $command->getDefinition()->getArgument('event')->getDescription()));
|
||||
$question->setAutocompleterValues($events);
|
||||
$question->setValidator(Validator::notBlank(...));
|
||||
$event = $io->askQuestion($question);
|
||||
$input->setArgument('event', $event);
|
||||
}
|
||||
|
||||
$event = $input->getArgument('event');
|
||||
if (null === $this->getEventConstant($event) && null === $this->eventRegistry->getEventClassName($event)) {
|
||||
$eventList = $this->eventRegistry->getAllActiveEvents();
|
||||
$eventFQCNList = array_filter(array_map($this->eventRegistry->getEventClassName(...), $eventList), fn ($eventFQCN) => \is_string($eventFQCN));
|
||||
$eventIdAndFQCNList = array_unique(array_merge($eventList, $eventFQCNList));
|
||||
$suggestionList = [];
|
||||
|
||||
foreach ($eventIdAndFQCNList as $eventSuggestion) {
|
||||
if (levenshtein($event, Str::getShortClassName($eventSuggestion)) < 3) {
|
||||
$suggestionList[] = $eventSuggestion;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$suggestionList) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (1 === \count($suggestionList)) {
|
||||
$question = new ConfirmationQuestion(\sprintf('<fg=green>Did you mean</> <fg=yellow>"%s"</> <fg=green>?</>', $suggestionList[0]), false);
|
||||
$input->setArgument('event', $io->askQuestion($question) ? $suggestionList[0] : $event);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$io->writeln(' <fg=yellow>Did you mean one of these events?</>');
|
||||
$io->listing($suggestionList);
|
||||
$question = new Question(\sprintf(' <fg=green>%s</>', $command->getDefinition()->getArgument('event')->getDescription()), $event);
|
||||
$question->setAutocompleterValues(array_merge($suggestionList, [$event]));
|
||||
|
||||
$input->setArgument('event', $io->askQuestion($question));
|
||||
}
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
if ($this->isSubscriber) {
|
||||
$useStatements = new UseStatementGenerator([
|
||||
EventSubscriberInterface::class,
|
||||
]);
|
||||
} else {
|
||||
$useStatements = new UseStatementGenerator([
|
||||
AsEventListener::class,
|
||||
]);
|
||||
}
|
||||
|
||||
$event = $input->getArgument('event');
|
||||
$eventFullClassName = $this->eventRegistry->getEventClassName($event);
|
||||
$eventClassName = $eventFullClassName ? Str::getShortClassName($eventFullClassName) : null;
|
||||
|
||||
if ($this->getEventConstant($event)) {
|
||||
$event = $eventFullClassName;
|
||||
}
|
||||
|
||||
$eventName = class_exists($event) ? \sprintf('%s::class', $eventClassName) : \sprintf('\'%s\'', $event);
|
||||
|
||||
if (null !== $eventFullClassName) {
|
||||
$useStatements->addUseStatement($eventFullClassName);
|
||||
}
|
||||
|
||||
if ($this->isSubscriber) {
|
||||
$this->generateSubscriberClass($input, $io, $generator, $useStatements, $event, $eventName, $eventClassName);
|
||||
} else {
|
||||
$this->generateListenerClass($input, $io, $generator, $useStatements, $event, $eventName, $eventClassName);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function configureDependencies(DependencyBuilder $dependencies)
|
||||
{
|
||||
}
|
||||
|
||||
private function getEventConstant(string $event): ?string
|
||||
{
|
||||
$constants = (new \ReflectionClass(KernelEvents::class))->getConstants();
|
||||
|
||||
if (false !== ($name = array_search($event, $constants, true))) {
|
||||
return \sprintf('KernelEvents::%s', $name);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function generateSubscriberClass(InputInterface $input, ConsoleStyle $io, Generator $generator, UseStatementGenerator $useStatements, string $event, string $eventName, ?string $eventClassName): void
|
||||
{
|
||||
$subscriberClassNameDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('name'),
|
||||
'EventSubscriber\\',
|
||||
'Subscriber'
|
||||
);
|
||||
|
||||
$generator->generateClass(
|
||||
$subscriberClassNameDetails->getFullName(),
|
||||
'event/Subscriber.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'event' => $eventName,
|
||||
'event_arg' => $eventClassName ? \sprintf('%s $event', $eventClassName) : '$event',
|
||||
'method_name' => class_exists($event) ? Str::asEventMethod($eventClassName) : Str::asEventMethod($event),
|
||||
]
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next: Open your new subscriber class and start customizing it.',
|
||||
'Find the documentation at <fg=yellow>https://symfony.com/doc/current/event_dispatcher.html#creating-an-event-subscriber</>',
|
||||
]);
|
||||
}
|
||||
|
||||
private function generateListenerClass(InputInterface $input, ConsoleStyle $io, Generator $generator, UseStatementGenerator $useStatements, string $event, string $eventName, ?string $eventClassName): void
|
||||
{
|
||||
$listenerClassNameDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('name'),
|
||||
'EventListener\\',
|
||||
'Listener'
|
||||
);
|
||||
|
||||
$generator->generateClass(
|
||||
$listenerClassNameDetails->getFullName(),
|
||||
'event/Listener.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'event' => $eventName,
|
||||
'class_event' => str_ends_with($eventName, '::class'),
|
||||
'event_arg' => $eventClassName ? \sprintf('%s $event', $eventClassName) : '$event',
|
||||
'method_name' => class_exists($event) ? Str::asEventMethod($eventClassName) : Str::asEventMethod($event),
|
||||
]
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next: Open your new listener class and start customizing it.',
|
||||
'Find the documentation at <fg=yellow>https://symfony.com/doc/current/event_dispatcher.html#creating-an-event-listener</>',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
private function handleDeprecatedMakerCommands(InputInterface $input, ConsoleStyle $io): void
|
||||
{
|
||||
$currentCommand = $input->getFirstArgument();
|
||||
$name = $input->getArgument('name');
|
||||
|
||||
if ('make:subscriber' === $currentCommand) {
|
||||
if (!str_ends_with($name, 'Subscriber')) {
|
||||
$input->setArgument('name', $name.'Subscriber');
|
||||
}
|
||||
|
||||
$io->warning('The "make:subscriber" command is deprecated, use "make:listener" instead.');
|
||||
}
|
||||
}
|
||||
}
|
||||
171
vendor/symfony/maker-bundle/src/Maker/MakeMessage.php
vendored
Normal file
171
vendor/symfony/maker-bundle/src/Maker/MakeMessage.php
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\FileManager;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Bundle\MakerBundle\Util\YamlSourceManipulator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Messenger\Attribute\AsMessage;
|
||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
|
||||
/**
|
||||
* @author Ryan Weaver <ryan@symfonycasts.com>
|
||||
* @author Nicolas Philippe <nikophil@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class MakeMessage extends AbstractMaker
|
||||
{
|
||||
public function __construct(private FileManager $fileManager)
|
||||
{
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:message';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new message and handler';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'The name of the message class (e.g. <fg=yellow>SendEmailMessage</>)')
|
||||
->setHelp($this->getHelpFileContents('MakeMessage.txt'))
|
||||
;
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
$command->addArgument('chosen-transport', InputArgument::OPTIONAL);
|
||||
|
||||
$messengerData = [];
|
||||
|
||||
try {
|
||||
$manipulator = new YamlSourceManipulator($this->fileManager->getFileContents('config/packages/messenger.yaml'));
|
||||
$messengerData = $manipulator->getData();
|
||||
} catch (\Exception) {
|
||||
}
|
||||
|
||||
if (!isset($messengerData['framework']['messenger']['transports'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$transports = array_keys($messengerData['framework']['messenger']['transports']);
|
||||
array_unshift($transports, $noTransport = '[no transport]');
|
||||
|
||||
$chosenTransport = $io->choice(
|
||||
'Which transport do you want to route your message to?',
|
||||
$transports,
|
||||
$noTransport
|
||||
);
|
||||
|
||||
if ($noTransport !== $chosenTransport) {
|
||||
$input->setArgument('chosen-transport', $chosenTransport);
|
||||
}
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$chosenTransport = $input->getArgument('chosen-transport');
|
||||
|
||||
$messageClassNameDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('name'),
|
||||
'Message\\'
|
||||
);
|
||||
|
||||
$handlerClassNameDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('name').'Handler',
|
||||
'MessageHandler\\',
|
||||
'Handler'
|
||||
);
|
||||
|
||||
$useStatements = new UseStatementGenerator([]);
|
||||
|
||||
/* @legacy remove when AsMessage is always available */
|
||||
if ($chosenTransport && class_exists(AsMessage::class)) {
|
||||
$useStatements->addUseStatement(AsMessage::class);
|
||||
}
|
||||
|
||||
$generator->generateClass(
|
||||
$messageClassNameDetails->getFullName(),
|
||||
'message/Message.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'transport' => class_exists(AsMessage::class) ? $chosenTransport : null,
|
||||
]
|
||||
);
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
AsMessageHandler::class,
|
||||
$messageClassNameDetails->getFullName(),
|
||||
]);
|
||||
|
||||
$generator->generateClass(
|
||||
$handlerClassNameDetails->getFullName(),
|
||||
'message/MessageHandler.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'message_class_name' => $messageClassNameDetails->getShortName(),
|
||||
]
|
||||
);
|
||||
|
||||
/* @legacy remove when AsMessage is always available */
|
||||
if ($chosenTransport && !class_exists(AsMessage::class)) {
|
||||
$this->updateMessengerConfig($generator, $chosenTransport, $messageClassNameDetails->getFullName());
|
||||
}
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next: Open your new message class and add the properties you need.',
|
||||
' Then, open the new message handler and do whatever work you want!',
|
||||
'Find the documentation at <fg=yellow>https://symfony.com/doc/current/messenger.html</>',
|
||||
]);
|
||||
}
|
||||
|
||||
private function updateMessengerConfig(Generator $generator, string $chosenTransport, string $messageClass): void
|
||||
{
|
||||
$manipulator = new YamlSourceManipulator($this->fileManager->getFileContents($configFilePath = 'config/packages/messenger.yaml'));
|
||||
$messengerData = $manipulator->getData();
|
||||
|
||||
if (!isset($messengerData['framework']['messenger']['routing'])) {
|
||||
$messengerData['framework']['messenger']['routing'] = [];
|
||||
}
|
||||
|
||||
$messengerData['framework']['messenger']['routing'][$messageClass] = $chosenTransport;
|
||||
|
||||
$manipulator->setData($messengerData);
|
||||
$generator->dumpFile($configFilePath, $manipulator->getContents());
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
MessageBusInterface::class,
|
||||
'messenger'
|
||||
);
|
||||
}
|
||||
}
|
||||
93
vendor/symfony/maker-bundle/src/Maker/MakeMessengerMiddleware.php
vendored
Normal file
93
vendor/symfony/maker-bundle/src/Maker/MakeMessengerMiddleware.php
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Messenger\Envelope;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
|
||||
use Symfony\Component\Messenger\Middleware\StackInterface;
|
||||
|
||||
/**
|
||||
* @author Imad ZAIRIG <imadzairig@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class MakeMessengerMiddleware extends AbstractMaker
|
||||
{
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:messenger-middleware';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new messenger middleware';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'The name of the middleware class (e.g. <fg=yellow>CustomMiddleware</>)')
|
||||
->setHelp($this->getHelpFileContents('MakeMessage.txt'))
|
||||
;
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$middlewareClassNameDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('name'),
|
||||
'Middleware\\',
|
||||
'Middleware'
|
||||
);
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
Envelope::class,
|
||||
MiddlewareInterface::class,
|
||||
StackInterface::class,
|
||||
]);
|
||||
|
||||
$generator->generateClass(
|
||||
$middlewareClassNameDetails->getFullName(),
|
||||
'middleware/Middleware.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
]
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next:',
|
||||
\sprintf('- Open the <info>%s</info> class and add the code you need', $middlewareClassNameDetails->getFullName()),
|
||||
'- Add the middleware to your <info>config/packages/messenger.yaml</info> file',
|
||||
'Find the documentation at <fg=yellow>https://symfony.com/doc/current/messenger.html#middleware</>',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
MessageBusInterface::class,
|
||||
'messenger'
|
||||
);
|
||||
}
|
||||
}
|
||||
180
vendor/symfony/maker-bundle/src/Maker/MakeMigration.php
vendored
Normal file
180
vendor/symfony/maker-bundle/src/Maker/MakeMigration.php
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Doctrine\Bundle\MigrationsBundle\Command\MigrationsDiffDoctrineCommand;
|
||||
use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle;
|
||||
use Symfony\Bundle\MakerBundle\ApplicationAwareMakerInterface;
|
||||
use Symfony\Bundle\MakerBundle\Console\MigrationDiffFilteredOutput;
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Util\CliOutputHelper;
|
||||
use Symfony\Bundle\MakerBundle\Util\MakerFileLinkFormatter;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* @author Amrouche Hamza <hamza.simperfit@gmail.com>
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*/
|
||||
final class MakeMigration extends AbstractMaker implements ApplicationAwareMakerInterface
|
||||
{
|
||||
private Application $application;
|
||||
|
||||
public function __construct(
|
||||
private string $projectDir,
|
||||
private ?MakerFileLinkFormatter $makerFileLinkFormatter = null,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:migration';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new migration based on database changes';
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function setApplication(Application $application)
|
||||
{
|
||||
$this->application = $application;
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->setHelp($this->getHelpFileContents('MakeMigration.txt'))
|
||||
;
|
||||
|
||||
if (class_exists(MigrationsDiffDoctrineCommand::class)) {
|
||||
// support for DoctrineMigrationsBundle 2.x
|
||||
$command
|
||||
->addOption('db', null, InputOption::VALUE_REQUIRED, 'The database connection name')
|
||||
->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager name')
|
||||
->addOption('shard', null, InputOption::VALUE_REQUIRED, 'The shard connection name')
|
||||
;
|
||||
}
|
||||
|
||||
$command
|
||||
->addOption('formatted', null, InputOption::VALUE_NONE, 'Format the generated SQL')
|
||||
->addOption('configuration', null, InputOption::VALUE_OPTIONAL, 'The path of doctrine configuration file')
|
||||
;
|
||||
}
|
||||
|
||||
/** @return void|int */
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
|
||||
{
|
||||
$options = ['doctrine:migrations:diff'];
|
||||
|
||||
// DoctrineMigrationsBundle 2.x support
|
||||
if ($input->hasOption('db') && null !== $input->getOption('db')) {
|
||||
$options[] = '--db='.$input->getOption('db');
|
||||
}
|
||||
if ($input->hasOption('em') && null !== $input->getOption('em')) {
|
||||
$options[] = '--em='.$input->getOption('em');
|
||||
}
|
||||
if ($input->hasOption('shard') && null !== $input->getOption('shard')) {
|
||||
$options[] = '--shard='.$input->getOption('shard');
|
||||
}
|
||||
// end 2.x support
|
||||
|
||||
if ($input->getOption('formatted')) {
|
||||
$options[] = '--formatted';
|
||||
}
|
||||
|
||||
if (null !== $configuration = $input->getOption('configuration')) {
|
||||
$options[] = '--configuration='.$configuration;
|
||||
}
|
||||
|
||||
$generateMigrationCommand = $this->application->find('doctrine:migrations:diff');
|
||||
$generateMigrationCommandInput = new ArgvInput($options);
|
||||
|
||||
if (!$input->isInteractive()) {
|
||||
$generateMigrationCommandInput->setInteractive(false);
|
||||
}
|
||||
|
||||
$commandOutput = new MigrationDiffFilteredOutput($io->getOutput());
|
||||
try {
|
||||
$returnCode = $generateMigrationCommand->run($generateMigrationCommandInput, $commandOutput);
|
||||
|
||||
// non-zero code would ideally mean the internal command has already printed an errror
|
||||
// this happens if you "decline" generating a migration when you already
|
||||
// have some available
|
||||
if (0 !== $returnCode) {
|
||||
return $returnCode;
|
||||
}
|
||||
|
||||
$migrationOutput = $commandOutput->fetch();
|
||||
|
||||
if (str_contains($migrationOutput, 'No changes detected')) {
|
||||
$this->noChangesMessage($io);
|
||||
|
||||
return;
|
||||
}
|
||||
} catch (\Doctrine\Migrations\Generator\Exception\NoChangesDetected) {
|
||||
$this->noChangesMessage($io);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$absolutePath = $this->getGeneratedMigrationFilename($migrationOutput);
|
||||
$relativePath = str_replace($this->projectDir.'/', '', $absolutePath);
|
||||
|
||||
$io->comment('<fg=blue>created</>: '.($this->makerFileLinkFormatter?->makeLinkedPath($absolutePath, $relativePath) ?? $relativePath));
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
\sprintf('Review the new migration then run it with <info>%s doctrine:migrations:migrate</info>', CliOutputHelper::getCommandPrefix()),
|
||||
'See <fg=yellow>https://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html</>',
|
||||
]);
|
||||
}
|
||||
|
||||
private function noChangesMessage(ConsoleStyle $io): void
|
||||
{
|
||||
$io->warning([
|
||||
'No database changes were detected.',
|
||||
]);
|
||||
$io->text([
|
||||
'The database schema and the application mapping information are already in sync.',
|
||||
'',
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function configureDependencies(DependencyBuilder $dependencies)
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
DoctrineMigrationsBundle::class,
|
||||
'doctrine/doctrine-migrations-bundle'
|
||||
);
|
||||
}
|
||||
|
||||
private function getGeneratedMigrationFilename(string $migrationOutput): string
|
||||
{
|
||||
preg_match('#"<info>(.*?)</info>"#', $migrationOutput, $matches);
|
||||
|
||||
if (!isset($matches[1])) {
|
||||
throw new \Exception('Your migration generated successfully, but an error occurred printing the summary of what occurred.');
|
||||
}
|
||||
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
603
vendor/symfony/maker-bundle/src/Maker/MakeRegistrationForm.php
vendored
Normal file
603
vendor/symfony/maker-bundle/src/Maker/MakeRegistrationForm.php
vendored
Normal file
@@ -0,0 +1,603 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
|
||||
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
|
||||
use Symfony\Bundle\MakerBundle\FileManager;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Maker\Common\CanGenerateTestsTrait;
|
||||
use Symfony\Bundle\MakerBundle\Renderer\FormTypeRenderer;
|
||||
use Symfony\Bundle\MakerBundle\Security\InteractiveSecurityHelper;
|
||||
use Symfony\Bundle\MakerBundle\Security\Model\Authenticator;
|
||||
use Symfony\Bundle\MakerBundle\Security\Model\AuthenticatorType;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassDetails;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassNameDetails;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator;
|
||||
use Symfony\Bundle\MakerBundle\Util\CliOutputHelper;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Bundle\MakerBundle\Util\YamlSourceManipulator;
|
||||
use Symfony\Bundle\MakerBundle\Validator;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Bundle\SecurityBundle\SecurityBundle;
|
||||
use Symfony\Bundle\TwigBundle\TwigBundle;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Mailer\MailerInterface;
|
||||
use Symfony\Component\Mime\Address;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Translation\Translator;
|
||||
use Symfony\Component\Validator\Validation;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use SymfonyCasts\Bundle\VerifyEmail\Exception\VerifyEmailExceptionInterface;
|
||||
use SymfonyCasts\Bundle\VerifyEmail\SymfonyCastsVerifyEmailBundle;
|
||||
use SymfonyCasts\Bundle\VerifyEmail\VerifyEmailHelper;
|
||||
use SymfonyCasts\Bundle\VerifyEmail\VerifyEmailHelperInterface;
|
||||
|
||||
/**
|
||||
* @author Ryan Weaver <ryan@symfonycasts.com>
|
||||
* @author Jesse Rushlow <jr@rushlow.dev>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class MakeRegistrationForm extends AbstractMaker
|
||||
{
|
||||
use CanGenerateTestsTrait;
|
||||
|
||||
private string $userClass;
|
||||
private string $usernameField;
|
||||
private string $passwordField;
|
||||
private bool $willVerifyEmail = false;
|
||||
private bool $verifyEmailAnonymously = false;
|
||||
private string $idGetter;
|
||||
private string $emailGetter;
|
||||
private string $fromEmailAddress;
|
||||
private string $fromEmailName;
|
||||
private ?Authenticator $autoLoginAuthenticator = null;
|
||||
private string $redirectRouteName;
|
||||
private bool $addUniqueEntityConstraint = false;
|
||||
|
||||
public function __construct(
|
||||
private FileManager $fileManager,
|
||||
private FormTypeRenderer $formTypeRenderer,
|
||||
private DoctrineHelper $doctrineHelper,
|
||||
private ?RouterInterface $router = null,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:registration-form';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new registration form system';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->setHelp($this->getHelpFileContents('MakeRegistrationForm.txt'))
|
||||
;
|
||||
|
||||
$this->configureCommandWithTestsOption($command);
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
$interactiveSecurityHelper = new InteractiveSecurityHelper();
|
||||
|
||||
if (null === $this->router) {
|
||||
throw new RuntimeCommandException('Router have been explicitly disabled in your configuration. This command needs to use the router.');
|
||||
}
|
||||
|
||||
if (!$this->fileManager->fileExists($path = 'config/packages/security.yaml')) {
|
||||
throw new RuntimeCommandException('The file "config/packages/security.yaml" does not exist. PHP & XML configuration formats are currently not supported.');
|
||||
}
|
||||
|
||||
$manipulator = new YamlSourceManipulator($this->fileManager->getFileContents($path));
|
||||
$securityData = $manipulator->getData();
|
||||
$providersData = $securityData['security']['providers'] ?? [];
|
||||
|
||||
$this->userClass = $interactiveSecurityHelper->guessUserClass(
|
||||
$io,
|
||||
$providersData,
|
||||
'Enter the User class that you want to create during registration (e.g. <fg=yellow>App\\Entity\\User</>)'
|
||||
);
|
||||
$io->text(\sprintf('Creating a registration form for <info>%s</info>', $this->userClass));
|
||||
|
||||
$this->usernameField = $interactiveSecurityHelper->guessUserNameField($io, $this->userClass, $providersData);
|
||||
|
||||
$this->passwordField = $interactiveSecurityHelper->guessPasswordField($io, $this->userClass);
|
||||
|
||||
// see if it makes sense to add the UniqueEntity constraint
|
||||
$userClassDetails = new ClassDetails($this->userClass);
|
||||
|
||||
if (!$userClassDetails->hasAttribute(UniqueEntity::class)) {
|
||||
$this->addUniqueEntityConstraint = (bool) $io->confirm(\sprintf('Do you want to add a <comment>#[UniqueEntity]</comment> validation attribute to your <comment>%s</comment> class to make sure duplicate accounts aren\'t created?', Str::getShortClassName($this->userClass)));
|
||||
}
|
||||
|
||||
$this->willVerifyEmail = (bool) $io->confirm('Do you want to send an email to verify the user\'s email address after registration?');
|
||||
|
||||
if ($this->willVerifyEmail) {
|
||||
$this->checkComponentsExist($io);
|
||||
|
||||
$emailText[] = 'By default, users are required to be authenticated when they click the verification link that is emailed to them.';
|
||||
$emailText[] = 'This prevents the user from registering on their laptop, then clicking the link on their phone, without';
|
||||
$emailText[] = 'having to log in. To allow multi device email verification, we can embed a user id in the verification link.';
|
||||
$io->text($emailText);
|
||||
$io->newLine();
|
||||
$this->verifyEmailAnonymously = (bool) $io->confirm('Would you like to include the user id in the verification link to allow anonymous email verification?', false);
|
||||
|
||||
$this->idGetter = $interactiveSecurityHelper->guessIdGetter($io, $this->userClass);
|
||||
$this->emailGetter = $interactiveSecurityHelper->guessEmailGetter($io, $this->userClass, 'email');
|
||||
|
||||
$this->fromEmailAddress = $io->ask(
|
||||
'What email address will be used to send registration confirmations? (e.g. <fg=yellow>mailer@your-domain.com</>)',
|
||||
null,
|
||||
Validator::validateEmailAddress(...)
|
||||
);
|
||||
|
||||
$this->fromEmailName = $io->ask(
|
||||
'What "name" should be associated with that email address? (e.g. <fg=yellow>Acme Mail Bot</>)',
|
||||
null,
|
||||
Validator::notBlank(...)
|
||||
);
|
||||
}
|
||||
|
||||
if ($io->confirm('Do you want to automatically authenticate the user after registration?')) {
|
||||
$this->interactAuthenticatorQuestions(
|
||||
$io,
|
||||
$interactiveSecurityHelper,
|
||||
$securityData
|
||||
);
|
||||
}
|
||||
|
||||
if (!$this->autoLoginAuthenticator) {
|
||||
$routeNames = array_keys($this->router->getRouteCollection()->all());
|
||||
$this->redirectRouteName = $io->choice('What route should the user be redirected to after registration?', $routeNames);
|
||||
}
|
||||
|
||||
$this->interactSetGenerateTests($input, $io);
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $securityData */
|
||||
private function interactAuthenticatorQuestions(ConsoleStyle $io, InteractiveSecurityHelper $interactiveSecurityHelper, array $securityData): void
|
||||
{
|
||||
// get list of authenticators
|
||||
$authenticators = $interactiveSecurityHelper->getAuthenticatorsFromConfig($securityData['security']['firewalls'] ?? []);
|
||||
|
||||
if (empty($authenticators)) {
|
||||
$io->note('No authenticators found - so your user won\'t be automatically authenticated after registering.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->autoLoginAuthenticator =
|
||||
1 === \count($authenticators) ? $authenticators[0] : $io->choice(
|
||||
'Which authenticator should be used to login the user?',
|
||||
$authenticators
|
||||
);
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$userClassNameDetails = $generator->createClassNameDetails(
|
||||
'\\'.$this->userClass,
|
||||
'Entity\\'
|
||||
);
|
||||
|
||||
$userDoctrineDetails = $this->doctrineHelper->createDoctrineDetails($userClassNameDetails->getFullName());
|
||||
|
||||
$userRepoVars = [
|
||||
'repository_full_class_name' => EntityManagerInterface::class,
|
||||
'repository_class_name' => 'EntityManagerInterface',
|
||||
'repository_var' => '$manager',
|
||||
];
|
||||
|
||||
$userRepository = $userDoctrineDetails->getRepositoryClass();
|
||||
|
||||
if (null !== $userRepository) {
|
||||
$userRepoClassDetails = $generator->createClassNameDetails('\\'.$userRepository, 'Repository\\', 'Repository');
|
||||
|
||||
$userRepoVars = [
|
||||
'repository_full_class_name' => $userRepoClassDetails->getFullName(),
|
||||
'repository_class_name' => $userRepoClassDetails->getShortName(),
|
||||
'repository_var' => \sprintf('$%s', lcfirst($userRepoClassDetails->getShortName())),
|
||||
];
|
||||
}
|
||||
|
||||
$verifyEmailServiceClassNameDetails = $generator->createClassNameDetails(
|
||||
'EmailVerifier',
|
||||
'Security\\'
|
||||
);
|
||||
|
||||
$verifyEmailVars = ['will_verify_email' => $this->willVerifyEmail];
|
||||
|
||||
if ($this->willVerifyEmail) {
|
||||
$verifyEmailVars = [
|
||||
'will_verify_email' => $this->willVerifyEmail,
|
||||
'email_verifier_class_details' => $verifyEmailServiceClassNameDetails,
|
||||
'verify_email_anonymously' => $this->verifyEmailAnonymously,
|
||||
'from_email' => $this->fromEmailAddress,
|
||||
'from_email_name' => addslashes($this->fromEmailName),
|
||||
'email_getter' => $this->emailGetter,
|
||||
];
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
EntityManagerInterface::class,
|
||||
TemplatedEmail::class,
|
||||
Request::class,
|
||||
MailerInterface::class,
|
||||
UserInterface::class,
|
||||
VerifyEmailExceptionInterface::class,
|
||||
VerifyEmailHelperInterface::class,
|
||||
$userClassNameDetails->getFullName(),
|
||||
]);
|
||||
|
||||
$generator->generateClass(
|
||||
$verifyEmailServiceClassNameDetails->getFullName(),
|
||||
'verifyEmail/EmailVerifier.tpl.php',
|
||||
array_merge([
|
||||
'use_statements' => $useStatements,
|
||||
'id_getter' => $this->idGetter,
|
||||
'email_getter' => $this->emailGetter,
|
||||
'verify_email_anonymously' => $this->verifyEmailAnonymously,
|
||||
'user_class_name' => $userClassNameDetails->getShortName(),
|
||||
],
|
||||
$userRepoVars
|
||||
)
|
||||
);
|
||||
|
||||
$generator->generateTemplate(
|
||||
'registration/confirmation_email.html.twig',
|
||||
'registration/twig_email.tpl.php'
|
||||
);
|
||||
}
|
||||
|
||||
// 1) Generate the form class
|
||||
$usernameField = $this->usernameField;
|
||||
$formClassDetails = $this->generateFormClass(
|
||||
$userClassNameDetails,
|
||||
$generator,
|
||||
$usernameField
|
||||
);
|
||||
|
||||
// 2) Generate the controller
|
||||
$controllerClassNameDetails = $generator->createClassNameDetails(
|
||||
'RegistrationController',
|
||||
'Controller\\'
|
||||
);
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
AbstractController::class,
|
||||
$formClassDetails->getFullName(),
|
||||
$userClassNameDetails->getFullName(),
|
||||
Request::class,
|
||||
Response::class,
|
||||
Route::class,
|
||||
UserPasswordHasherInterface::class,
|
||||
EntityManagerInterface::class,
|
||||
]);
|
||||
|
||||
if ($this->willVerifyEmail) {
|
||||
$useStatements->addUseStatement([
|
||||
$verifyEmailServiceClassNameDetails->getFullName(),
|
||||
TemplatedEmail::class,
|
||||
Address::class,
|
||||
VerifyEmailExceptionInterface::class,
|
||||
]);
|
||||
|
||||
if ($this->verifyEmailAnonymously) {
|
||||
$useStatements->addUseStatement($userRepoVars['repository_full_class_name']);
|
||||
}
|
||||
}
|
||||
|
||||
$autoLoginVars = [
|
||||
'login_after_registration' => null !== $this->autoLoginAuthenticator,
|
||||
];
|
||||
|
||||
if (null !== $this->autoLoginAuthenticator) {
|
||||
$useStatements->addUseStatement([
|
||||
Security::class,
|
||||
]);
|
||||
|
||||
$autoLoginVars['firewall'] = $this->autoLoginAuthenticator->firewallName;
|
||||
$autoLoginVars['authenticator'] = \sprintf('\'%s\'', $this->autoLoginAuthenticator->type->value);
|
||||
|
||||
if (AuthenticatorType::CUSTOM === $this->autoLoginAuthenticator->type) {
|
||||
$useStatements->addUseStatement($this->autoLoginAuthenticator->authenticatorClass);
|
||||
$autoLoginVars['authenticator'] = \sprintf('%s::class', Str::getShortClassName($this->autoLoginAuthenticator->authenticatorClass));
|
||||
}
|
||||
}
|
||||
|
||||
if ($isTranslatorAvailable = class_exists(Translator::class)) {
|
||||
$useStatements->addUseStatement(TranslatorInterface::class);
|
||||
}
|
||||
|
||||
$generator->generateController(
|
||||
$controllerClassNameDetails->getFullName(),
|
||||
'registration/RegistrationController.tpl.php',
|
||||
array_merge([
|
||||
'use_statements' => $useStatements,
|
||||
'route_path' => '/register',
|
||||
'route_name' => 'app_register',
|
||||
'form_class_name' => $formClassDetails->getShortName(),
|
||||
'user_class_name' => $userClassNameDetails->getShortName(),
|
||||
'password_field' => $this->passwordField,
|
||||
'redirect_route_name' => $this->redirectRouteName ?? null,
|
||||
'translator_available' => $isTranslatorAvailable,
|
||||
],
|
||||
$userRepoVars,
|
||||
$autoLoginVars,
|
||||
$verifyEmailVars,
|
||||
)
|
||||
);
|
||||
|
||||
// 3) Generate the template
|
||||
$generator->generateTemplate(
|
||||
'registration/register.html.twig',
|
||||
'registration/twig_template.tpl.php',
|
||||
[
|
||||
'username_field' => $usernameField,
|
||||
'will_verify_email' => $this->willVerifyEmail,
|
||||
]
|
||||
);
|
||||
|
||||
// 4) Update the User class if necessary
|
||||
if ($this->addUniqueEntityConstraint) {
|
||||
$classDetails = new ClassDetails($this->userClass);
|
||||
$userManipulator = new ClassSourceManipulator(
|
||||
sourceCode: file_get_contents($classDetails->getPath())
|
||||
);
|
||||
$userManipulator->setIo($io);
|
||||
|
||||
if ($this->doctrineHelper->isDoctrineSupportingAttributes()) {
|
||||
$userManipulator->addAttributeToClass(
|
||||
UniqueEntity::class,
|
||||
['fields' => [$usernameField], 'message' => \sprintf('There is already an account with this %s', $usernameField)]
|
||||
);
|
||||
}
|
||||
|
||||
$this->fileManager->dumpFile($classDetails->getPath(), $userManipulator->getSourceCode());
|
||||
}
|
||||
|
||||
if ($this->willVerifyEmail) {
|
||||
$classDetails = new ClassDetails($this->userClass);
|
||||
$userManipulator = new ClassSourceManipulator(
|
||||
sourceCode: file_get_contents($classDetails->getPath()),
|
||||
overwrite: false,
|
||||
);
|
||||
$userManipulator->setIo($io);
|
||||
|
||||
$userManipulator->addProperty(
|
||||
name: 'isVerified',
|
||||
defaultValue: false,
|
||||
attributes: [$userManipulator->buildAttributeNode(attributeClass: Column::class, options: [], attributePrefix: 'ORM')],
|
||||
propertyType: 'bool'
|
||||
);
|
||||
$userManipulator->addAccessorMethod('isVerified', 'isVerified', 'bool', false);
|
||||
$userManipulator->addSetter('isVerified', 'bool', false);
|
||||
|
||||
$this->fileManager->dumpFile($classDetails->getPath(), $userManipulator->getSourceCode());
|
||||
}
|
||||
|
||||
// Generate PHPUnit Tests
|
||||
if ($this->shouldGenerateTests()) {
|
||||
$testClassDetails = $generator->createClassNameDetails(
|
||||
'RegistrationControllerTest',
|
||||
'Test\\'
|
||||
);
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
EntityManager::class,
|
||||
KernelBrowser::class,
|
||||
TemplatedEmail::class,
|
||||
WebTestCase::class,
|
||||
$userRepoVars['repository_full_class_name'],
|
||||
]);
|
||||
|
||||
$generator->generateFile(
|
||||
targetPath: \sprintf('tests/%s.php', $testClassDetails->getShortName()),
|
||||
templateName: $this->willVerifyEmail ? 'registration/Test.WithVerify.tpl.php' : 'registration/Test.WithoutVerify.tpl.php',
|
||||
variables: array_merge([
|
||||
'use_statements' => $useStatements,
|
||||
'from_email' => $this->fromEmailAddress ?? null,
|
||||
], $userRepoVars)
|
||||
);
|
||||
|
||||
if (!class_exists(WebTestCase::class)) {
|
||||
$io->caution('You\'ll need to install the `symfony/test-pack` to execute the tests for your new controller.');
|
||||
}
|
||||
}
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
$this->successMessage($io, $this->willVerifyEmail, $userClassNameDetails->getShortName());
|
||||
}
|
||||
|
||||
private function successMessage(ConsoleStyle $io, bool $emailVerification, string $userClass): void
|
||||
{
|
||||
$closing[] = 'Next:';
|
||||
|
||||
if (!$emailVerification) {
|
||||
$closing[] = 'Make any changes you need to the form, controller & template.';
|
||||
} else {
|
||||
$index = 1;
|
||||
if ($missingPackagesMessage = $this->getMissingComponentsComposerMessage()) {
|
||||
$closing[] = '1) Install some missing packages:';
|
||||
$closing[] = \sprintf(' <fg=green>%s</>', $missingPackagesMessage);
|
||||
++$index;
|
||||
}
|
||||
|
||||
$closing[] = \sprintf('%d) In <fg=yellow>RegistrationController::verifyUserEmail()</>:', $index++);
|
||||
$closing[] = ' * Customize the last <fg=yellow>redirectToRoute()</> after a successful email verification.';
|
||||
$closing[] = ' * Make sure you\'re rendering <fg=yellow>success</> flash messages or change the <fg=yellow>$this->addFlash()</> line.';
|
||||
$closing[] = \sprintf('%d) Review and customize the form, controller, and templates as needed.', $index++);
|
||||
$closing[] = \sprintf('%d) Run <fg=yellow>"%s make:migration"</> to generate a migration for the newly added <fg=yellow>%s::isVerified</> property.', $index++, CliOutputHelper::getCommandPrefix(), $userClass);
|
||||
}
|
||||
|
||||
$io->text($closing);
|
||||
$io->newLine();
|
||||
$io->text('Then open your browser, go to "/register" and enjoy your new form!');
|
||||
$io->newLine();
|
||||
}
|
||||
|
||||
private function checkComponentsExist(ConsoleStyle $io): void
|
||||
{
|
||||
$message = $this->getMissingComponentsComposerMessage();
|
||||
|
||||
if ($message) {
|
||||
$io->warning([
|
||||
'We\'re missing some important components. Don\'t forget to install these after you\'re finished.',
|
||||
$message,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function getMissingComponentsComposerMessage(): ?string
|
||||
{
|
||||
$missing = false;
|
||||
$composerMessage = 'composer require';
|
||||
|
||||
// verify-email-bundle 1.17.0 includes the new validateEmailConfirmationFromRequest method.
|
||||
// we need to check that if the bundle is installed, it is version 1.17.0 or greater
|
||||
if (class_exists(SymfonyCastsVerifyEmailBundle::class)) {
|
||||
$reflectedComponents = new \ReflectionClass(VerifyEmailHelper::class);
|
||||
|
||||
if (!$reflectedComponents->hasMethod('validateEmailConfirmationFromRequest')) {
|
||||
throw new RuntimeCommandException('Please upgrade symfonycasts/verify-email-bundle to version 1.17.0 or greater.');
|
||||
}
|
||||
} else {
|
||||
$missing = true;
|
||||
$composerMessage = \sprintf('%s symfonycasts/verify-email-bundle', $composerMessage);
|
||||
}
|
||||
|
||||
if (!interface_exists(MailerInterface::class)) {
|
||||
$missing = true;
|
||||
$composerMessage = \sprintf('%s symfony/mailer', $composerMessage);
|
||||
}
|
||||
|
||||
if (!$missing) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $composerMessage;
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
AbstractType::class,
|
||||
'form'
|
||||
);
|
||||
|
||||
$dependencies->addClassDependency(
|
||||
Validation::class,
|
||||
'validator'
|
||||
);
|
||||
|
||||
$dependencies->addClassDependency(
|
||||
TwigBundle::class,
|
||||
'twig-bundle'
|
||||
);
|
||||
|
||||
$dependencies->addClassDependency(
|
||||
DoctrineBundle::class,
|
||||
'orm'
|
||||
);
|
||||
|
||||
$dependencies->addClassDependency(
|
||||
SecurityBundle::class,
|
||||
'security'
|
||||
);
|
||||
}
|
||||
|
||||
private function generateFormClass(ClassNameDetails $userClassDetails, Generator $generator, string $usernameField): ClassNameDetails
|
||||
{
|
||||
$formClassDetails = $generator->createClassNameDetails(
|
||||
'RegistrationFormType',
|
||||
'Form\\'
|
||||
);
|
||||
|
||||
$formFields = [
|
||||
$usernameField => null,
|
||||
'agreeTerms' => [
|
||||
'type' => CheckboxType::class,
|
||||
'options_code' => <<<EOF
|
||||
'mapped' => false,
|
||||
'constraints' => [
|
||||
new IsTrue([
|
||||
'message' => 'You should agree to our terms.',
|
||||
]),
|
||||
],
|
||||
EOF
|
||||
],
|
||||
'plainPassword' => [
|
||||
'type' => PasswordType::class,
|
||||
'options_code' => <<<EOF
|
||||
// instead of being set onto the object directly,
|
||||
// this is read and encoded in the controller
|
||||
'mapped' => false,
|
||||
'attr' => ['autocomplete' => 'new-password'],
|
||||
'constraints' => [
|
||||
new NotBlank([
|
||||
'message' => 'Please enter a password',
|
||||
]),
|
||||
new Length([
|
||||
'min' => 6,
|
||||
'minMessage' => 'Your password should be at least {{ limit }} characters',
|
||||
// max length allowed by Symfony for security reasons
|
||||
'max' => 4096,
|
||||
]),
|
||||
],
|
||||
EOF
|
||||
],
|
||||
];
|
||||
|
||||
$this->formTypeRenderer->render(
|
||||
$formClassDetails,
|
||||
$formFields,
|
||||
$userClassDetails,
|
||||
[
|
||||
'Symfony\Component\Validator\Constraints\IsTrue',
|
||||
'Symfony\Component\Validator\Constraints\Length',
|
||||
'Symfony\Component\Validator\Constraints\NotBlank',
|
||||
]
|
||||
);
|
||||
|
||||
return $formClassDetails;
|
||||
}
|
||||
}
|
||||
553
vendor/symfony/maker-bundle/src/Maker/MakeResetPassword.php
vendored
Normal file
553
vendor/symfony/maker-bundle/src/Maker/MakeResetPassword.php
vendored
Normal file
@@ -0,0 +1,553 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PhpParser\Builder\Param;
|
||||
use Symfony\Bridge\Twig\AppVariable;
|
||||
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\EntityClassGenerator;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\ORMDependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\RelationManyToOne;
|
||||
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
|
||||
use Symfony\Bundle\MakerBundle\FileManager;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Maker\Common\CanGenerateTestsTrait;
|
||||
use Symfony\Bundle\MakerBundle\Maker\Common\UidTrait;
|
||||
use Symfony\Bundle\MakerBundle\Security\InteractiveSecurityHelper;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassNameDetails;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator;
|
||||
use Symfony\Bundle\MakerBundle\Util\CliOutputHelper;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Bundle\MakerBundle\Util\YamlSourceManipulator;
|
||||
use Symfony\Bundle\MakerBundle\Validator;
|
||||
use Symfony\Bundle\SecurityBundle\SecurityBundle;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
|
||||
use Symfony\Component\Form\Form;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Mailer\MailerInterface;
|
||||
use Symfony\Component\Mime\Address;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Routing\Route as RouteObject;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Symfony\Component\Translation\Translator;
|
||||
use Symfony\Component\Validator\Constraints\Length;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Constraints\NotCompromisedPassword;
|
||||
use Symfony\Component\Validator\Constraints\PasswordStrength;
|
||||
use Symfony\Component\Validator\Validation;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use SymfonyCasts\Bundle\ResetPassword\Controller\ResetPasswordControllerTrait;
|
||||
use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface;
|
||||
use SymfonyCasts\Bundle\ResetPassword\Model\ResetPasswordRequestInterface;
|
||||
use SymfonyCasts\Bundle\ResetPassword\Model\ResetPasswordRequestTrait;
|
||||
use SymfonyCasts\Bundle\ResetPassword\Persistence\Repository\ResetPasswordRequestRepositoryTrait;
|
||||
use SymfonyCasts\Bundle\ResetPassword\Persistence\ResetPasswordRequestRepositoryInterface;
|
||||
use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelper;
|
||||
use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface;
|
||||
use SymfonyCasts\Bundle\ResetPassword\SymfonyCastsResetPasswordBundle;
|
||||
|
||||
/**
|
||||
* @author Romaric Drigon <romaric.drigon@gmail.com>
|
||||
* @author Jesse Rushlow <jr@rushlow.dev>
|
||||
* @author Ryan Weaver <ryan@symfonycasts.com>
|
||||
* @author Antoine Michelet <jean.marcel.michelet@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class MakeResetPassword extends AbstractMaker
|
||||
{
|
||||
use CanGenerateTestsTrait;
|
||||
use UidTrait;
|
||||
|
||||
private string $fromEmailAddress;
|
||||
private string $fromEmailName;
|
||||
private string $controllerResetSuccessRedirect;
|
||||
private ?RouteObject $controllerResetSuccessRoute = null;
|
||||
private string $userClass;
|
||||
private string $emailPropertyName;
|
||||
private string $emailGetterMethodName;
|
||||
private string $passwordSetterMethodName;
|
||||
|
||||
public function __construct(
|
||||
private FileManager $fileManager,
|
||||
private DoctrineHelper $doctrineHelper,
|
||||
private EntityClassGenerator $entityClassGenerator,
|
||||
private ?RouterInterface $router = null,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:reset-password';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create controller, entity, and repositories for use with symfonycasts/reset-password-bundle';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->setHelp($this->getHelpFileContents('MakeResetPassword.txt'))
|
||||
;
|
||||
|
||||
$this->addWithUuidOption($command);
|
||||
$this->configureCommandWithTestsOption($command);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$dependencies->addClassDependency(SymfonyCastsResetPasswordBundle::class, 'symfonycasts/reset-password-bundle');
|
||||
$dependencies->addClassDependency(MailerInterface::class, 'symfony/mailer');
|
||||
$dependencies->addClassDependency(Form::class, 'symfony/form');
|
||||
$dependencies->addClassDependency(Validation::class, 'symfony/validator');
|
||||
$dependencies->addClassDependency(SecurityBundle::class, 'security-bundle');
|
||||
$dependencies->addClassDependency(AppVariable::class, 'twig');
|
||||
|
||||
ORMDependencyBuilder::buildDependencies($dependencies);
|
||||
|
||||
// reset-password-bundle 1.6 includes the ability to generate a fake token.
|
||||
// we need to check that version 1.6 is installed
|
||||
if (class_exists(ResetPasswordHelper::class) && !method_exists(ResetPasswordHelper::class, 'generateFakeResetToken')) {
|
||||
throw new RuntimeCommandException('Please run "composer upgrade symfonycasts/reset-password-bundle". Version 1.6 or greater of this bundle is required.');
|
||||
}
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
$io->title('Let\'s make a password reset feature!');
|
||||
|
||||
$this->checkIsUsingUid($input);
|
||||
|
||||
$interactiveSecurityHelper = new InteractiveSecurityHelper();
|
||||
|
||||
if (!$this->fileManager->fileExists($path = 'config/packages/security.yaml')) {
|
||||
throw new RuntimeCommandException('The file "config/packages/security.yaml" does not exist. PHP & XML configuration formats are currently not supported.');
|
||||
}
|
||||
|
||||
$manipulator = new YamlSourceManipulator($this->fileManager->getFileContents($path));
|
||||
$securityData = $manipulator->getData();
|
||||
$providersData = $securityData['security']['providers'] ?? [];
|
||||
|
||||
$this->userClass = $interactiveSecurityHelper->guessUserClass(
|
||||
$io,
|
||||
$providersData,
|
||||
'What is the User entity that should be used with the "forgotten password" feature? (e.g. <fg=yellow>App\\Entity\\User</>)'
|
||||
);
|
||||
|
||||
$this->emailPropertyName = $interactiveSecurityHelper->guessEmailField($io, $this->userClass);
|
||||
$this->emailGetterMethodName = $interactiveSecurityHelper->guessEmailGetter($io, $this->userClass, $this->emailPropertyName);
|
||||
$this->passwordSetterMethodName = $interactiveSecurityHelper->guessPasswordSetter($io, $this->userClass);
|
||||
|
||||
$io->text(\sprintf('Implementing reset password for <info>%s</info>', $this->userClass));
|
||||
|
||||
$io->section('- ResetPasswordController -');
|
||||
$io->text('A named route is used for redirecting after a successful reset. Even a route that does not exist yet can be used here.');
|
||||
|
||||
$this->controllerResetSuccessRedirect = $io->ask(
|
||||
'What route should users be redirected to after their password has been successfully reset?',
|
||||
'app_home',
|
||||
Validator::notBlank(...)
|
||||
);
|
||||
|
||||
if ($this->router instanceof RouterInterface) {
|
||||
$this->controllerResetSuccessRoute = $this->router->getRouteCollection()->get($this->controllerResetSuccessRedirect);
|
||||
}
|
||||
|
||||
$io->section('- Email -');
|
||||
$emailText[] = 'These are used to generate the email code. Don\'t worry, you can change them in the code later!';
|
||||
$io->text($emailText);
|
||||
|
||||
$this->fromEmailAddress = $io->ask(
|
||||
'What email address will be used to send reset confirmations? e.g. mailer@your-domain.com',
|
||||
null,
|
||||
Validator::validateEmailAddress(...)
|
||||
);
|
||||
|
||||
$this->fromEmailName = $io->ask(
|
||||
'What "name" should be associated with that email address? e.g. "Acme Mail Bot"',
|
||||
null,
|
||||
Validator::notBlank(...)
|
||||
);
|
||||
|
||||
$this->interactSetGenerateTests($input, $io);
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$userClassNameDetails = $generator->createClassNameDetails(
|
||||
'\\'.$this->userClass,
|
||||
'Entity\\'
|
||||
);
|
||||
|
||||
$controllerClassNameDetails = $generator->createClassNameDetails(
|
||||
'ResetPasswordController',
|
||||
'Controller\\'
|
||||
);
|
||||
|
||||
$requestClassNameDetails = $generator->createClassNameDetails(
|
||||
'ResetPasswordRequest',
|
||||
'Entity\\'
|
||||
);
|
||||
|
||||
$repositoryClassNameDetails = $generator->createClassNameDetails(
|
||||
'ResetPasswordRequestRepository',
|
||||
'Repository\\'
|
||||
);
|
||||
|
||||
$requestFormTypeClassNameDetails = $generator->createClassNameDetails(
|
||||
'ResetPasswordRequestFormType',
|
||||
'Form\\'
|
||||
);
|
||||
|
||||
$changePasswordFormTypeClassNameDetails = $generator->createClassNameDetails(
|
||||
'ChangePasswordFormType',
|
||||
'Form\\'
|
||||
);
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
AbstractController::class,
|
||||
$userClassNameDetails->getFullName(),
|
||||
$changePasswordFormTypeClassNameDetails->getFullName(),
|
||||
$requestFormTypeClassNameDetails->getFullName(),
|
||||
TemplatedEmail::class,
|
||||
RedirectResponse::class,
|
||||
Request::class,
|
||||
Response::class,
|
||||
MailerInterface::class,
|
||||
Address::class,
|
||||
Route::class,
|
||||
ResetPasswordControllerTrait::class,
|
||||
ResetPasswordExceptionInterface::class,
|
||||
ResetPasswordHelperInterface::class,
|
||||
UserPasswordHasherInterface::class,
|
||||
EntityManagerInterface::class,
|
||||
]);
|
||||
|
||||
// Namespace for ResetPasswordExceptionInterface was imported above
|
||||
$problemValidateMessageOrConstant = \defined('SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface::MESSAGE_PROBLEM_VALIDATE')
|
||||
? 'ResetPasswordExceptionInterface::MESSAGE_PROBLEM_VALIDATE'
|
||||
: "'There was a problem validating your password reset request'";
|
||||
$problemHandleMessageOrConstant = \defined('SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface::MESSAGE_PROBLEM_HANDLE')
|
||||
? 'ResetPasswordExceptionInterface::MESSAGE_PROBLEM_HANDLE'
|
||||
: "'There was a problem handling your password reset request'";
|
||||
|
||||
if ($isTranslatorAvailable = class_exists(Translator::class)) {
|
||||
$useStatements->addUseStatement(TranslatorInterface::class);
|
||||
}
|
||||
|
||||
$generator->generateController(
|
||||
$controllerClassNameDetails->getFullName(),
|
||||
'resetPassword/ResetPasswordController.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'user_class_name' => $userClassNameDetails->getShortName(),
|
||||
'request_form_type_class_name' => $requestFormTypeClassNameDetails->getShortName(),
|
||||
'reset_form_type_class_name' => $changePasswordFormTypeClassNameDetails->getShortName(),
|
||||
'password_setter' => $this->passwordSetterMethodName,
|
||||
'success_redirect_route' => $this->controllerResetSuccessRedirect,
|
||||
'from_email' => $this->fromEmailAddress,
|
||||
'from_email_name' => $this->fromEmailName,
|
||||
'email_getter' => $this->emailGetterMethodName,
|
||||
'email_field' => $this->emailPropertyName,
|
||||
'problem_validate_message_or_constant' => $problemValidateMessageOrConstant,
|
||||
'problem_handle_message_or_constant' => $problemHandleMessageOrConstant,
|
||||
'translator_available' => $isTranslatorAvailable,
|
||||
]
|
||||
);
|
||||
|
||||
$this->generateRequestEntity($generator, $requestClassNameDetails, $repositoryClassNameDetails, $userClassNameDetails);
|
||||
|
||||
$this->setBundleConfig($io, $generator, $repositoryClassNameDetails->getFullName());
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
AbstractType::class,
|
||||
EmailType::class,
|
||||
FormBuilderInterface::class,
|
||||
OptionsResolver::class,
|
||||
NotBlank::class,
|
||||
]);
|
||||
|
||||
$generator->generateClass(
|
||||
$requestFormTypeClassNameDetails->getFullName(),
|
||||
'resetPassword/ResetPasswordRequestFormType.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'email_field' => $this->emailPropertyName,
|
||||
]
|
||||
);
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
AbstractType::class,
|
||||
PasswordType::class,
|
||||
RepeatedType::class,
|
||||
FormBuilderInterface::class,
|
||||
OptionsResolver::class,
|
||||
Length::class,
|
||||
NotBlank::class,
|
||||
NotCompromisedPassword::class,
|
||||
PasswordStrength::class,
|
||||
]);
|
||||
|
||||
$generator->generateClass(
|
||||
$changePasswordFormTypeClassNameDetails->getFullName(),
|
||||
'resetPassword/ChangePasswordFormType.tpl.php',
|
||||
['use_statements' => $useStatements]
|
||||
);
|
||||
|
||||
$generator->generateTemplate(
|
||||
'reset_password/check_email.html.twig',
|
||||
'resetPassword/twig_check_email.tpl.php'
|
||||
);
|
||||
|
||||
$generator->generateTemplate(
|
||||
'reset_password/email.html.twig',
|
||||
'resetPassword/twig_email.tpl.php'
|
||||
);
|
||||
|
||||
$generator->generateTemplate(
|
||||
'reset_password/request.html.twig',
|
||||
'resetPassword/twig_request.tpl.php',
|
||||
[
|
||||
'email_field' => $this->emailPropertyName,
|
||||
]
|
||||
);
|
||||
|
||||
$generator->generateTemplate(
|
||||
'reset_password/reset.html.twig',
|
||||
'resetPassword/twig_reset.tpl.php'
|
||||
);
|
||||
|
||||
// Generate PHPUnit tests
|
||||
if ($this->shouldGenerateTests()) {
|
||||
$testClassDetails = $generator->createClassNameDetails(
|
||||
'ResetPasswordControllerTest',
|
||||
'Test\\',
|
||||
);
|
||||
|
||||
$userRepositoryDetails = $generator->createClassNameDetails(
|
||||
\sprintf('%sRepository', $userClassNameDetails->getShortName()),
|
||||
'Repository\\'
|
||||
);
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
$userClassNameDetails->getFullName(),
|
||||
$userRepositoryDetails->getFullName(),
|
||||
EntityManagerInterface::class,
|
||||
KernelBrowser::class,
|
||||
WebTestCase::class,
|
||||
UserPasswordHasherInterface::class,
|
||||
]);
|
||||
|
||||
$generator->generateFile(
|
||||
targetPath: \sprintf('tests/%s.php', $testClassDetails->getShortName()),
|
||||
templateName: 'resetPassword/Test.ResetPasswordController.tpl.php',
|
||||
variables: [
|
||||
'use_statements' => $useStatements,
|
||||
'user_short_name' => $userClassNameDetails->getShortName(),
|
||||
'user_repo_short_name' => $userRepositoryDetails->getShortName(),
|
||||
'success_route_path' => null !== $this->controllerResetSuccessRoute ? $this->controllerResetSuccessRoute->getPath() : '/',
|
||||
'from_email' => $this->fromEmailAddress,
|
||||
],
|
||||
);
|
||||
|
||||
if (!class_exists(WebTestCase::class)) {
|
||||
$io->caution('You\'ll need to install the `symfony/test-pack` to execute the tests for your new controller.');
|
||||
}
|
||||
}
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
$this->successMessage($io, $requestClassNameDetails->getFullName());
|
||||
}
|
||||
|
||||
private function setBundleConfig(ConsoleStyle $io, Generator $generator, string $repositoryClassFullName): void
|
||||
{
|
||||
$configFileExists = $this->fileManager->fileExists($path = 'config/packages/reset_password.yaml');
|
||||
|
||||
/*
|
||||
* reset_password.yaml does not exist, we assume flex was present when
|
||||
* the bundle was installed & a customized configuration is in use.
|
||||
* Remind the developer to set the repository class accordingly.
|
||||
*/
|
||||
if (!$configFileExists) {
|
||||
$io->text(\sprintf('We can\'t find %s. That\'s ok, you probably have a customized configuration.', $path));
|
||||
$io->text('Just remember to set the <fg=yellow>request_password_repository</> in your configuration.');
|
||||
$io->newLine();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$manipulator = new YamlSourceManipulator($this->fileManager->getFileContents($path));
|
||||
$data = $manipulator->getData();
|
||||
|
||||
$symfonyCastsKey = 'symfonycasts_reset_password';
|
||||
|
||||
/*
|
||||
* reset_password.yaml exists, and was probably created by flex;
|
||||
* Let's replace it with a "clean" file.
|
||||
*/
|
||||
if (1 >= (is_countable($data[$symfonyCastsKey]) ? \count($data[$symfonyCastsKey]) : 0)) {
|
||||
$yaml = [
|
||||
$symfonyCastsKey => [
|
||||
'request_password_repository' => $repositoryClassFullName,
|
||||
],
|
||||
];
|
||||
|
||||
$generator->dumpFile($path, Yaml::dump($yaml));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* reset_password.yaml exists and appears to have been customized
|
||||
* before running make:reset-password. Let's just change the repository
|
||||
* value and preserve everything else.
|
||||
*/
|
||||
$data[$symfonyCastsKey]['request_password_repository'] = $repositoryClassFullName;
|
||||
|
||||
$manipulator->setData($data);
|
||||
|
||||
$generator->dumpFile($path, $manipulator->getContents());
|
||||
}
|
||||
|
||||
private function successMessage(ConsoleStyle $io, string $requestClassName): void
|
||||
{
|
||||
$closing[] = 'Next:';
|
||||
$closing[] = \sprintf(' 1) Run <fg=yellow>"%s make:migration"</> to generate a migration for the new <fg=yellow>"%s"</> entity.', CliOutputHelper::getCommandPrefix(), $requestClassName);
|
||||
$closing[] = ' 2) Review forms in <fg=yellow>"src/Form"</> to customize validation and labels.';
|
||||
$closing[] = ' 3) Review and customize the templates in <fg=yellow>`templates/reset_password`</>.';
|
||||
$closing[] = ' 4) Make sure your <fg=yellow>MAILER_DSN</> env var has the correct settings.';
|
||||
$closing[] = ' 5) Create a "forgot your password link" to the <fg=yellow>app_forgot_password_request</> route on your login form.';
|
||||
|
||||
$io->text($closing);
|
||||
$io->newLine();
|
||||
$io->text('Then open your browser, go to "/reset-password" and enjoy!');
|
||||
$io->newLine();
|
||||
}
|
||||
|
||||
private function generateRequestEntity(Generator $generator, ClassNameDetails $requestClassNameDetails, ClassNameDetails $repositoryClassNameDetails, ClassNameDetails $userClassDetails): void
|
||||
{
|
||||
// Generate ResetPasswordRequest Entity
|
||||
$requestEntityPath = $this->entityClassGenerator->generateEntityClass(
|
||||
entityClassDetails: $requestClassNameDetails,
|
||||
apiResource: false,
|
||||
generateRepositoryClass: false,
|
||||
useUuidIdentifier: $this->getIdType()
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$manipulator = new ClassSourceManipulator(
|
||||
sourceCode: $this->fileManager->getFileContents($requestEntityPath),
|
||||
overwrite: false,
|
||||
useAttributesForDoctrineMapping: $this->doctrineHelper->doesClassUsesAttributes($requestClassNameDetails->getFullName()),
|
||||
);
|
||||
|
||||
$manipulator->addInterface(ResetPasswordRequestInterface::class);
|
||||
|
||||
$manipulator->addTrait(ResetPasswordRequestTrait::class);
|
||||
|
||||
$manipulator->addUseStatementIfNecessary($userClassDetails->getFullName());
|
||||
|
||||
$manipulator->addConstructor([
|
||||
(new Param('user'))->setType($userClassDetails->getShortName())->getNode(),
|
||||
(new Param('expiresAt'))->setType('\DateTimeInterface')->getNode(),
|
||||
(new Param('selector'))->setType('string')->getNode(),
|
||||
(new Param('hashedToken'))->setType('string')->getNode(),
|
||||
], <<<'CODE'
|
||||
<?php
|
||||
$this->user = $user;
|
||||
$this->initialize($expiresAt, $selector, $hashedToken);
|
||||
CODE
|
||||
);
|
||||
|
||||
$manipulator->addManyToOneRelation(new RelationManyToOne(
|
||||
propertyName: 'user',
|
||||
targetClassName: $this->userClass,
|
||||
mapInverseRelation: false,
|
||||
avoidSetter: true,
|
||||
isCustomReturnTypeNullable: false,
|
||||
customReturnType: $userClassDetails->getShortName(),
|
||||
isOwning: true,
|
||||
));
|
||||
|
||||
$this->fileManager->dumpFile($requestEntityPath, $manipulator->getSourceCode());
|
||||
|
||||
$this->entityClassGenerator->generateRepositoryClass(
|
||||
$repositoryClassNameDetails->getFullName(),
|
||||
$requestClassNameDetails->getFullName(),
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
// Generate ResetPasswordRequestRepository
|
||||
$pathRequestRepository = $this->fileManager->getRelativePathForFutureClass(
|
||||
$repositoryClassNameDetails->getFullName()
|
||||
);
|
||||
|
||||
$manipulator = new ClassSourceManipulator(
|
||||
sourceCode: $this->fileManager->getFileContents($pathRequestRepository)
|
||||
);
|
||||
|
||||
$manipulator->addInterface(ResetPasswordRequestRepositoryInterface::class);
|
||||
|
||||
$manipulator->addTrait(ResetPasswordRequestRepositoryTrait::class);
|
||||
|
||||
$methodBuilder = $manipulator->createMethodBuilder(
|
||||
methodName: 'createResetPasswordRequest',
|
||||
returnType: ResetPasswordRequestInterface::class,
|
||||
isReturnTypeNullable: false,
|
||||
commentLines: [\sprintf('@param %s $user', $userClassDetails->getShortName())]
|
||||
);
|
||||
|
||||
$manipulator->addUseStatementIfNecessary($userClassDetails->getFullName());
|
||||
|
||||
$manipulator->addMethodBuilder($methodBuilder, [
|
||||
(new Param('user'))->setType('object')->getNode(),
|
||||
(new Param('expiresAt'))->setType('\DateTimeInterface')->getNode(),
|
||||
(new Param('selector'))->setType('string')->getNode(),
|
||||
(new Param('hashedToken'))->setType('string')->getNode(),
|
||||
], <<<'CODE'
|
||||
<?php
|
||||
return new ResetPasswordRequest($user, $expiresAt, $selector, $hashedToken);
|
||||
CODE
|
||||
);
|
||||
|
||||
$this->fileManager->dumpFile($pathRequestRepository, $manipulator->getSourceCode());
|
||||
}
|
||||
}
|
||||
149
vendor/symfony/maker-bundle/src/Maker/MakeSchedule.php
vendored
Normal file
149
vendor/symfony/maker-bundle/src/Maker/MakeSchedule.php
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\FileManager;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Symfony\Component\Scheduler\Attribute\AsSchedule;
|
||||
use Symfony\Component\Scheduler\RecurringMessage;
|
||||
use Symfony\Component\Scheduler\Schedule;
|
||||
use Symfony\Component\Scheduler\ScheduleProviderInterface;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
/**
|
||||
* @author Jesse Rushlow <jr@rushlow.dev>
|
||||
*
|
||||
* @deprecated since MakerBundle v1.63.0, use symfony/scheduler recipe instead,
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class MakeSchedule extends AbstractMaker
|
||||
{
|
||||
private string $scheduleName;
|
||||
private ?string $message = null;
|
||||
private ?string $transportName = null;
|
||||
|
||||
public function __construct(
|
||||
private FileManager $fileManager,
|
||||
private Finder $finder = new Finder(),
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:schedule';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a scheduler component';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->setHelp($this->getHelpFileContents('MakeScheduler.txt'))
|
||||
;
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
trigger_deprecation('symfony/maker-bundle', 'v1.63.0', '"make:schedule" is deprecated, install the symfony/scheduler recipe instead.');
|
||||
|
||||
if (!class_exists(AsSchedule::class)) {
|
||||
$io->writeln('Running composer require symfony/scheduler');
|
||||
$process = Process::fromShellCommandline('composer require symfony/scheduler');
|
||||
$process->run();
|
||||
$io->writeln('Scheduler successfully installed!');
|
||||
}
|
||||
|
||||
// Loop over existing src/Message/* and ask which message the user would like to schedule
|
||||
$availableMessages = ['Empty Schedule'];
|
||||
$messageDir = $this->fileManager->getRootDirectory().'/src/Message';
|
||||
|
||||
if ($this->fileManager->fileExists($messageDir)) {
|
||||
$finder = $this->finder->in($this->fileManager->getRootDirectory().'/src/Message');
|
||||
|
||||
foreach ($finder->files() as $file) {
|
||||
$availableMessages[] = $file->getFilenameWithoutExtension();
|
||||
}
|
||||
}
|
||||
|
||||
$this->transportName = $io->ask('What should we call the new transport? (To be used for the attribute #[AsSchedule(name)])');
|
||||
|
||||
$scheduleNameHint = 'MainSchedule';
|
||||
|
||||
// If the count is 1, no other messages were found - don't ask to create a message
|
||||
if (1 !== \count($availableMessages)) {
|
||||
$selectedMessage = $io->choice('Select which message', $availableMessages);
|
||||
|
||||
if ('Empty Schedule' !== $selectedMessage) {
|
||||
$this->message = $selectedMessage;
|
||||
|
||||
// We don't want SomeMessageSchedule, so remove the "Message" suffix to give us SomeSchedule
|
||||
$scheduleNameHint = \sprintf('%sSchedule', Str::removeSuffix($selectedMessage, 'Message'));
|
||||
}
|
||||
}
|
||||
|
||||
// Ask the name of the new schedule
|
||||
$this->scheduleName = $io->ask(question: 'What should we call the new schedule?', default: $scheduleNameHint);
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$scheduleClassDetails = $generator->createClassNameDetails(
|
||||
$this->scheduleName,
|
||||
'Scheduler\\',
|
||||
);
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
AsSchedule::class,
|
||||
RecurringMessage::class,
|
||||
Schedule::class,
|
||||
ScheduleProviderInterface::class,
|
||||
CacheInterface::class,
|
||||
]);
|
||||
|
||||
if (null !== $this->message) {
|
||||
$useStatements->addUseStatement('App\\Message\\'.$this->message);
|
||||
}
|
||||
|
||||
$generator->generateClass(
|
||||
$scheduleClassDetails->getFullName(),
|
||||
'scheduler/Schedule.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'has_custom_message' => null !== $this->message,
|
||||
'message_class_name' => $this->message,
|
||||
'has_transport_name' => null !== $this->transportName,
|
||||
'transport_name' => $this->transportName,
|
||||
],
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
}
|
||||
}
|
||||
93
vendor/symfony/maker-bundle/src/Maker/MakeSerializerEncoder.php
vendored
Normal file
93
vendor/symfony/maker-bundle/src/Maker/MakeSerializerEncoder.php
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
use Symfony\Component\Serializer\Encoder\DecoderInterface;
|
||||
use Symfony\Component\Serializer\Encoder\EncoderInterface;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
|
||||
/**
|
||||
* @author Piotr Grabski-Gradzinski <piotr.gradzinski@gmail.com>
|
||||
*/
|
||||
final class MakeSerializerEncoder extends AbstractMaker
|
||||
{
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:serializer:encoder';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new serializer encoder class';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'Choose a class name for your encoder (e.g. <fg=yellow>YamlEncoder</>)')
|
||||
->addArgument('format', InputArgument::OPTIONAL, 'Pick your format name (e.g. <fg=yellow>yaml</>)')
|
||||
->setHelp($this->getHelpFileContents('MakeSerializerEncoder.txt'))
|
||||
;
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$encoderClassNameDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('name'),
|
||||
'Serializer\\',
|
||||
'Encoder'
|
||||
);
|
||||
$format = $input->getArgument('format');
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
DecoderInterface::class,
|
||||
EncoderInterface::class,
|
||||
]);
|
||||
|
||||
/* @legacy - Remove "decoder_return_type" when Symfony 6.4 is no longer supported */
|
||||
$generator->generateClass(
|
||||
$encoderClassNameDetails->getFullName(),
|
||||
'serializer/Encoder.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'format' => $format,
|
||||
'use_decoder_return_type' => Kernel::VERSION_ID >= 70000,
|
||||
]
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next: Open your new serializer encoder class and start customizing it.',
|
||||
'Find the documentation at <fg=yellow>http://symfony.com/doc/current/serializer/custom_encoders.html</>',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
Serializer::class,
|
||||
'serializer'
|
||||
);
|
||||
}
|
||||
}
|
||||
108
vendor/symfony/maker-bundle/src/Maker/MakeSerializerNormalizer.php
vendored
Normal file
108
vendor/symfony/maker-bundle/src/Maker/MakeSerializerNormalizer.php
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\FileManager;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
|
||||
/**
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*/
|
||||
final class MakeSerializerNormalizer extends AbstractMaker
|
||||
{
|
||||
public function __construct(private ?FileManager $fileManager = null)
|
||||
{
|
||||
if (null !== $this->fileManager) {
|
||||
@trigger_deprecation(
|
||||
'symfony/maker-bundle',
|
||||
'1.56.0',
|
||||
\sprintf('Initializing MakeSerializerNormalizer while providing an instance of "%s" is deprecated. The $fileManager param will be removed in a future version.', FileManager::class)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:serializer:normalizer';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new serializer normalizer class';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'Choose a class name for your normalizer (e.g. <fg=yellow>UserNormalizer</>)')
|
||||
->setHelp($this->getHelpFileContents('MakeSerializerNormalizer.txt'))
|
||||
;
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$normalizerClassNameDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('name'),
|
||||
'Serializer\\Normalizer\\',
|
||||
\Normalizer::class
|
||||
);
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
NormalizerInterface::class,
|
||||
Autowire::class,
|
||||
\sprintf('App\Entity\%s', str_replace('Normalizer', '', $normalizerClassNameDetails->getShortName())),
|
||||
]);
|
||||
|
||||
$entityDetails = $generator->createClassNameDetails(
|
||||
str_replace('Normalizer', '', $normalizerClassNameDetails->getShortName()),
|
||||
'Entity\\',
|
||||
);
|
||||
|
||||
if ($entityExists = class_exists($entityDetails->getFullName())) {
|
||||
$useStatements->addUseStatement($entityDetails->getFullName());
|
||||
}
|
||||
|
||||
$generator->generateClass($normalizerClassNameDetails->getFullName(), 'serializer/Normalizer.tpl.php', [
|
||||
'use_statements' => $useStatements,
|
||||
'entity_exists' => $entityExists,
|
||||
'entity_name' => $entityDetails->getShortName(),
|
||||
]);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next:',
|
||||
' - Open your new serializer normalizer class and start customizing it.',
|
||||
' - Find the documentation at <fg=yellow>https://symfony.com/doc/current/serializer/custom_normalizer.html</>',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
Serializer::class,
|
||||
'serializer'
|
||||
);
|
||||
}
|
||||
}
|
||||
358
vendor/symfony/maker-bundle/src/Maker/MakeStimulusController.php
vendored
Normal file
358
vendor/symfony/maker-bundle/src/Maker/MakeStimulusController.php
vendored
Normal file
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Symfony\UX\StimulusBundle\StimulusBundle;
|
||||
use Symfony\WebpackEncoreBundle\WebpackEncoreBundle;
|
||||
|
||||
/**
|
||||
* @author Abdelilah Jabri <jbrabdelilah@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class MakeStimulusController extends AbstractMaker
|
||||
{
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:stimulus-controller';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new Stimulus controller';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::REQUIRED, 'The name of the Stimulus controller (e.g. <fg=yellow>hello</>)')
|
||||
->addOption('typescript', 'ts', InputOption::VALUE_NONE, 'Create a TypeScript controller (default is JavaScript)')
|
||||
->setHelp($this->getHelpFileContents('MakeStimulusController.txt'))
|
||||
;
|
||||
|
||||
$inputConfig->setArgumentAsNonInteractive('typescript');
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
$command->addArgument('extension', InputArgument::OPTIONAL);
|
||||
$command->addArgument('targets', InputArgument::OPTIONAL);
|
||||
$command->addArgument('values', InputArgument::OPTIONAL);
|
||||
$command->addArgument('classes', InputArgument::OPTIONAL);
|
||||
|
||||
if ($input->getOption('typescript')) {
|
||||
$input->setArgument('extension', 'ts');
|
||||
} else {
|
||||
$chosenExtension = $io->choice(
|
||||
'Language (<fg=yellow>JavaScript</> or <fg=yellow>TypeScript</>)',
|
||||
[
|
||||
'js' => 'JavaScript',
|
||||
'ts' => 'TypeScript',
|
||||
],
|
||||
'js',
|
||||
);
|
||||
|
||||
$input->setArgument('extension', $chosenExtension);
|
||||
}
|
||||
|
||||
if ($io->confirm('Do you want to include targets?')) {
|
||||
$targets = [];
|
||||
$isFirstTarget = true;
|
||||
|
||||
while (true) {
|
||||
$newTarget = $this->askForNextTarget($io, $targets, $isFirstTarget);
|
||||
$isFirstTarget = false;
|
||||
|
||||
if (null === $newTarget) {
|
||||
break;
|
||||
}
|
||||
|
||||
$targets[] = $newTarget;
|
||||
}
|
||||
|
||||
$input->setArgument('targets', $targets);
|
||||
}
|
||||
|
||||
if ($io->confirm('Do you want to include values?')) {
|
||||
$values = [];
|
||||
$isFirstValue = true;
|
||||
while (true) {
|
||||
$newValue = $this->askForNextValue($io, $values, $isFirstValue);
|
||||
$isFirstValue = false;
|
||||
|
||||
if (null === $newValue) {
|
||||
break;
|
||||
}
|
||||
|
||||
$values[$newValue['name']] = $newValue;
|
||||
}
|
||||
|
||||
$input->setArgument('values', $values);
|
||||
}
|
||||
|
||||
if ($io->confirm('Do you want to add classes?', false)) {
|
||||
$classes = [];
|
||||
$isFirstClass = true;
|
||||
|
||||
while (true) {
|
||||
$newClass = $this->askForNextClass($io, $classes, $isFirstClass);
|
||||
if (null === $newClass) {
|
||||
break;
|
||||
}
|
||||
|
||||
$isFirstClass = false;
|
||||
$classes[] = $newClass;
|
||||
}
|
||||
|
||||
$input->setArgument('classes', $classes);
|
||||
}
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$controllerName = Str::asSnakeCase($input->getArgument('name'));
|
||||
$chosenExtension = $input->getArgument('extension');
|
||||
$targets = $targetArgs = $input->getArgument('targets') ?? [];
|
||||
$values = $valuesArg = $input->getArgument('values') ?? [];
|
||||
$classes = $classesArgs = $input->getArgument('classes') ?? [];
|
||||
|
||||
$targets = empty($targets) ? $targets : \sprintf("['%s']", implode("', '", $targets));
|
||||
$classes = $classes ? \sprintf("['%s']", implode("', '", $classes)) : null;
|
||||
|
||||
$fileName = \sprintf('%s_controller.%s', $controllerName, $chosenExtension);
|
||||
$filePath = \sprintf('assets/controllers/%s', $fileName);
|
||||
|
||||
$generator->generateFile(
|
||||
$filePath,
|
||||
'stimulus/Controller.tpl.php',
|
||||
[
|
||||
'targets' => $targets,
|
||||
'values' => $values,
|
||||
'classes' => $classes,
|
||||
]
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next:',
|
||||
\sprintf('- Open <info>%s</info> and add the code you need', $filePath),
|
||||
'- Use the controller in your templates:',
|
||||
...array_map(
|
||||
fn (string $line): string => " $line",
|
||||
explode("\n", $this->generateUsageExample($controllerName, $targetArgs, $valuesArg, $classesArgs)),
|
||||
),
|
||||
'Find the documentation at <fg=yellow>https://symfony.com/bundles/StimulusBundle</>',
|
||||
]);
|
||||
}
|
||||
|
||||
/** @param string[] $targets */
|
||||
private function askForNextTarget(ConsoleStyle $io, array $targets, bool $isFirstTarget): ?string
|
||||
{
|
||||
$questionText = 'New target name (press <return> to stop adding targets)';
|
||||
|
||||
if (!$isFirstTarget) {
|
||||
$questionText = 'Add another target? Enter the target name (or press <return> to stop adding targets)';
|
||||
}
|
||||
|
||||
$targetName = $io->ask($questionText, validator: function (?string $name) use ($targets) {
|
||||
if (\in_array($name, $targets)) {
|
||||
throw new \InvalidArgumentException(\sprintf('The "%s" target already exists.', $name));
|
||||
}
|
||||
|
||||
return $name;
|
||||
});
|
||||
|
||||
return !$targetName ? null : $targetName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string, string>> $values
|
||||
*
|
||||
* @return array<string, string>|null
|
||||
*/
|
||||
private function askForNextValue(ConsoleStyle $io, array $values, bool $isFirstValue): ?array
|
||||
{
|
||||
$questionText = 'New value name (press <return> to stop adding values)';
|
||||
|
||||
if (!$isFirstValue) {
|
||||
$questionText = 'Add another value? Enter the value name (or press <return> to stop adding values)';
|
||||
}
|
||||
|
||||
$valueName = $io->ask($questionText, null, function ($name) use ($values) {
|
||||
if (\array_key_exists($name, $values)) {
|
||||
throw new \InvalidArgumentException(\sprintf('The "%s" value already exists.', $name));
|
||||
}
|
||||
|
||||
return $name;
|
||||
});
|
||||
|
||||
if (!$valueName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$defaultType = 'String';
|
||||
// try to guess the type by the value name prefix/suffix
|
||||
// convert to snake case for simplicity
|
||||
$snakeCasedField = Str::asSnakeCase($valueName);
|
||||
|
||||
if (str_ends_with($snakeCasedField, '_id')) {
|
||||
$defaultType = 'Number';
|
||||
} elseif (str_starts_with($snakeCasedField, 'is_')) {
|
||||
$defaultType = 'Boolean';
|
||||
} elseif (str_starts_with($snakeCasedField, 'has_')) {
|
||||
$defaultType = 'Boolean';
|
||||
}
|
||||
|
||||
$type = null;
|
||||
$types = $this->getValuesTypes();
|
||||
|
||||
while (null === $type) {
|
||||
$question = new Question('Value type (enter <comment>?</comment> to see all types)', $defaultType);
|
||||
$question->setAutocompleterValues($types);
|
||||
$type = $io->askQuestion($question);
|
||||
|
||||
if ('?' === $type) {
|
||||
$this->printAvailableTypes($io);
|
||||
$io->writeln('');
|
||||
|
||||
$type = null;
|
||||
} elseif (!\in_array($type, $types)) {
|
||||
$this->printAvailableTypes($io);
|
||||
$io->error(\sprintf('Invalid type "%s".', $type));
|
||||
$io->writeln('');
|
||||
|
||||
$type = null;
|
||||
}
|
||||
}
|
||||
|
||||
return ['name' => $valueName, 'type' => $type];
|
||||
}
|
||||
|
||||
/** @param string[] $classes */
|
||||
private function askForNextClass(ConsoleStyle $io, array $classes, bool $isFirstClass): ?string
|
||||
{
|
||||
$questionText = 'New class name (press <return> to stop adding classes)';
|
||||
|
||||
if (!$isFirstClass) {
|
||||
$questionText = 'Add another class? Enter the class name (or press <return> to stop adding classes)';
|
||||
}
|
||||
|
||||
$className = $io->ask($questionText, validator: function (?string $name) use ($classes) {
|
||||
if (str_contains($name, ' ')) {
|
||||
throw new \InvalidArgumentException('Class name cannot contain spaces.');
|
||||
}
|
||||
if (\in_array($name, $classes, true)) {
|
||||
throw new \InvalidArgumentException(\sprintf('The "%s" class already exists.', $name));
|
||||
}
|
||||
|
||||
return $name;
|
||||
});
|
||||
|
||||
return $className ?: null;
|
||||
}
|
||||
|
||||
private function printAvailableTypes(ConsoleStyle $io): void
|
||||
{
|
||||
foreach ($this->getValuesTypes() as $type) {
|
||||
$io->writeln(\sprintf('<info>%s</info>', $type));
|
||||
}
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
private function getValuesTypes(): array
|
||||
{
|
||||
return [
|
||||
'Array',
|
||||
'Boolean',
|
||||
'Number',
|
||||
'Object',
|
||||
'String',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $targets
|
||||
* @param array<array{name: string, type: string}> $values
|
||||
* @param array<int, string> $classes
|
||||
*/
|
||||
private function generateUsageExample(string $name, array $targets, array $values, array $classes): string
|
||||
{
|
||||
$slugify = fn (string $name) => str_replace('_', '-', Str::asSnakeCase($name));
|
||||
$controller = $slugify($name);
|
||||
|
||||
$htmlTargets = [];
|
||||
foreach ($targets as $target) {
|
||||
$htmlTargets[] = \sprintf('<div data-%s-target="%s"></div>', $controller, $target);
|
||||
}
|
||||
|
||||
$htmlValues = [];
|
||||
foreach ($values as ['name' => $name, 'type' => $type]) {
|
||||
$value = match ($type) {
|
||||
'Array' => '[]',
|
||||
'Boolean' => 'false',
|
||||
'Number' => '123',
|
||||
'Object' => '{}',
|
||||
'String' => 'abc',
|
||||
default => '',
|
||||
};
|
||||
$htmlValues[] = \sprintf('data-%s-%s-value="%s"', $controller, $slugify($name), $value);
|
||||
}
|
||||
|
||||
$htmlClasses = [];
|
||||
foreach ($classes as $class) {
|
||||
$value = Str::asLowerCamelCase($class);
|
||||
$htmlClasses[] = \sprintf('data-%s-%s-class="%s"', $controller, $slugify($class), $value);
|
||||
}
|
||||
|
||||
return \sprintf(
|
||||
'<div data-controller="%s"%s%s%s>%s%s</div>',
|
||||
$controller,
|
||||
$htmlValues ? ("\n ".implode("\n ", $htmlValues)) : '',
|
||||
$htmlClasses ? ("\n ".implode("\n ", $htmlClasses)) : '',
|
||||
($htmlValues || $htmlClasses) ? "\n" : '',
|
||||
$htmlTargets ? ("\n ".implode("\n ", $htmlTargets)) : '',
|
||||
"\n <!-- ... -->\n",
|
||||
);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
// lower than 8.1, allow WebpackEncoreBundle
|
||||
if (\PHP_VERSION_ID < 80100) {
|
||||
$dependencies->addClassDependency(
|
||||
WebpackEncoreBundle::class,
|
||||
'symfony/webpack-encore-bundle'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// else: encourage StimulusBundle by requiring it
|
||||
$dependencies->addClassDependency(
|
||||
StimulusBundle::class,
|
||||
'symfony/stimulus-bundle'
|
||||
);
|
||||
}
|
||||
}
|
||||
142
vendor/symfony/maker-bundle/src/Maker/MakeSubscriber.php
vendored
Normal file
142
vendor/symfony/maker-bundle/src/Maker/MakeSubscriber.php
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\EventRegistry;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Bundle\MakerBundle\Validator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
trigger_deprecation('symfony/maker-bundle', '1.51', 'The "%s" class is deprecated, use "%s" instead.', MakeSubscriber::class, MakeListener::class);
|
||||
|
||||
/**
|
||||
* @deprecated since MakerBundle 1.51, use Symfony\Bundle\MakerBundle\Maker\MakeListener instead.
|
||||
*
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*/
|
||||
final class MakeSubscriber extends AbstractMaker
|
||||
{
|
||||
public function __construct(private EventRegistry $eventRegistry)
|
||||
{
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:subscriber';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new event subscriber class';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'Choose a class name for your event subscriber (e.g. <fg=yellow>ExceptionSubscriber</>)')
|
||||
->addArgument('event', InputArgument::OPTIONAL, 'What event do you want to subscribe to?')
|
||||
->setHelp($this->getHelpFileContents('MakeSubscriber.txt'))
|
||||
;
|
||||
|
||||
$inputConfig->setArgumentAsNonInteractive('event');
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
if (!$input->getArgument('event')) {
|
||||
$events = $this->eventRegistry->getAllActiveEvents();
|
||||
|
||||
$io->writeln(' <fg=green>Suggested Events:</>');
|
||||
$io->listing($this->eventRegistry->listActiveEvents($events));
|
||||
$question = new Question(\sprintf(' <fg=green>%s</>', $command->getDefinition()->getArgument('event')->getDescription()));
|
||||
$question->setAutocompleterValues($events);
|
||||
$question->setValidator(Validator::notBlank(...));
|
||||
$event = $io->askQuestion($question);
|
||||
$input->setArgument('event', $event);
|
||||
}
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$subscriberClassNameDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('name'),
|
||||
'EventSubscriber\\',
|
||||
'Subscriber'
|
||||
);
|
||||
|
||||
$event = $input->getArgument('event');
|
||||
$eventFullClassName = $this->eventRegistry->getEventClassName($event);
|
||||
$eventClassName = $eventFullClassName ? Str::getShortClassName($eventFullClassName) : null;
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
EventSubscriberInterface::class,
|
||||
]);
|
||||
|
||||
// Determine if we use a KernelEvents::CONSTANT or custom even name
|
||||
if (null !== ($eventConstant = $this->getEventConstant($event))) {
|
||||
$useStatements->addUseStatement(KernelEvents::class);
|
||||
$eventName = $eventConstant;
|
||||
} else {
|
||||
$eventName = class_exists($event) ? \sprintf('%s::class', $eventClassName) : \sprintf('\'%s\'', $event);
|
||||
}
|
||||
|
||||
if (null !== $eventFullClassName) {
|
||||
$useStatements->addUseStatement($eventFullClassName);
|
||||
}
|
||||
|
||||
$generator->generateClass(
|
||||
$subscriberClassNameDetails->getFullName(),
|
||||
'event/Subscriber.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'event' => $eventName,
|
||||
'event_arg' => $eventClassName ? \sprintf('%s $event', $eventClassName) : '$event',
|
||||
'method_name' => class_exists($event) ? Str::asEventMethod($eventClassName) : Str::asEventMethod($event),
|
||||
]
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next: Open your new subscriber class and start customizing it.',
|
||||
'Find the documentation at <fg=yellow>https://symfony.com/doc/current/event_dispatcher.html#creating-an-event-subscriber</>',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
}
|
||||
|
||||
private function getEventConstant(string $event): ?string
|
||||
{
|
||||
$constants = (new \ReflectionClass(KernelEvents::class))->getConstants();
|
||||
|
||||
if (false !== ($name = array_search($event, $constants, true))) {
|
||||
return \sprintf('KernelEvents::%s', $name);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
229
vendor/symfony/maker-bundle/src/Maker/MakeTest.php
vendored
Normal file
229
vendor/symfony/maker-bundle/src/Maker/MakeTest.php
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestAssertionsTrait;
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputAwareMakerInterface;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Validator;
|
||||
use Symfony\Component\BrowserKit\History;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\CssSelector\CssSelectorConverter;
|
||||
use Symfony\Component\Panther\PantherTestCaseTrait;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <kevin@dunglas.fr>
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*/
|
||||
final class MakeTest extends AbstractMaker implements InputAwareMakerInterface
|
||||
{
|
||||
private const DESCRIPTIONS = [
|
||||
'TestCase' => 'basic PHPUnit tests',
|
||||
'KernelTestCase' => 'basic tests that have access to Symfony services',
|
||||
'WebTestCase' => 'to run browser-like scenarios, but that don\'t execute JavaScript code',
|
||||
'ApiTestCase' => 'to run API-oriented scenarios',
|
||||
'PantherTestCase' => 'to run e2e scenarios, using a real-browser or HTTP client and a real web server',
|
||||
];
|
||||
private const DOCS = [
|
||||
'TestCase' => 'https://symfony.com/doc/current/testing.html#unit-tests',
|
||||
'KernelTestCase' => 'https://symfony.com/doc/current/testing/database.html#functional-testing-of-a-doctrine-repository',
|
||||
'WebTestCase' => 'https://symfony.com/doc/current/testing.html#functional-tests',
|
||||
'ApiTestCase' => 'https://api-platform.com/docs/distribution/testing/',
|
||||
'PantherTestCase' => 'https://github.com/symfony/panther#testing-usage',
|
||||
];
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:test';
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated remove this method when removing make:unit-test and make:functional-test
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getCommandAliases(): iterable
|
||||
{
|
||||
yield 'make:unit-test';
|
||||
yield 'make:functional-test';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new test class';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$typesDesc = [];
|
||||
$typesHelp = [];
|
||||
foreach (self::DESCRIPTIONS as $type => $desc) {
|
||||
$typesDesc[] = \sprintf('<fg=yellow>%s</> (%s)', $type, $desc);
|
||||
$typesHelp[] = \sprintf('* <info>%s</info>: %s', $type, $desc);
|
||||
}
|
||||
|
||||
$command
|
||||
->addArgument('type', InputArgument::OPTIONAL, 'The type of test: '.implode(', ', $typesDesc))
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'The name of the test class (e.g. <fg=yellow>BlogPostTest</>)')
|
||||
->setHelp($this->getHelpFileContents('MakeTest.txt').implode("\n", $typesHelp))
|
||||
;
|
||||
|
||||
$inputConfig->setArgumentAsNonInteractive('name');
|
||||
$inputConfig->setArgumentAsNonInteractive('type');
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
/* @deprecated remove the following block when removing make:unit-test and make:functional-test */
|
||||
$this->handleDeprecatedMakerCommands($input, $io);
|
||||
|
||||
if (null !== $type = $input->getArgument('type')) {
|
||||
if (!isset(self::DESCRIPTIONS[$type])) {
|
||||
throw new RuntimeCommandException(\sprintf('The test type must be one of "%s", "%s" given.', implode('", "', array_keys(self::DESCRIPTIONS)), $type));
|
||||
}
|
||||
} else {
|
||||
$input->setArgument(
|
||||
'type',
|
||||
$io->choice('Which test type would you like?', self::DESCRIPTIONS)
|
||||
);
|
||||
}
|
||||
|
||||
if ('ApiTestCase' === $input->getArgument('type') && !class_exists(ApiTestCase::class)) {
|
||||
$io->warning([
|
||||
'API Platform is required for this test type. Install it with',
|
||||
'composer require api',
|
||||
]);
|
||||
}
|
||||
|
||||
if ('PantherTestCase' === $input->getArgument('type') && !trait_exists(PantherTestCaseTrait::class)) {
|
||||
$io->warning([
|
||||
'symfony/panther is required for this test type. Install it with',
|
||||
'composer require symfony/panther --dev',
|
||||
]);
|
||||
}
|
||||
|
||||
if (null === $input->getArgument('name')) {
|
||||
$io->writeln([
|
||||
'',
|
||||
'Choose a class name for your test, like:',
|
||||
' * <fg=yellow>UtilTest</> (to create tests/UtilTest.php)',
|
||||
' * <fg=yellow>Service\\UtilTest</> (to create tests/Service/UtilTest.php)',
|
||||
' * <fg=yellow>\\App\Tests\\Service\\UtilTest</> (to create tests/Service/UtilTest.php)',
|
||||
]);
|
||||
|
||||
$nameArgument = $command->getDefinition()->getArgument('name');
|
||||
$value = $io->ask($nameArgument->getDescription(), $nameArgument->getDefault(), Validator::notBlank(...));
|
||||
$input->setArgument($nameArgument->getName(), $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$testClassNameDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('name'),
|
||||
'Tests\\',
|
||||
'Test'
|
||||
);
|
||||
|
||||
$type = $input->getArgument('type');
|
||||
|
||||
$generator->generateClass(
|
||||
$testClassNameDetails->getFullName(),
|
||||
"test/$type.tpl.php",
|
||||
[
|
||||
'web_assertions_are_available' => trait_exists(WebTestAssertionsTrait::class),
|
||||
'api_test_case_fqcn' => !class_exists(ApiTestCase::class) ? LegacyApiTestCase::class : ApiTestCase::class,
|
||||
]
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next: Open your new test class and start customizing it.',
|
||||
\sprintf('Find the documentation at <fg=yellow>%s</>', self::DOCS[$type]),
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies, ?InputInterface $input = null): void
|
||||
{
|
||||
if (null === $input) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($input->getArgument('type')) {
|
||||
case 'WebTestCase':
|
||||
$dependencies->addClassDependency(
|
||||
History::class,
|
||||
'browser-kit',
|
||||
true,
|
||||
true
|
||||
);
|
||||
$dependencies->addClassDependency(
|
||||
CssSelectorConverter::class,
|
||||
'css-selector',
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
|
||||
case 'ApiTestCase':
|
||||
$dependencies->addClassDependency(
|
||||
!class_exists(ApiTestCase::class) ? LegacyApiTestCase::class : ApiTestCase::class,
|
||||
'api',
|
||||
true,
|
||||
false
|
||||
);
|
||||
|
||||
return;
|
||||
|
||||
case 'PantherTestCase':
|
||||
$dependencies->addClassDependency(
|
||||
PantherTestCaseTrait::class,
|
||||
'panther',
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
private function handleDeprecatedMakerCommands(InputInterface $input, ConsoleStyle $io): void
|
||||
{
|
||||
$currentCommand = $input->getFirstArgument();
|
||||
switch ($currentCommand) {
|
||||
case 'make:unit-test':
|
||||
$input->setArgument('type', 'TestCase');
|
||||
$io->warning('The "make:unit-test" command is deprecated, use "make:test" instead.');
|
||||
break;
|
||||
|
||||
case 'make:functional-test':
|
||||
$input->setArgument('type', trait_exists(PantherTestCaseTrait::class) ? 'WebTestCase' : 'PantherTestCase');
|
||||
$io->warning('The "make:functional-test" command is deprecated, use "make:test" instead.');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
119
vendor/symfony/maker-bundle/src/Maker/MakeTwigComponent.php
vendored
Normal file
119
vendor/symfony/maker-bundle/src/Maker/MakeTwigComponent.php
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
|
||||
use Symfony\Bundle\MakerBundle\FileManager;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
|
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
|
||||
|
||||
/**
|
||||
* @author Kevin Bond <kevinbond@gmail.com>
|
||||
*/
|
||||
final class MakeTwigComponent extends AbstractMaker
|
||||
{
|
||||
private string $namespace = 'Twig\\Components';
|
||||
|
||||
public function __construct(private FileManager $fileManager)
|
||||
{
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:twig-component';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a Twig (or Live) component';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->setDescription(self::getCommandDescription())
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'The name of your Twig component (ie <fg=yellow>Notification</>)')
|
||||
->addOption('live', null, InputOption::VALUE_NONE, 'Whether to create a Live component (requires <fg=yellow>symfony/ux-live-component</>)')
|
||||
;
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$dependencies->addClassDependency(AsTwigComponent::class, 'symfony/ux-twig-component');
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$live = $input->getOption('live');
|
||||
|
||||
if ($live && !class_exists(AsLiveComponent::class)) {
|
||||
throw new \RuntimeException('You must install symfony/ux-live-component to create a Live component (composer require symfony/ux-live-component)');
|
||||
}
|
||||
|
||||
$factory = $generator->createClassNameDetails(
|
||||
$name,
|
||||
str_replace($generator->getRootNamespace().'\\', '', $this->namespace),
|
||||
);
|
||||
|
||||
$templatePath = str_replace('\\', '/', $factory->getRelativeNameWithoutSuffix());
|
||||
$shortName = str_replace('\\', ':', $factory->getRelativeNameWithoutSuffix());
|
||||
|
||||
$generator->generateClass(
|
||||
$factory->getFullName(),
|
||||
\sprintf('%s/templates/twig/%s', \dirname(__DIR__, 2), $live ? 'LiveComponent.tpl.php' : 'Component.tpl.php'),
|
||||
[
|
||||
'live' => $live,
|
||||
]
|
||||
);
|
||||
$generator->generateTemplate(
|
||||
"components/{$templatePath}.html.twig",
|
||||
\sprintf('%s/templates/twig/%s', \dirname(__DIR__, 2), 'component_template.tpl.php')
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
$io->newLine();
|
||||
$io->writeln(" To render the component, use <fg=yellow><twig:{$shortName} /></>.");
|
||||
$io->newLine();
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
if (!$input->getOption('live')) {
|
||||
$input->setOption('live', $io->confirm('Make this a Live component?', false));
|
||||
}
|
||||
|
||||
$path = 'config/packages/twig_component.yaml';
|
||||
|
||||
if (!$this->fileManager->fileExists($path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$value = Yaml::parse($this->fileManager->getFileContents($path));
|
||||
$this->namespace = array_key_first($value['twig_component']['defaults']);
|
||||
} catch (\Throwable $throwable) {
|
||||
throw new RuntimeCommandException(message: 'Unable to parse config/packages/twig_component.yaml', previous: $throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
107
vendor/symfony/maker-bundle/src/Maker/MakeTwigExtension.php
vendored
Normal file
107
vendor/symfony/maker-bundle/src/Maker/MakeTwigExtension.php
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\Extension\RuntimeExtensionInterface;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
/**
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*/
|
||||
final class MakeTwigExtension extends AbstractMaker
|
||||
{
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:twig-extension';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new Twig extension with its runtime class';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'The name of the Twig extension class (e.g. <fg=yellow>AppExtension</>)')
|
||||
->setHelp($this->getHelpFileContents('MakeTwigExtension.txt'))
|
||||
;
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
|
||||
$extensionClassNameDetails = $generator->createClassNameDetails(
|
||||
$name,
|
||||
'Twig\\Extension\\',
|
||||
'Extension'
|
||||
);
|
||||
|
||||
$runtimeClassNameDetails = $generator->createClassNameDetails(
|
||||
$name,
|
||||
'Twig\\Runtime\\',
|
||||
'Runtime'
|
||||
);
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
AbstractExtension::class,
|
||||
TwigFilter::class,
|
||||
TwigFunction::class,
|
||||
$runtimeClassNameDetails->getFullName(),
|
||||
]);
|
||||
|
||||
$runtimeUseStatements = new UseStatementGenerator([
|
||||
RuntimeExtensionInterface::class,
|
||||
]);
|
||||
|
||||
$generator->generateClass(
|
||||
$extensionClassNameDetails->getFullName(),
|
||||
'twig/Extension.tpl.php',
|
||||
['use_statements' => $useStatements, 'runtime_class_name' => $runtimeClassNameDetails->getShortName()]
|
||||
);
|
||||
|
||||
$generator->generateClass(
|
||||
$runtimeClassNameDetails->getFullName(),
|
||||
'twig/Runtime.tpl.php',
|
||||
['use_statements' => $runtimeUseStatements]
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next: Open your new extension class and start customizing it.',
|
||||
'Find the documentation at <fg=yellow>http://symfony.com/doc/current/templating/twig_extension.html</>',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
AbstractExtension::class,
|
||||
'twig'
|
||||
);
|
||||
}
|
||||
}
|
||||
79
vendor/symfony/maker-bundle/src/Maker/MakeUnitTest.php
vendored
Normal file
79
vendor/symfony/maker-bundle/src/Maker/MakeUnitTest.php
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
trigger_deprecation('symfony/maker-bundle', '1.29', 'The "%s" class is deprecated, use "%s" instead.', MakeUnitTest::class, MakeTest::class);
|
||||
|
||||
/**
|
||||
* @deprecated since MakerBundle 1.29, use Symfony\Bundle\MakerBundle\Maker\MakeTest instead.
|
||||
*
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*/
|
||||
final class MakeUnitTest extends AbstractMaker
|
||||
{
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:unit-test';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new unit test class';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'The name of the unit test class (e.g. <fg=yellow>UtilTest</>)')
|
||||
->setHelp($this->getHelpFileContents('MakeUnitTest.txt'))
|
||||
;
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$testClassNameDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('name'),
|
||||
'Tests\\',
|
||||
'Test'
|
||||
);
|
||||
|
||||
$generator->generateClass(
|
||||
$testClassNameDetails->getFullName(),
|
||||
'test/Unit.tpl.php',
|
||||
['use_statements' => new UseStatementGenerator([TestCase::class])]
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next: Open your new test class and start customizing it.',
|
||||
'Find the documentation at <fg=yellow>https://symfony.com/doc/current/testing.html#unit-tests</>',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
}
|
||||
}
|
||||
262
vendor/symfony/maker-bundle/src/Maker/MakeUser.php
vendored
Normal file
262
vendor/symfony/maker-bundle/src/Maker/MakeUser.php
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\EntityClassGenerator;
|
||||
use Symfony\Bundle\MakerBundle\Doctrine\ORMDependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
|
||||
use Symfony\Bundle\MakerBundle\FileManager;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Maker\Common\UidTrait;
|
||||
use Symfony\Bundle\MakerBundle\Security\SecurityConfigUpdater;
|
||||
use Symfony\Bundle\MakerBundle\Security\UserClassBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Security\UserClassConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator;
|
||||
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
|
||||
use Symfony\Bundle\MakerBundle\Util\YamlManipulationFailedException;
|
||||
use Symfony\Bundle\MakerBundle\Validator;
|
||||
use Symfony\Bundle\SecurityBundle\SecurityBundle;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
|
||||
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class MakeUser extends AbstractMaker
|
||||
{
|
||||
use UidTrait;
|
||||
|
||||
public function __construct(
|
||||
private FileManager $fileManager,
|
||||
private UserClassBuilder $userClassBuilder,
|
||||
private SecurityConfigUpdater $configUpdater,
|
||||
private EntityClassGenerator $entityClassGenerator,
|
||||
private DoctrineHelper $doctrineHelper,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:user';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new security user class';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'The name of the security user class (e.g. <fg=yellow>User</>)')
|
||||
->addOption('is-entity', null, InputOption::VALUE_NONE, 'Do you want to store user data in the database (via Doctrine)?')
|
||||
->addOption('identity-property-name', null, InputOption::VALUE_REQUIRED, 'Enter a property name that will be the unique "display" name for the user (e.g. <comment>email, username, uuid</comment>)')
|
||||
->addOption('with-password', null, InputOption::VALUE_NONE, 'Will this app be responsible for checking the password? Choose <comment>No</comment> if the password is actually checked by some other system (e.g. a single sign-on server)')
|
||||
->setHelp($this->getHelpFileContents('MakeUser.txt'))
|
||||
;
|
||||
|
||||
$this->addWithUuidOption($command);
|
||||
|
||||
$inputConfig->setArgumentAsNonInteractive('name');
|
||||
}
|
||||
|
||||
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
|
||||
{
|
||||
$this->checkIsUsingUid($input);
|
||||
|
||||
if (null === $input->getArgument('name')) {
|
||||
$name = $io->ask(
|
||||
$command->getDefinition()->getArgument('name')->getDescription(),
|
||||
'User'
|
||||
);
|
||||
$input->setArgument('name', $name);
|
||||
}
|
||||
|
||||
$userIsEntity = $io->confirm(
|
||||
'Do you want to store user data in the database (via Doctrine)?',
|
||||
class_exists(DoctrineBundle::class)
|
||||
);
|
||||
if ($userIsEntity) {
|
||||
$dependencies = new DependencyBuilder();
|
||||
ORMDependencyBuilder::buildDependencies($dependencies);
|
||||
|
||||
$missingPackagesMessage = $dependencies->getMissingPackagesMessage(self::getCommandName(), 'Doctrine must be installed to store user data in the database');
|
||||
if ($missingPackagesMessage) {
|
||||
throw new RuntimeCommandException($missingPackagesMessage);
|
||||
}
|
||||
}
|
||||
$input->setOption('is-entity', $userIsEntity);
|
||||
|
||||
$identityFieldName = $io->ask('Enter a property name that will be the unique "display" name for the user (e.g. <comment>email, username, uuid</comment>)', 'email', Validator::validatePropertyName(...));
|
||||
$input->setOption('identity-property-name', $identityFieldName);
|
||||
|
||||
$io->text('Will this app need to hash/check user passwords? Choose <comment>No</comment> if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server).');
|
||||
$userWillHavePassword = $io->confirm('Does this app need to hash/check user passwords?');
|
||||
$input->setOption('with-password', $userWillHavePassword);
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$userClassConfiguration = new UserClassConfiguration(
|
||||
$input->getOption('is-entity'),
|
||||
$input->getOption('identity-property-name'),
|
||||
$input->getOption('with-password')
|
||||
);
|
||||
|
||||
$userClassNameDetails = $generator->createClassNameDetails(
|
||||
$input->getArgument('name'),
|
||||
$userClassConfiguration->isEntity() ? 'Entity\\' : 'Security\\'
|
||||
);
|
||||
|
||||
// A) Generate the User class
|
||||
if ($userClassConfiguration->isEntity()) {
|
||||
$classPath = $this->entityClassGenerator->generateEntityClass(
|
||||
entityClassDetails: $userClassNameDetails,
|
||||
apiResource: false, // api resource
|
||||
withPasswordUpgrade: $userClassConfiguration->hasPassword(), // security user
|
||||
useUuidIdentifier: $this->getIdType()
|
||||
);
|
||||
} else {
|
||||
$classPath = $generator->generateClass($userClassNameDetails->getFullName(), 'Class.tpl.php');
|
||||
}
|
||||
// need to write changes early so we can modify the contents below
|
||||
$generator->writeChanges();
|
||||
|
||||
$entityUsesAttributes = ($isEntity = $userClassConfiguration->isEntity()) && $this->doctrineHelper->doesClassUsesAttributes($userClassNameDetails->getFullName());
|
||||
|
||||
if ($isEntity && !$entityUsesAttributes) {
|
||||
throw new \RuntimeException('MakeUser only supports attribute mapping with doctrine entities.');
|
||||
}
|
||||
|
||||
// B) Implement UserInterface
|
||||
$manipulator = new ClassSourceManipulator(
|
||||
sourceCode: $this->fileManager->getFileContents($classPath),
|
||||
overwrite: true,
|
||||
useAttributesForDoctrineMapping: $entityUsesAttributes
|
||||
);
|
||||
|
||||
$manipulator->setIo($io);
|
||||
|
||||
$this->userClassBuilder->addUserInterfaceImplementation($manipulator, $userClassConfiguration);
|
||||
|
||||
$generator->dumpFile($classPath, $manipulator->getSourceCode());
|
||||
|
||||
// C) Generate a custom user provider, if necessary
|
||||
if (!$userClassConfiguration->isEntity()) {
|
||||
$userClassConfiguration->setUserProviderClass($generator->getRootNamespace().'\\Security\\UserProvider');
|
||||
|
||||
$useStatements = new UseStatementGenerator([
|
||||
UnsupportedUserException::class,
|
||||
UserNotFoundException::class,
|
||||
PasswordAuthenticatedUserInterface::class,
|
||||
PasswordUpgraderInterface::class,
|
||||
UserInterface::class,
|
||||
UserProviderInterface::class,
|
||||
]);
|
||||
|
||||
$customProviderPath = $generator->generateClass(
|
||||
$userClassConfiguration->getUserProviderClass(),
|
||||
'security/UserProvider.tpl.php',
|
||||
[
|
||||
'use_statements' => $useStatements,
|
||||
'user_short_name' => $userClassNameDetails->getShortName(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// D) Update security.yaml
|
||||
$securityYamlUpdated = false;
|
||||
$path = 'config/packages/security.yaml';
|
||||
if ($this->fileManager->fileExists($path)) {
|
||||
try {
|
||||
$newYaml = $this->configUpdater->updateForUserClass(
|
||||
$this->fileManager->getFileContents($path),
|
||||
$userClassConfiguration,
|
||||
$userClassNameDetails->getFullName()
|
||||
);
|
||||
$generator->dumpFile($path, $newYaml);
|
||||
$securityYamlUpdated = true;
|
||||
} catch (YamlManipulationFailedException) {
|
||||
}
|
||||
}
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text('Next Steps:');
|
||||
$nextSteps = [
|
||||
\sprintf('Review your new <info>%s</info> class.', $userClassNameDetails->getFullName()),
|
||||
];
|
||||
if ($userClassConfiguration->isEntity()) {
|
||||
$nextSteps[] = \sprintf(
|
||||
'Use <comment>make:entity</comment> to add more fields to your <info>%s</info> entity and then run <comment>make:migration</comment>.',
|
||||
$userClassNameDetails->getShortName()
|
||||
);
|
||||
} else {
|
||||
$nextSteps[] = \sprintf(
|
||||
'Open <info>%s</info> to finish implementing your user provider.',
|
||||
/* @phpstan-ignore-next-line - $customProviderPath is defined in this else statement */
|
||||
$this->fileManager->relativizePath($customProviderPath)
|
||||
);
|
||||
}
|
||||
|
||||
if (!$securityYamlUpdated) {
|
||||
$yamlExample = $this->configUpdater->updateForUserClass(
|
||||
'security: {}',
|
||||
$userClassConfiguration,
|
||||
$userClassNameDetails->getFullName()
|
||||
);
|
||||
$nextSteps[] = "Your <info>security.yaml</info> could not be updated automatically. You'll need to add the following config manually:\n\n".$yamlExample;
|
||||
}
|
||||
|
||||
$nextSteps[] = 'Create a way to authenticate! See https://symfony.com/doc/current/security.html';
|
||||
|
||||
$nextSteps = array_map(static fn ($step) => \sprintf(' - %s', $step), $nextSteps);
|
||||
$io->text($nextSteps);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies, ?InputInterface $input = null): void
|
||||
{
|
||||
// checking for SecurityBundle guarantees security.yaml is present
|
||||
$dependencies->addClassDependency(
|
||||
SecurityBundle::class,
|
||||
'security'
|
||||
);
|
||||
|
||||
// needed to update the YAML files
|
||||
$dependencies->addClassDependency(
|
||||
Yaml::class,
|
||||
'yaml'
|
||||
);
|
||||
|
||||
if (null !== $input && $input->getOption('is-entity')) {
|
||||
ORMDependencyBuilder::buildDependencies($dependencies);
|
||||
}
|
||||
}
|
||||
}
|
||||
100
vendor/symfony/maker-bundle/src/Maker/MakeValidator.php
vendored
Normal file
100
vendor/symfony/maker-bundle/src/Maker/MakeValidator.php
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Str;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassData;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Validation;
|
||||
|
||||
/**
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*/
|
||||
final class MakeValidator extends AbstractMaker
|
||||
{
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:validator';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new validator and constraint class';
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConf)
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'The name of the validator class (e.g. <fg=yellow>EnabledValidator</>)')
|
||||
->setHelp($this->getHelpFileContents('MakeValidator.txt'))
|
||||
;
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
|
||||
{
|
||||
$validatorClassData = ClassData::create(
|
||||
class: \sprintf('Validator\\%s', $input->getArgument('name')),
|
||||
suffix: 'Validator',
|
||||
extendsClass: ConstraintValidator::class,
|
||||
useStatements: [
|
||||
Constraint::class,
|
||||
],
|
||||
);
|
||||
|
||||
$constraintDataClass = ClassData::create(
|
||||
class: \sprintf('Validator\\%s', Str::removeSuffix($validatorClassData->getClassName(), 'Validator')),
|
||||
extendsClass: Constraint::class,
|
||||
);
|
||||
|
||||
$generator->generateClassFromClassData(
|
||||
$validatorClassData,
|
||||
'validator/Validator.tpl.php',
|
||||
[
|
||||
'constraint_class_name' => $constraintDataClass->getClassName(),
|
||||
]
|
||||
);
|
||||
|
||||
$generator->generateClassFromClassData(
|
||||
$constraintDataClass,
|
||||
'validator/Constraint.tpl.php',
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next: Open your new constraint & validators and add your logic.',
|
||||
'Find the documentation at <fg=yellow>http://symfony.com/doc/current/validation/custom_constraint.html</>',
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
public function configureDependencies(DependencyBuilder $dependencies)
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
Validation::class,
|
||||
'validator'
|
||||
);
|
||||
}
|
||||
}
|
||||
85
vendor/symfony/maker-bundle/src/Maker/MakeVoter.php
vendored
Normal file
85
vendor/symfony/maker-bundle/src/Maker/MakeVoter.php
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony MakerBundle package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\MakerBundle\Maker;
|
||||
|
||||
use Symfony\Bundle\MakerBundle\ConsoleStyle;
|
||||
use Symfony\Bundle\MakerBundle\DependencyBuilder;
|
||||
use Symfony\Bundle\MakerBundle\Generator;
|
||||
use Symfony\Bundle\MakerBundle\InputConfiguration;
|
||||
use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassData;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
/**
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||
*/
|
||||
final class MakeVoter extends AbstractMaker
|
||||
{
|
||||
public static function getCommandName(): string
|
||||
{
|
||||
return 'make:voter';
|
||||
}
|
||||
|
||||
public static function getCommandDescription(): string
|
||||
{
|
||||
return 'Create a new security voter class';
|
||||
}
|
||||
|
||||
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
|
||||
{
|
||||
$command
|
||||
->addArgument('name', InputArgument::OPTIONAL, 'The name of the security voter class (e.g. <fg=yellow>BlogPostVoter</>)')
|
||||
->setHelp($this->getHelpFileContents('MakeVoter.txt'))
|
||||
;
|
||||
}
|
||||
|
||||
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
|
||||
{
|
||||
$voterClassData = ClassData::create(
|
||||
class: \sprintf('Security\Voter\%s', $input->getArgument('name')),
|
||||
suffix: 'Voter',
|
||||
extendsClass: Voter::class,
|
||||
useStatements: [
|
||||
TokenInterface::class,
|
||||
Voter::class,
|
||||
UserInterface::class,
|
||||
]
|
||||
);
|
||||
|
||||
$generator->generateClassFromClassData(
|
||||
$voterClassData,
|
||||
'security/Voter.tpl.php',
|
||||
);
|
||||
|
||||
$generator->writeChanges();
|
||||
|
||||
$this->writeSuccessMessage($io);
|
||||
|
||||
$io->text([
|
||||
'Next: Open your voter and add your logic.',
|
||||
'Find the documentation at <fg=yellow>https://symfony.com/doc/current/security/voters.html</>',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureDependencies(DependencyBuilder $dependencies): void
|
||||
{
|
||||
$dependencies->addClassDependency(
|
||||
Voter::class,
|
||||
'security'
|
||||
);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user