commit
						35101b5cec
					
				| @ -0,0 +1,21 @@ | ||||
| /.* | ||||
| 
 | ||||
| ###> symfony/framework-bundle ### | ||||
| /.env.local | ||||
| /.env.local.php | ||||
| /.env.*.local | ||||
| /config/secrets/prod/prod.decrypt.private.php | ||||
| /public/bundles/ | ||||
| /var/ | ||||
| /vendor/ | ||||
| ###< symfony/framework-bundle ### | ||||
| 
 | ||||
| ###> symfony/phpunit-bridge ### | ||||
| .phpunit.result.cache | ||||
| /phpunit.xml | ||||
| ###< symfony/phpunit-bridge ### | ||||
| 
 | ||||
| ###> phpunit/phpunit ### | ||||
| /phpunit.xml | ||||
| .phpunit.result.cache | ||||
| ###< phpunit/phpunit ### | ||||
| @ -0,0 +1,17 @@ | ||||
| #!/usr/bin/env php | ||||
| <?php | ||||
| 
 | ||||
| use App\Kernel; | ||||
| use Symfony\Bundle\FrameworkBundle\Console\Application; | ||||
| 
 | ||||
| if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) { | ||||
|     throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".'); | ||||
| } | ||||
| 
 | ||||
| require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; | ||||
| 
 | ||||
| return function (array $context) { | ||||
|     $kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); | ||||
| 
 | ||||
|     return new Application($kernel); | ||||
| }; | ||||
| @ -0,0 +1,23 @@ | ||||
| #!/usr/bin/env php | ||||
| <?php | ||||
| 
 | ||||
| if (!ini_get('date.timezone')) { | ||||
|     ini_set('date.timezone', 'UTC'); | ||||
| } | ||||
| 
 | ||||
| if (is_file(dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit')) { | ||||
|     if (PHP_VERSION_ID >= 80000) { | ||||
|         require dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit'; | ||||
|     } else { | ||||
|         define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__).'/vendor/autoload.php'); | ||||
|         require PHPUNIT_COMPOSER_INSTALL; | ||||
|         PHPUnit\TextUI\Command::main(); | ||||
|     } | ||||
| } else { | ||||
|     if (!is_file(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) { | ||||
|         echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n"; | ||||
|         exit(1); | ||||
|     } | ||||
| 
 | ||||
|     require dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php'; | ||||
| } | ||||
| @ -0,0 +1,94 @@ | ||||
| { | ||||
|     "type": "project", | ||||
|     "license": "proprietary", | ||||
|     "minimum-stability": "stable", | ||||
|     "prefer-stable": true, | ||||
|     "require": { | ||||
|         "php": ">=8.1", | ||||
|         "ext-ctype": "*", | ||||
|         "ext-iconv": "*", | ||||
|         "api-platform/core": "^3.2", | ||||
|         "doctrine/doctrine-bundle": "^2.11", | ||||
|         "doctrine/doctrine-migrations-bundle": "^3.3", | ||||
|         "doctrine/orm": "^2.17", | ||||
|         "nelmio/cors-bundle": "^2.4", | ||||
|         "phpdocumentor/reflection-docblock": "^5.3", | ||||
|         "phpstan/phpdoc-parser": "^1.25", | ||||
|         "symfony/asset": "6.4.*", | ||||
|         "symfony/console": "6.4.*", | ||||
|         "symfony/dotenv": "6.4.*", | ||||
|         "symfony/expression-language": "6.4.*", | ||||
|         "symfony/flex": "^2", | ||||
|         "symfony/framework-bundle": "6.4.*", | ||||
|         "symfony/property-access": "6.4.*", | ||||
|         "symfony/property-info": "6.4.*", | ||||
|         "symfony/runtime": "6.4.*", | ||||
|         "symfony/security-bundle": "6.4.*", | ||||
|         "symfony/serializer": "6.4.*", | ||||
|         "symfony/string": "6.4.*", | ||||
|         "symfony/twig-bundle": "6.4.*", | ||||
|         "symfony/uid": "6.4.*", | ||||
|         "symfony/validator": "6.4.*", | ||||
|         "symfony/yaml": "6.4.*" | ||||
|     }, | ||||
|     "config": { | ||||
|         "allow-plugins": { | ||||
|             "php-http/discovery": true, | ||||
|             "symfony/flex": true, | ||||
|             "symfony/runtime": true | ||||
|         }, | ||||
|         "sort-packages": true | ||||
|     }, | ||||
|     "autoload": { | ||||
|         "psr-4": { | ||||
|             "App\\": "src/" | ||||
|         } | ||||
|     }, | ||||
|     "autoload-dev": { | ||||
|         "psr-4": { | ||||
|             "App\\Tests\\": "tests/" | ||||
|         } | ||||
|     }, | ||||
|     "replace": { | ||||
|         "symfony/polyfill-ctype": "*", | ||||
|         "symfony/polyfill-iconv": "*", | ||||
|         "symfony/polyfill-php72": "*", | ||||
|         "symfony/polyfill-php73": "*", | ||||
|         "symfony/polyfill-php74": "*", | ||||
|         "symfony/polyfill-php80": "*", | ||||
|         "symfony/polyfill-php81": "*" | ||||
|     }, | ||||
|     "scripts": { | ||||
|         "auto-scripts": { | ||||
|             "cache:clear": "symfony-cmd", | ||||
|             "assets:install %PUBLIC_DIR%": "symfony-cmd" | ||||
|         }, | ||||
|         "post-install-cmd": [ | ||||
|             "@auto-scripts" | ||||
|         ], | ||||
|         "post-update-cmd": [ | ||||
|             "@auto-scripts" | ||||
|         ] | ||||
|     }, | ||||
|     "conflict": { | ||||
|         "symfony/symfony": "*" | ||||
|     }, | ||||
|     "extra": { | ||||
|         "symfony": { | ||||
|             "allow-contrib": false, | ||||
|             "require": "6.4.*", | ||||
|             "docker": false | ||||
|         } | ||||
|     }, | ||||
|     "require-dev": { | ||||
|         "dama/doctrine-test-bundle": "^8.0", | ||||
|         "doctrine/doctrine-fixtures-bundle": "^3.5", | ||||
|         "phpunit/phpunit": "^9.5", | ||||
|         "symfony/browser-kit": "6.4.*", | ||||
|         "symfony/css-selector": "6.4.*", | ||||
|         "symfony/http-client": "6.4.*", | ||||
|         "symfony/maker-bundle": "^1.52", | ||||
|         "symfony/phpunit-bridge": "^7.0", | ||||
|         "zenstruck/foundry": "^1.36" | ||||
|     } | ||||
| } | ||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| @ -0,0 +1,165 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" ?> | ||||
| <!-- api/config/api_platform/resources.xml --> | ||||
| 
 | ||||
| <resources xmlns="https://api-platform.com/schema/metadata/resources-3.0" | ||||
|         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|         xsi:schemaLocation="https://api-platform.com/schema/metadata/resources-3.0 | ||||
|         https://api-platform.com/schema/metadata/resources-3.0.xsd"> | ||||
| 
 | ||||
| <resource class="App\Entity\User"> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Get" /> | ||||
|         <operation class="ApiPlatform\Metadata\Get" controller="App\Controller\GetUserByName" uriTemplate="users/authName/{authName}" description="Receives a user by its authentication name"> | ||||
|             <uriVariables><uriVariable parameterName="authName"></uriVariable></uriVariables> | ||||
|         </operation> | ||||
|         <operation class="ApiPlatform\Metadata\Patch" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| 
 | ||||
| <resource class="App\Entity\Country"> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\GetCollection" /> | ||||
|         <operation class="ApiPlatform\Metadata\Get" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| 
 | ||||
| <resource class="App\Entity\City" uriTemplate="/country/{countryId}/capital"> | ||||
|     <uriVariables> | ||||
|         <uriVariable parameterName="countryId" fromClass="App\Entity\Country" toProperty="capital"></uriVariable> | ||||
|     </uriVariables> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Get" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| <resource class="App\Entity\City" uriTemplate="/prefecture/{prefectureId}/capital"> | ||||
|     <uriVariables> | ||||
|         <uriVariable parameterName="prefectureId" fromClass="App\Entity\Prefecture" toProperty="capital"></uriVariable> | ||||
|     </uriVariables> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Get" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| <resource class="App\Entity\City"> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Get" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| 
 | ||||
| <resource class="App\Entity\Village" uriTemplate="/prefecture/{prefectureId}/villages"> | ||||
|     <uriVariables> | ||||
|         <uriVariable parameterName="prefectureId" fromClass="App\Entity\Prefecture" toProperty="villages"></uriVariable> | ||||
|     </uriVariables> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\GetCollection" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| <resource class="App\Entity\Village"> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Get" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| 
 | ||||
| <resource class="App\Entity\Prefecture" uriTemplate="/country/{countryId}/prefectures"> | ||||
|     <uriVariables> | ||||
|         <uriVariable parameterName="countryId" fromClass="App\Entity\Country" toProperty="prefectures"></uriVariable> | ||||
|     </uriVariables> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\GetCollection" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| <resource class="App\Entity\Prefecture"> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Get" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| 
 | ||||
| 
 | ||||
| <resource class="App\Entity\Dojo"> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Get" /> | ||||
|         <operation class="ApiPlatform\Metadata\Get" uriTemplate="dojo" controller="App\Controller\GetUserDojo" /> | ||||
|         <operation class="ApiPlatform\Metadata\Post" processor="App\State\DojoPostProcessor"/> | ||||
|         <operation class="ApiPlatform\Metadata\Patch" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| <resource class="App\Entity\Dojo" uriTemplate="/village/{villageId}/dojos"> | ||||
|     <uriVariables> | ||||
|         <uriVariable parameterName="villageId" fromClass="App\Entity\Village" toProperty="dojos"></uriVariable> | ||||
|     </uriVariables> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Get" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| 
 | ||||
| <resource class="App\Entity\Dungeon"> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Get" uriTemplate="dungeon/{id}" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| <resource class="App\Entity\Dungeon" uriTemplate="/city/{cityId}/dungeon"> | ||||
|     <uriVariables> | ||||
|         <uriVariable parameterName="cityId" fromClass="App\Entity\City" toProperty="dungeon"></uriVariable> | ||||
|     </uriVariables> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Get" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| 
 | ||||
| <resource class="App\Entity\Character"> | ||||
|     <normalizationContext><values><value name="groups"><values><value>public</value></values></value></values></normalizationContext> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Get" /> | ||||
|         <operation class="ApiPlatform\Metadata\GetCollection" controller="App\Controller\GetDojoCharacters" uriTemplate="dojos/{dojoId}/characters" description="Receives the characters from a dojo."> | ||||
|             <uriVariables><uriVariable parameterName="dojoId" /></uriVariables> | ||||
|         </operation> | ||||
|         <operation class="ApiPlatform\Metadata\GetCollection" controller="App\Controller\GetOwnDojoCharacters" uriTemplate="dojo/characters" description="Receives the characters from the users dojo."> | ||||
|             <normalizationContext><values><value name="groups"><values><value>public</value><value>detail</value></values></value></values></normalizationContext> | ||||
|         </operation> | ||||
|     </operations> | ||||
| </resource> | ||||
| 
 | ||||
| <resource class="App\Entity\Character"> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Post" /> | ||||
|         <operation class="ApiPlatform\Metadata\Patch" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| 
 | ||||
| <resource class="App\Entity\Technique"> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Get" /> | ||||
|         <operation class="ApiPlatform\Metadata\GetCollection" /> | ||||
|         <operation class="ApiPlatform\Metadata\Post" security="is_granted('ROLE_ADMIN')"> | ||||
|         </operation> | ||||
|     </operations> | ||||
| </resource> | ||||
| 
 | ||||
| <resource class="App\Entity\Tournament"> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Get" /> | ||||
|         <operation class="ApiPlatform\Metadata\GetCollection" /> | ||||
|     </operations> | ||||
| </resource> | ||||
| 
 | ||||
| <resource class="App\Entity\TournamentRegistration"> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Post"  processor="App\State\TournamentRegistrationStateProcessor"/> | ||||
|     </operations> | ||||
| </resource> | ||||
| 
 | ||||
| <resource class="App\Entity\Fight"> | ||||
|     <operations> | ||||
|         <operation class="ApiPlatform\Metadata\Get" /> | ||||
|         <operation class="ApiPlatform\Metadata\GetCollection" controller="App\Controller\GetTournamentFights" uriTemplate="tournaments/{tournamentId}/fights" description="Receives the fights from a tournament."> | ||||
|             <uriVariables><uriVariable parameterName="tournamentId" fromClass="App\Entity\Tournament"></uriVariable></uriVariables> | ||||
|         </operation> | ||||
|         <operation class="ApiPlatform\Metadata\GetCollection" controller="App\Controller\GetTournamentFights" uriTemplate="tournaments/{tournamentId}/characters/{characterId}/fights" description="Receives the fights from a tournament for a given character."> | ||||
|             <uriVariables> | ||||
|                 <uriVariable parameterName="tournamentId" fromClass="App\Entity\Tournament"></uriVariable> | ||||
|                 <uriVariable parameterName="characterId" fromClass="App\Entity\Character"></uriVariable> | ||||
|             </uriVariables> | ||||
|         </operation> | ||||
|     </operations> | ||||
| </resource> | ||||
| 
 | ||||
| </resources> | ||||
| @ -0,0 +1,15 @@ | ||||
| <?php | ||||
| 
 | ||||
| return [ | ||||
|     Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], | ||||
|     Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], | ||||
|     Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], | ||||
|     Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], | ||||
|     Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], | ||||
|     Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], | ||||
|     ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], | ||||
|     DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true], | ||||
|     Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], | ||||
|     Zenstruck\Foundry\ZenstruckFoundryBundle::class => ['dev' => true, 'test' => true], | ||||
|     Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], | ||||
| ]; | ||||
| @ -0,0 +1,20 @@ | ||||
| api_platform: | ||||
|     title: Hello API Platform | ||||
|     version: 1.0.0 | ||||
|     # Good defaults for REST APIs | ||||
|     defaults: | ||||
|         stateless: true | ||||
|         cache_headers: | ||||
|             vary: ['Content-Type', 'Authorization', 'Origin'] | ||||
|         extra_properties: | ||||
|             standard_put: true | ||||
|     exception_to_status: | ||||
|         Doctrine\DBAL\Exception\UniqueConstraintViolationException: 409 | ||||
|     formats: | ||||
|         json: ['application/json'] | ||||
|         html: ['text/html'] | ||||
|     docs_formats: | ||||
|         jsonopenapi: ['application/vnd.openapi+json'] | ||||
|         html: ['text/html'] | ||||
|     event_listeners_backward_compatibility_layer: false | ||||
|     keep_legacy_inflector: false | ||||
| @ -0,0 +1,19 @@ | ||||
| framework: | ||||
|     cache: | ||||
|         # Unique name of your app: used to compute stable namespaces for cache keys. | ||||
|         #prefix_seed: your_vendor_name/app_name | ||||
| 
 | ||||
|         # The "app" cache stores to the filesystem by default. | ||||
|         # The data in this cache should persist between deploys. | ||||
|         # Other options include: | ||||
| 
 | ||||
|         # Redis | ||||
|         #app: cache.adapter.redis | ||||
|         #default_redis_provider: redis://localhost | ||||
| 
 | ||||
|         # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) | ||||
|         #app: cache.adapter.apcu | ||||
| 
 | ||||
|         # Namespaced pools use the above "app" backend by default | ||||
|         #pools: | ||||
|             #my.dedicated.cache: null | ||||
| @ -0,0 +1,49 @@ | ||||
| doctrine: | ||||
|     dbal: | ||||
|         url: '%env(resolve:DATABASE_URL)%' | ||||
| 
 | ||||
|         # IMPORTANT: You MUST configure your server version, | ||||
|         # either here or in the DATABASE_URL env var (see .env file) | ||||
|         # server_version: '%env(resolve:DATABASE_VERSION)%' | ||||
| 
 | ||||
|         profiling_collect_backtrace: '%kernel.debug%' | ||||
|         use_savepoints: true | ||||
|     orm: | ||||
|         auto_generate_proxy_classes: true | ||||
|         enable_lazy_ghost_objects: true | ||||
|         report_fields_where_declared: true | ||||
|         validate_xml_mapping: true | ||||
|         naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware | ||||
|         auto_mapping: true | ||||
|         mappings: | ||||
|             App: | ||||
|                 is_bundle: false | ||||
|                 dir: '%kernel.project_dir%/src/Entity' | ||||
|                 prefix: 'App\Entity' | ||||
|                 alias: App | ||||
| 
 | ||||
| when@test: | ||||
|     doctrine: | ||||
|         dbal: | ||||
|             # "TEST_TOKEN" is typically set by ParaTest | ||||
|             dbname_suffix: '_test%env(default::TEST_TOKEN)%' | ||||
| 
 | ||||
| when@prod: | ||||
|     doctrine: | ||||
|         orm: | ||||
|             auto_generate_proxy_classes: false | ||||
|             proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' | ||||
|             query_cache_driver: | ||||
|                 type: pool | ||||
|                 pool: doctrine.system_cache_pool | ||||
|             result_cache_driver: | ||||
|                 type: pool | ||||
|                 pool: doctrine.result_cache_pool | ||||
| 
 | ||||
|     framework: | ||||
|         cache: | ||||
|             pools: | ||||
|                 doctrine.result_cache_pool: | ||||
|                     adapter: cache.app | ||||
|                 doctrine.system_cache_pool: | ||||
|                     adapter: cache.system | ||||
| @ -0,0 +1,6 @@ | ||||
| doctrine_migrations: | ||||
|     migrations_paths: | ||||
|         # namespace is arbitrary but should be different from App\Migrations | ||||
|         # as migrations classes should NOT be autoloaded | ||||
|         'DoctrineMigrations': '%kernel.project_dir%/migrations' | ||||
|     enable_profiler: false | ||||
| @ -0,0 +1,25 @@ | ||||
| # see https://symfony.com/doc/current/reference/configuration/framework.html | ||||
| framework: | ||||
|     secret: '%env(APP_SECRET)%' | ||||
|     #csrf_protection: true | ||||
|     http_method_override: false | ||||
|     handle_all_throwables: true | ||||
| 
 | ||||
|     # Enables session support. Note that the session will ONLY be started if you read or write from it. | ||||
|     # Remove or comment this section to explicitly disable session support. | ||||
|     session: | ||||
|         handler_id: null | ||||
|         cookie_secure: auto | ||||
|         cookie_samesite: lax | ||||
|         storage_factory_id: session.storage.factory.native | ||||
| 
 | ||||
|     #esi: true | ||||
|     #fragments: true | ||||
|     php_errors: | ||||
|         log: true | ||||
| 
 | ||||
| when@test: | ||||
|     framework: | ||||
|         test: true | ||||
|         session: | ||||
|             storage_factory_id: session.storage.factory.mock_file | ||||
| @ -0,0 +1,10 @@ | ||||
| nelmio_cors: | ||||
|     defaults: | ||||
|         origin_regex: true | ||||
|         allow_origin: ['%env(CORS_ALLOW_ORIGIN)%'] | ||||
|         allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE'] | ||||
|         allow_headers: ['Content-Type', 'Authorization'] | ||||
|         expose_headers: ['Link'] | ||||
|         max_age: 3600 | ||||
|     paths: | ||||
|         '^/': null | ||||
| @ -0,0 +1,12 @@ | ||||
| framework: | ||||
|     router: | ||||
|         utf8: true | ||||
| 
 | ||||
|         # Configure how to generate URLs in non-HTTP contexts, such as CLI commands. | ||||
|         # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands | ||||
|         #default_uri: http://localhost | ||||
| 
 | ||||
| when@prod: | ||||
|     framework: | ||||
|         router: | ||||
|             strict_requirements: null | ||||
| @ -0,0 +1,42 @@ | ||||
| security: | ||||
|     # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords | ||||
|     password_hashers: | ||||
|         Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' | ||||
|     # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider | ||||
|     providers: | ||||
|         users_in_memory: { memory: null } | ||||
|     firewalls: | ||||
|         dev: | ||||
|             pattern: ^/(_(profiler|wdt)|css|images|js)/ | ||||
|             security: false | ||||
|         main: | ||||
|             stateless: true | ||||
|              | ||||
|             access_token: | ||||
|                 token_handler: App\Security\AccessTokenHandler | ||||
|                 token_extractors: 'App\Security\CustomTokenExtractor' | ||||
| 
 | ||||
|             # activate different ways to authenticate | ||||
|             # https://symfony.com/doc/current/security.html#the-firewall | ||||
| 
 | ||||
|             # https://symfony.com/doc/current/security/impersonating_user.html | ||||
|             # switch_user: true | ||||
| 
 | ||||
|     # Easy way to control access for large sections of your site | ||||
|     # Note: Only the *first* access control that matches will be used | ||||
|     access_control: | ||||
|         - { path: ^/admin, roles: ROLE_ADMIN } | ||||
|         # - { path: ^/api, roles: ROLE_USER } | ||||
| 
 | ||||
| when@test: | ||||
|     security: | ||||
|         password_hashers: | ||||
|             # By default, password hashers are resource intensive and take time. This is | ||||
|             # important to generate secure password hashes. In tests however, secure hashes | ||||
|             # are not important, waste resources and increase test times. The following | ||||
|             # reduces the work factor to the lowest possible values. | ||||
|             Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: | ||||
|                 algorithm: auto | ||||
|                 cost: 4 # Lowest possible value for bcrypt | ||||
|                 time_cost: 3 # Lowest possible value for argon | ||||
|                 memory_cost: 10 # Lowest possible value for argon | ||||
| @ -0,0 +1,4 @@ | ||||
| dama_doctrine_test: | ||||
|     enable_static_connection: true | ||||
|     enable_static_meta_data_cache: true | ||||
|     enable_static_query_cache: true | ||||
| @ -0,0 +1,6 @@ | ||||
| twig: | ||||
|     default_path: '%kernel.project_dir%/templates' | ||||
| 
 | ||||
| when@test: | ||||
|     twig: | ||||
|         strict_variables: true | ||||
| @ -0,0 +1,4 @@ | ||||
| framework: | ||||
|     uid: | ||||
|         default_uuid_version: 7 | ||||
|         time_based_uuid_version: 7 | ||||
| @ -0,0 +1,13 @@ | ||||
| framework: | ||||
|     validation: | ||||
|         email_validation_mode: html5 | ||||
| 
 | ||||
|         # Enables validator auto-mapping support. | ||||
|         # For instance, basic validation constraints will be inferred from Doctrine's metadata. | ||||
|         #auto_mapping: | ||||
|         #    App\Entity\: [] | ||||
| 
 | ||||
| when@test: | ||||
|     framework: | ||||
|         validation: | ||||
|             not_compromised_password: false | ||||
| @ -0,0 +1,7 @@ | ||||
| when@dev: &dev | ||||
|     # See full configuration: https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#full-default-bundle-configuration | ||||
|     zenstruck_foundry: | ||||
|         # Whether to auto-refresh proxies by default (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#auto-refresh) | ||||
|         auto_refresh_proxies: true | ||||
| 
 | ||||
| when@test: *dev | ||||
| @ -0,0 +1,5 @@ | ||||
| <?php | ||||
| 
 | ||||
| if (file_exists(dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php')) { | ||||
|     require dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php'; | ||||
| } | ||||
| @ -0,0 +1,5 @@ | ||||
| controllers: | ||||
|     resource: | ||||
|         path: ../src/Controller/ | ||||
|         namespace: App\Controller | ||||
|     type: attribute | ||||
| @ -0,0 +1,4 @@ | ||||
| api_platform: | ||||
|     resource: . | ||||
|     type: api_platform | ||||
|     prefix: /api | ||||
| @ -0,0 +1,4 @@ | ||||
| when@dev: | ||||
|     _errors: | ||||
|         resource: '@FrameworkBundle/Resources/config/routing/errors.xml' | ||||
|         prefix: /_error | ||||
| @ -0,0 +1,24 @@ | ||||
| # This file is the entry point to configure your own services. | ||||
| # Files in the packages/ subdirectory configure your dependencies. | ||||
| 
 | ||||
| # Put parameters here that don't need to change on each machine where the app is deployed | ||||
| # https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration | ||||
| parameters: | ||||
| 
 | ||||
| services: | ||||
|     # default configuration for services in *this* file | ||||
|     _defaults: | ||||
|         autowire: true      # Automatically injects dependencies in your services. | ||||
|         autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. | ||||
| 
 | ||||
|     # makes classes in src/ available to be used as services | ||||
|     # this creates a service per class whose id is the fully-qualified class name | ||||
|     App\: | ||||
|         resource: '../src/' | ||||
|         exclude: | ||||
|             - '../src/DependencyInjection/' | ||||
|             - '../src/Entity/' | ||||
|             - '../src/Kernel.php' | ||||
| 
 | ||||
|     # add more service definitions when explicit configuration is needed | ||||
|     # please note that last definitions always *replace* previous ones | ||||
| @ -0,0 +1,39 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| 
 | ||||
| <!-- https://phpunit.readthedocs.io/en/latest/configuration.html --> | ||||
| <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|          xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd" | ||||
|          backupGlobals="false" | ||||
|          colors="true" | ||||
|          bootstrap="tests/bootstrap.php" | ||||
|          convertDeprecationsToExceptions="false" | ||||
| > | ||||
|     <php> | ||||
|         <ini name="display_errors" value="1" /> | ||||
|         <ini name="error_reporting" value="-1" /> | ||||
|         <server name="APP_ENV" value="test" force="true" /> | ||||
|         <server name="SHELL_VERBOSITY" value="-1" /> | ||||
|         <server name="SYMFONY_PHPUNIT_REMOVE" value="" /> | ||||
|         <server name="SYMFONY_PHPUNIT_VERSION" value="9.6" /> | ||||
|     </php> | ||||
| 
 | ||||
|     <testsuites> | ||||
|         <testsuite name="Project Test Suite"> | ||||
|             <directory>tests</directory> | ||||
|         </testsuite> | ||||
|     </testsuites> | ||||
| 
 | ||||
|     <coverage processUncoveredFiles="true"> | ||||
|         <include> | ||||
|             <directory suffix=".php">src</directory> | ||||
|         </include> | ||||
|     </coverage> | ||||
| 
 | ||||
|     <listeners> | ||||
|         <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" /> | ||||
|     </listeners> | ||||
| 
 | ||||
|     <extensions> | ||||
|         <extension class="DAMA\DoctrineTestBundle\PHPUnit\PHPUnitExtension"/> | ||||
|     </extensions> | ||||
| </phpunit> | ||||
| @ -0,0 +1,9 @@ | ||||
| <?php | ||||
| 
 | ||||
| use App\Kernel; | ||||
| 
 | ||||
| require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; | ||||
| 
 | ||||
| return function (array $context) { | ||||
|     return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); | ||||
| }; | ||||
| @ -0,0 +1,19 @@ | ||||
| <?php | ||||
| namespace App\Controller; | ||||
| 
 | ||||
| use App\Entity\Character; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Symfony\Component\HttpKernel\Attribute\AsController; | ||||
| 
 | ||||
| #[AsController] | ||||
| final class GetDojoCharacters | ||||
| { | ||||
| 
 | ||||
|     public function __invoke($dojoId, EntityManagerInterface $em): iterable | ||||
|     { | ||||
|         return $em->getRepository(Character::class)->findBy([ | ||||
|             'dojo' => $dojoId | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,26 @@ | ||||
| <?php | ||||
| namespace App\Controller; | ||||
| 
 | ||||
| use App\Entity\Character; | ||||
| use App\Entity\Dojo; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Symfony\Bundle\SecurityBundle\Security; | ||||
| use Symfony\Component\HttpKernel\Attribute\AsController; | ||||
| 
 | ||||
| #[AsController] | ||||
| final class GetOwnDojoCharacters | ||||
| { | ||||
| 
 | ||||
|     public function __construct(private Security $security) | ||||
|     {} | ||||
| 
 | ||||
|     public function __invoke(EntityManagerInterface $em): iterable | ||||
|     { | ||||
|         $dojo = $em->getRepository(Dojo::class)->findOneByOwner($this->security->getUser()); | ||||
| 
 | ||||
|         return $em->getRepository(Character::class)->findBy([ | ||||
|             'dojo' => $dojo->id | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,19 @@ | ||||
| <?php | ||||
| namespace App\Controller; | ||||
| 
 | ||||
| use App\Entity\Fight; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Symfony\Component\HttpKernel\Attribute\AsController; | ||||
| 
 | ||||
| #[AsController] | ||||
| class GetTournamentFights | ||||
| { | ||||
| 
 | ||||
|     public function __invoke($tournamentId, EntityManagerInterface $em): iterable | ||||
|     { | ||||
|         return $em->getRepository(Fight::class)->findBy([ | ||||
|             'tournament' => $tournamentId | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,24 @@ | ||||
| <?php | ||||
| namespace App\Controller; | ||||
| 
 | ||||
| use App\Entity\User; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Symfony\Component\HttpKernel\Attribute\AsController; | ||||
| use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||||
| 
 | ||||
| #[AsController] | ||||
| final class GetUserByName | ||||
| { | ||||
| 
 | ||||
|     public function __invoke($authName, EntityManagerInterface $em): User | ||||
|     { | ||||
|         $user = $em->getRepository(User::class)->findOneBy([ | ||||
|             'authName' => $authName | ||||
|         ]); | ||||
|         if (! $user) { | ||||
|             throw new NotFoundHttpException('User not found'); | ||||
|         } | ||||
|         return $user; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,21 @@ | ||||
| <?php | ||||
| namespace App\Controller; | ||||
| 
 | ||||
| use App\Entity\Dojo; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Symfony\Bundle\SecurityBundle\Security; | ||||
| use Symfony\Component\HttpKernel\Attribute\AsController; | ||||
| 
 | ||||
| #[AsController] | ||||
| final class GetUserDojo | ||||
| { | ||||
| 
 | ||||
|     public function __construct(private Security $security) | ||||
|     {} | ||||
| 
 | ||||
|     public function __invoke(EntityManagerInterface $em): ?Dojo | ||||
|     { | ||||
|         return $em->getRepository(Dojo::class)->findOneByOwner($this->security->getUser()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,16 @@ | ||||
| <?php | ||||
| namespace App\DataFixtures; | ||||
| 
 | ||||
| use Doctrine\Bundle\FixturesBundle\Fixture; | ||||
| use Doctrine\Persistence\ObjectManager; | ||||
| 
 | ||||
| class AppFixtures extends Fixture | ||||
| { | ||||
| 
 | ||||
|     public function load(ObjectManager $manager): void | ||||
|     { | ||||
|         // $product = new Product(); | ||||
|         // $manager->persist($product); | ||||
|         $manager->flush(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,193 @@ | ||||
| <?php | ||||
| namespace App\Entity; | ||||
| 
 | ||||
| use ApiPlatform\Metadata\ApiProperty; | ||||
| use App\Validator\CharacterStats; | ||||
| use Doctrine\Common\Collections\ArrayCollection; | ||||
| use Doctrine\Common\Collections\Collection; | ||||
| use Doctrine\ORM\Mapping\Column; | ||||
| use Doctrine\ORM\Mapping\Entity; | ||||
| use Doctrine\ORM\Mapping\JoinColumn; | ||||
| use Doctrine\ORM\Mapping\ManyToMany; | ||||
| use Doctrine\ORM\Mapping\ManyToOne; | ||||
| use Doctrine\ORM\Mapping\Table; | ||||
| use Symfony\Component\Serializer\Attribute\Groups; | ||||
| use Symfony\Component\Validator\Mapping\ClassMetadata; | ||||
| 
 | ||||
| #[Entity] | ||||
| #[Table(name: '`character`')] | ||||
| class Character extends Thing | ||||
| { | ||||
| 
 | ||||
|     #[ManyToOne(inversedBy: 'members')] | ||||
|     #[JoinColumn(onDelete: 'cascade')] | ||||
|     #[Groups('public')] | ||||
|     #[ApiProperty(readableLink: false, writableLink: false)] | ||||
|     public ?Dojo $dojo; | ||||
| 
 | ||||
|     #[Column(length: 64)] | ||||
|     #[Groups('public')] | ||||
|     public string $name; | ||||
| 
 | ||||
|     #[Column] | ||||
|     #[Groups('detail')] | ||||
|     public int $strength; | ||||
| 
 | ||||
|     #[Column] | ||||
|     #[Groups('detail')] | ||||
|     public int $constitution; | ||||
| 
 | ||||
|     #[Column] | ||||
|     #[Groups('detail')] | ||||
|     public int $agility; | ||||
| 
 | ||||
|     #[Column] | ||||
|     #[Groups('detail')] | ||||
|     public int $chi; | ||||
| 
 | ||||
|     #[Groups('detail')] | ||||
|     #[ManyToMany(targetEntity: Technique::class, cascade: [ | ||||
|         'persist' | ||||
|     ])] | ||||
|     #[ApiProperty(readableLink: false, writableLink: false)] | ||||
|     public Collection $techniques; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->techniques = new ArrayCollection(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * MVP: Only | ||||
|      */ | ||||
|     #[Groups('detail')] | ||||
|     public function getFreeSkillPoints() | ||||
|     { | ||||
|         $available = 32; | ||||
|         $costs = self::skillCosts($this->getStrength()); | ||||
|         $costs += self::skillCosts($this->getConstitution()); | ||||
|         $costs += self::skillCosts($this->getAgility()); | ||||
|         $costs += self::skillCosts($this->getChi()); | ||||
| 
 | ||||
|         foreach ($this->getTechniques() as $tech) { | ||||
|             $costs += $tech->getCosts(); | ||||
|         } | ||||
|         return $available - $costs; | ||||
|     } | ||||
| 
 | ||||
|     public static function loadValidatorMetadata(ClassMetadata $metadata): void | ||||
|     { | ||||
|         $metadata->addConstraint(new CharacterStats()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Calculates the aged based on the ulid value? | ||||
|      */ | ||||
|     #[Groups('public')] | ||||
|     public function getAge(): int | ||||
|     { | ||||
|         return 21; | ||||
|     } | ||||
| 
 | ||||
|     public static function skillCosts(int $value) | ||||
|     { | ||||
|         return pow(2, $value); | ||||
|     } | ||||
| 
 | ||||
|     public function getName(): ?string | ||||
|     { | ||||
|         return $this->name; | ||||
|     } | ||||
| 
 | ||||
|     public function setName(string $name): static | ||||
|     { | ||||
|         $this->name = $name; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getStrength(): ?int | ||||
|     { | ||||
|         return $this->strength; | ||||
|     } | ||||
| 
 | ||||
|     public function setStrength(int $strength): static | ||||
|     { | ||||
|         $this->strength = $strength; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getConstitution(): ?int | ||||
|     { | ||||
|         return $this->constitution; | ||||
|     } | ||||
| 
 | ||||
|     public function setConstitution(int $constitution): static | ||||
|     { | ||||
|         $this->constitution = $constitution; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getAgility(): ?int | ||||
|     { | ||||
|         return $this->agility; | ||||
|     } | ||||
| 
 | ||||
|     public function setAgility(int $agility): static | ||||
|     { | ||||
|         $this->agility = $agility; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getChi(): ?int | ||||
|     { | ||||
|         return $this->chi; | ||||
|     } | ||||
| 
 | ||||
|     public function setChi(int $chi): static | ||||
|     { | ||||
|         $this->chi = $chi; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getDojo(): ?Dojo | ||||
|     { | ||||
|         return $this->dojo; | ||||
|     } | ||||
| 
 | ||||
|     public function setDojo(?Dojo $dojo): static | ||||
|     { | ||||
|         $this->dojo = $dojo; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return Collection<int, Technique> | ||||
|      */ | ||||
|     public function getTechniques(): Collection | ||||
|     { | ||||
|         return $this->techniques; | ||||
|     } | ||||
| 
 | ||||
|     public function addTechnique(Technique $technique): static | ||||
|     { | ||||
|         if (!$this->techniques->contains($technique)) { | ||||
|             $this->techniques->add($technique); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function removeTechnique(Technique $technique): static | ||||
|     { | ||||
|         $this->techniques->removeElement($technique); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,28 @@ | ||||
| <?php | ||||
| namespace App\Entity; | ||||
| 
 | ||||
| use Doctrine\ORM\Mapping\Entity; | ||||
| use Doctrine\ORM\Mapping\JoinColumn; | ||||
| use Doctrine\ORM\Mapping\OneToOne; | ||||
| 
 | ||||
| #[Entity] | ||||
| class City extends Thing | ||||
| { | ||||
| 
 | ||||
|     #[OneToOne(inversedBy: 'city')] | ||||
|     #[JoinColumn(onDelete: 'cascade', nullable: false)] | ||||
|     public Dungeon $dungeon; | ||||
| 
 | ||||
|     public function getDungeon(): ?Dungeon | ||||
|     { | ||||
|         return $this->dungeon; | ||||
|     } | ||||
| 
 | ||||
|     public function setDungeon(Dungeon $dungeon): static | ||||
|     { | ||||
|         $this->dungeon = $dungeon; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,71 @@ | ||||
| <?php | ||||
| namespace App\Entity; | ||||
| 
 | ||||
| use Doctrine\Common\Collections\ArrayCollection; | ||||
| use Doctrine\Common\Collections\Collection; | ||||
| use Doctrine\ORM\Mapping\Entity; | ||||
| use Doctrine\ORM\Mapping\JoinColumn; | ||||
| use Doctrine\ORM\Mapping\OneToMany; | ||||
| use Doctrine\ORM\Mapping\OneToOne; | ||||
| 
 | ||||
| #[Entity] | ||||
| class Country extends Thing | ||||
| { | ||||
| 
 | ||||
|     #[OneToOne()] | ||||
|     #[JoinColumn(onDelete: 'cascade', nullable: false)] | ||||
|     public City $capital; | ||||
| 
 | ||||
|     /** @var Prefecture[] */ | ||||
|     #[OneToMany(targetEntity: Prefecture::class, mappedBy: 'prefecture')] | ||||
|     #[JoinColumn(onDelete: 'cascade', nullable: false)] | ||||
|     public iterable $prefectures; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->prefectures = new ArrayCollection(); | ||||
|     } | ||||
| 
 | ||||
|     public function getCapital(): ?City | ||||
|     { | ||||
|         return $this->capital; | ||||
|     } | ||||
| 
 | ||||
|     public function setCapital(City $capital): static | ||||
|     { | ||||
|         $this->capital = $capital; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return Collection<int, Prefecture> | ||||
|      */ | ||||
|     public function getPrefectures(): Collection | ||||
|     { | ||||
|         return $this->prefectures; | ||||
|     } | ||||
| 
 | ||||
|     public function addPrefecture(Prefecture $prefecture): static | ||||
|     { | ||||
|         if (!$this->prefectures->contains($prefecture)) { | ||||
|             $this->prefectures->add($prefecture); | ||||
|             $prefecture->setPrefecture($this); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function removePrefecture(Prefecture $prefecture): static | ||||
|     { | ||||
|         if ($this->prefectures->removeElement($prefecture)) { | ||||
|             // set the owning side to null (unless already changed) | ||||
|             if ($prefecture->getPrefecture() === $this) { | ||||
|                 $prefecture->setPrefecture(null); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,118 @@ | ||||
| <?php | ||||
| namespace App\Entity; | ||||
| 
 | ||||
| use ApiPlatform\Metadata\ApiProperty; | ||||
| use Doctrine\Common\Collections\ArrayCollection; | ||||
| use Doctrine\Common\Collections\Collection; | ||||
| use Doctrine\ORM\Mapping\Column; | ||||
| use Doctrine\ORM\Mapping\Entity; | ||||
| use Doctrine\ORM\Mapping\JoinColumn; | ||||
| use Doctrine\ORM\Mapping\ManyToOne; | ||||
| use Doctrine\ORM\Mapping\OneToMany; | ||||
| use Doctrine\ORM\Mapping\OneToOne; | ||||
| 
 | ||||
| #[Entity] | ||||
| class Dojo extends Thing | ||||
| { | ||||
| 
 | ||||
|     #[Column(unique: true)] | ||||
|     public string $name; | ||||
| 
 | ||||
|     /** @var Character[] */ | ||||
|     #[ApiProperty(writable: false)] | ||||
|     #[OneToMany(targetEntity: Character::class, mappedBy: 'dojo')] | ||||
|     #[JoinColumn(onDelete: 'cascade', nullable: false)] | ||||
|     public Collection $members; | ||||
| 
 | ||||
|     #[ApiProperty(writable: false)] | ||||
|     #[ManyToOne()] | ||||
|     #[JoinColumn(onDelete: 'cascade', nullable: true)] | ||||
|     public ?Village $village; | ||||
| 
 | ||||
|     #[ApiProperty(writable: false)] | ||||
|     #[OneToOne(inversedBy: 'dojo', cascade: [ | ||||
|         'persist' | ||||
|     ])] | ||||
|     #[JoinColumn(onDelete: 'cascade', nullable: false)] | ||||
|     public User $owner; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->members = new ArrayCollection(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Helper method that reads the timestamp section from the ulid | ||||
|      */ | ||||
|     public function getCreatedAt(): \DateTimeImmutable | ||||
|     { | ||||
|         return $this->id->getDateTime(); | ||||
|     } | ||||
| 
 | ||||
|     public function getName(): ?string | ||||
|     { | ||||
|         return $this->name; | ||||
|     } | ||||
| 
 | ||||
|     public function setName(string $name): static | ||||
|     { | ||||
|         $this->name = $name; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return Collection<int, Character> | ||||
|      */ | ||||
|     public function getMembers(): Collection | ||||
|     { | ||||
|         return $this->members; | ||||
|     } | ||||
| 
 | ||||
|     public function addMember(Character $member): static | ||||
|     { | ||||
|         if (!$this->members->contains($member)) { | ||||
|             $this->members->add($member); | ||||
|             $member->setDojo($this); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function removeMember(Character $member): static | ||||
|     { | ||||
|         if ($this->members->removeElement($member)) { | ||||
|             // set the owning side to null (unless already changed) | ||||
|             if ($member->getDojo() === $this) { | ||||
|                 $member->setDojo(null); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getVillage(): ?Village | ||||
|     { | ||||
|         return $this->village; | ||||
|     } | ||||
| 
 | ||||
|     public function setVillage(?Village $village): static | ||||
|     { | ||||
|         $this->village = $village; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getOwner(): ?User | ||||
|     { | ||||
|         return $this->owner; | ||||
|     } | ||||
| 
 | ||||
|     public function setOwner(User $owner): static | ||||
|     { | ||||
|         $this->owner = $owner; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,28 @@ | ||||
| <?php | ||||
| namespace App\Entity; | ||||
| 
 | ||||
| use Doctrine\ORM\Mapping\Entity; | ||||
| use Doctrine\ORM\Mapping\JoinColumn; | ||||
| use Doctrine\ORM\Mapping\OneToOne; | ||||
| 
 | ||||
| #[Entity] | ||||
| class Dungeon extends Thing | ||||
| { | ||||
| 
 | ||||
|     #[OneToOne(inversedBy: 'dungeon')] | ||||
|     #[JoinColumn(onDelete: 'cascade', nullable: false)] | ||||
|     public City $city; | ||||
| 
 | ||||
|     public function getCity(): ?City | ||||
|     { | ||||
|         return $this->city; | ||||
|     } | ||||
| 
 | ||||
|     public function setCity(City $city): static | ||||
|     { | ||||
|         $this->city = $city; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,107 @@ | ||||
| <?php | ||||
| namespace App\Entity; | ||||
| 
 | ||||
| use App\Repository\FightRepository; | ||||
| use Doctrine\Common\Collections\ArrayCollection; | ||||
| use Doctrine\Common\Collections\Collection; | ||||
| use Doctrine\DBAL\Types\Types; | ||||
| use Doctrine\ORM\Mapping as ORM; | ||||
| 
 | ||||
| #[ORM\Entity(repositoryClass: FightRepository::class)] | ||||
| class Fight extends Thing | ||||
| { | ||||
| 
 | ||||
|     #[ORM\ManyToMany(targetEntity: Character::class)] | ||||
|     private Collection $characters; | ||||
| 
 | ||||
|     #[ORM\Column(type: Types::DATETIME_MUTABLE)] | ||||
|     private ?\DateTimeInterface $startDate = null; | ||||
| 
 | ||||
|     #[ORM\Column()] | ||||
|     private array $events = []; | ||||
| 
 | ||||
|     #[ORM\ManyToOne] | ||||
|     private ?Character $winner = null; | ||||
| 
 | ||||
|     #[ORM\ManyToOne(inversedBy: 'fights')] | ||||
|     #[ORM\JoinColumn(nullable: false, onDelete: 'cascade')] | ||||
|     private ?Tournament $tournament = null; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->characters = new ArrayCollection(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * @return Collection<int, Character> | ||||
|      */ | ||||
|     public function getCharacters(): Collection | ||||
|     { | ||||
|         return $this->characters; | ||||
|     } | ||||
| 
 | ||||
|     public function addCharacter(Character $character): static | ||||
|     { | ||||
|         if (! $this->characters->contains($character)) { | ||||
|             $this->characters->add($character); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function removeCharacter(Character $character): static | ||||
|     { | ||||
|         $this->characters->removeElement($character); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getStartDate(): ?\DateTimeInterface | ||||
|     { | ||||
|         return $this->startDate; | ||||
|     } | ||||
| 
 | ||||
|     public function setStartDate(\DateTimeInterface $startDate): static | ||||
|     { | ||||
|         $this->startDate = $startDate; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getEvents(): array | ||||
|     { | ||||
|         return $this->events; | ||||
|     } | ||||
| 
 | ||||
|     public function setEvents(array $events): static | ||||
|     { | ||||
|         $this->events = $events; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getWinner(): ?Character | ||||
|     { | ||||
|         return $this->winner; | ||||
|     } | ||||
| 
 | ||||
|     public function setWinner(?Character $winner): static | ||||
|     { | ||||
|         $this->winner = $winner; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getTournament(): ?Tournament | ||||
|     { | ||||
|         return $this->tournament; | ||||
|     } | ||||
| 
 | ||||
|     public function setTournament(?Tournament $tournament): static | ||||
|     { | ||||
|         $this->tournament = $tournament; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,89 @@ | ||||
| <?php | ||||
| namespace App\Entity; | ||||
| 
 | ||||
| use Doctrine\Common\Collections\ArrayCollection; | ||||
| use Doctrine\Common\Collections\Collection; | ||||
| use Doctrine\ORM\Mapping\Entity; | ||||
| use Doctrine\ORM\Mapping\JoinColumn; | ||||
| use Doctrine\ORM\Mapping\ManyToOne; | ||||
| use Doctrine\ORM\Mapping\OneToMany; | ||||
| use Doctrine\ORM\Mapping\OneToOne; | ||||
| 
 | ||||
| #[Entity] | ||||
| class Prefecture extends Thing | ||||
| { | ||||
| 
 | ||||
|     // FIXME:Shortcut to its users? | ||||
|     #[OneToOne()] | ||||
|     #[JoinColumn(onDelete: 'cascade', nullable: false)] | ||||
|     public City $capital; | ||||
| 
 | ||||
|     #[ManyToOne()] | ||||
|     #[JoinColumn(onDelete: 'cascade', nullable: false)] | ||||
|     public Country $country; | ||||
| 
 | ||||
|     /** @var Village[] */ | ||||
|     #[OneToMany(targetEntity: Village::class, mappedBy: 'prefecture')] | ||||
|     #[JoinColumn(onDelete: 'cascade', nullable: false)] | ||||
|     public iterable $villages; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->villages = new ArrayCollection(); | ||||
|     } | ||||
| 
 | ||||
|     public function getCapital(): ?City | ||||
|     { | ||||
|         return $this->capital; | ||||
|     } | ||||
| 
 | ||||
|     public function setCapital(City $capital): static | ||||
|     { | ||||
|         $this->capital = $capital; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getCountry(): ?Country | ||||
|     { | ||||
|         return $this->country; | ||||
|     } | ||||
| 
 | ||||
|     public function setCountry(?Country $country): static | ||||
|     { | ||||
|         $this->country = $country; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return Collection<int, Village> | ||||
|      */ | ||||
|     public function getVillages(): Collection | ||||
|     { | ||||
|         return $this->villages; | ||||
|     } | ||||
| 
 | ||||
|     public function addVillage(Village $village): static | ||||
|     { | ||||
|         if (!$this->villages->contains($village)) { | ||||
|             $this->villages->add($village); | ||||
|             $village->setPrefecture($this); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function removeVillage(Village $village): static | ||||
|     { | ||||
|         if ($this->villages->removeElement($village)) { | ||||
|             // set the owning side to null (unless already changed) | ||||
|             if ($village->getPrefecture() === $this) { | ||||
|                 $village->setPrefecture(null); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,121 @@ | ||||
| <?php | ||||
| namespace App\Entity; | ||||
| 
 | ||||
| use ApiPlatform\Metadata\ApiProperty; | ||||
| use Doctrine\ORM\Mapping\Column; | ||||
| use Doctrine\ORM\Mapping\Entity; | ||||
| use Doctrine\ORM\Mapping\JoinColumn; | ||||
| use Doctrine\ORM\Mapping\ManyToOne; | ||||
| 
 | ||||
| #[Entity(repositoryClass: 'App\Repository\TechniqueRepository')] | ||||
| class Technique extends Thing | ||||
| { | ||||
| 
 | ||||
|     #[Column(unique: true)] | ||||
|     public string $name; | ||||
| 
 | ||||
|     /** | ||||
|      * For now we calculate costs for mastering a technique. | ||||
|      */ | ||||
|     #[Column] | ||||
|     public int $costs; | ||||
| 
 | ||||
|     /** | ||||
|      * Formula to calculate the damage based on the stats. | ||||
|      */ | ||||
|     #[Column] | ||||
|     public string $damage; | ||||
| 
 | ||||
|     /** | ||||
|      * Formula to calculate the consumed energy on use based on the stats. | ||||
|      */ | ||||
|     #[Column] | ||||
|     public string $energy; | ||||
| 
 | ||||
|     /** | ||||
|      * Formula to calculate the hit chance accuracy based on the stats. | ||||
|      */ | ||||
|     #[Column] | ||||
|     public string $accuracy; | ||||
| 
 | ||||
|     /** | ||||
|      * Technique that is required to be learned before this Technique. | ||||
|      */ | ||||
|     #[ManyToOne()] | ||||
|     #[JoinColumn(onDelete: 'SET NULL', nullable: true)] | ||||
|     #[ApiProperty(readableLink: false, writableLink: false)] | ||||
|     public ?Technique $prerequisite; | ||||
| 
 | ||||
|     public function getCosts(): ?int | ||||
|     { | ||||
|         return $this->costs; | ||||
|     } | ||||
| 
 | ||||
|     public function setCosts(int $costs): static | ||||
|     { | ||||
|         $this->costs = $costs; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getDamage(): ?string | ||||
|     { | ||||
|         return $this->damage; | ||||
|     } | ||||
| 
 | ||||
|     public function setDamage(string $damage): static | ||||
|     { | ||||
|         $this->damage = $damage; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getEnergy(): ?string | ||||
|     { | ||||
|         return $this->energy; | ||||
|     } | ||||
| 
 | ||||
|     public function setEnergy(string $energy): static | ||||
|     { | ||||
|         $this->energy = $energy; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getAccuracy(): ?string | ||||
|     { | ||||
|         return $this->accuracy; | ||||
|     } | ||||
| 
 | ||||
|     public function setAccuracy(string $accuracy): static | ||||
|     { | ||||
|         $this->accuracy = $accuracy; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getPrerequisite(): ?self | ||||
|     { | ||||
|         return $this->prerequisite; | ||||
|     } | ||||
| 
 | ||||
|     public function setPrerequisite(?self $prerequisite): static | ||||
|     { | ||||
|         $this->prerequisite = $prerequisite; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getName(): ?string | ||||
|     { | ||||
|         return $this->name; | ||||
|     } | ||||
| 
 | ||||
|     public function setName(string $name): static | ||||
|     { | ||||
|         $this->name = $name; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,24 @@ | ||||
| <?php | ||||
| namespace App\Entity; | ||||
| 
 | ||||
| use Doctrine\ORM\Mapping as ORM; | ||||
| use Symfony\Bridge\Doctrine\Types\UlidType; | ||||
| use Symfony\Component\Serializer\Attribute\Groups; | ||||
| use Symfony\Component\Uid\Ulid; | ||||
| 
 | ||||
| abstract class Thing | ||||
| { | ||||
| 
 | ||||
|     #[ORM\Id] | ||||
|     #[ORM\Column(type: UlidType::NAME, unique: true)] | ||||
|     #[ORM\GeneratedValue(strategy: 'CUSTOM')] | ||||
|     #[ORM\CustomIdGenerator(class: 'doctrine.ulid_generator')] | ||||
|     #[Groups('public')] | ||||
|     public ?Ulid $id; | ||||
| 
 | ||||
|     public function getId(): ?Ulid | ||||
|     { | ||||
|         return $this->id; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,126 @@ | ||||
| <?php | ||||
| namespace App\Entity; | ||||
| 
 | ||||
| use App\Repository\TournamentRepository; | ||||
| use Doctrine\Common\Collections\ArrayCollection; | ||||
| use Doctrine\Common\Collections\Collection; | ||||
| use Doctrine\DBAL\Types\Types; | ||||
| use Doctrine\ORM\Mapping as ORM; | ||||
| 
 | ||||
| #[ORM\Entity(repositoryClass: TournamentRepository::class)] | ||||
| class Tournament extends Thing | ||||
| { | ||||
| 
 | ||||
|     #[ORM\Column(length: 255)] | ||||
|     public string $name; | ||||
| 
 | ||||
|     #[ORM\ManyToMany(targetEntity: Character::class)] | ||||
|     public Collection $characters; | ||||
| 
 | ||||
|     #[ORM\Column(type: Types::DATETIME_MUTABLE)] | ||||
|     private ?\DateTimeInterface $startDate = null; | ||||
| 
 | ||||
|     #[ORM\OneToMany(mappedBy: 'tournament', targetEntity: Fight::class)] | ||||
|     private Collection $fights; | ||||
| 
 | ||||
|     #[ORM\ManyToOne] | ||||
|     private ?Character $winner = null; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->characters = new ArrayCollection(); | ||||
|         $this->fights = new ArrayCollection(); | ||||
|     } | ||||
| 
 | ||||
|     public function getName(): ?string | ||||
|     { | ||||
|         return $this->name; | ||||
|     } | ||||
| 
 | ||||
|     public function setName(string $name): static | ||||
|     { | ||||
|         $this->name = $name; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * @return Collection<int, Character> | ||||
|      */ | ||||
|     public function getCharacters(): Collection | ||||
|     { | ||||
|         return $this->characters; | ||||
|     } | ||||
| 
 | ||||
|     public function addCharacter(Character $character): static | ||||
|     { | ||||
|         if (! $this->characters->contains($character)) { | ||||
|             $this->characters->add($character); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function removeCharacter(Character $character): static | ||||
|     { | ||||
|         $this->characters->removeElement($character); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getStartDate(): ?\DateTimeInterface | ||||
|     { | ||||
|         return $this->startDate; | ||||
|     } | ||||
| 
 | ||||
|     public function setStartDate(\DateTimeInterface $startDate): static | ||||
|     { | ||||
|         $this->startDate = $startDate; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * @return Collection<int, Fight> | ||||
|      */ | ||||
|     public function getFights(): Collection | ||||
|     { | ||||
|         return $this->fights; | ||||
|     } | ||||
| 
 | ||||
|     public function addFight(Fight $fight): static | ||||
|     { | ||||
|         if (! $this->fights->contains($fight)) { | ||||
|             $this->fights->add($fight); | ||||
|             $fight->setTournament($this); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function removeFight(Fight $fight): static | ||||
|     { | ||||
|         if ($this->fights->removeElement($fight)) { | ||||
|             // set the owning side to null (unless already changed) | ||||
|             if ($fight->getTournament() === $this) { | ||||
|                 $fight->setTournament(null); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getWinner(): ?Character | ||||
|     { | ||||
|         return $this->winner; | ||||
|     } | ||||
| 
 | ||||
|     public function setWinner(?Character $winner): static | ||||
|     { | ||||
|         $this->winner = $winner; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,15 @@ | ||||
| <?php | ||||
| namespace App\Entity; | ||||
| 
 | ||||
| use App\Validator\CharacterOwned; | ||||
| use App\Validator\StartDateInFuture; | ||||
| 
 | ||||
| class TournamentRegistration | ||||
| { | ||||
| 
 | ||||
|     #[StartDateInFuture()] | ||||
|     public Tournament $tournament; | ||||
| 
 | ||||
|     #[CharacterOwned()] | ||||
|     public Character $character; | ||||
| } | ||||
| @ -0,0 +1,97 @@ | ||||
| <?php | ||||
| namespace App\Entity; | ||||
| 
 | ||||
| use ApiPlatform\Metadata\ApiProperty; | ||||
| use Doctrine\ORM\Mapping\Column; | ||||
| use Doctrine\ORM\Mapping\Entity; | ||||
| use Doctrine\ORM\Mapping\JoinColumn; | ||||
| use Doctrine\ORM\Mapping\OneToOne; | ||||
| use Doctrine\ORM\Mapping\Table; | ||||
| use Symfony\Component\Security\Core\User\UserInterface; | ||||
| 
 | ||||
| #[Entity(repositoryClass: 'App\Repository\UserRepository')] | ||||
| #[Table(name: '`user`')] | ||||
| class User extends Thing implements UserInterface | ||||
| { | ||||
| 
 | ||||
|     // from discord | ||||
|     #[ApiProperty(writable: false)] | ||||
|     #[Column(length: 32)] | ||||
|     public string $authName; | ||||
| 
 | ||||
|     #[ApiProperty(writable: false)] | ||||
|     #[OneToOne(inversedBy: 'owner')] | ||||
|     #[JoinColumn(onDelete: 'cascade', nullable: true)] | ||||
|     public ?Dojo $dojo; | ||||
| 
 | ||||
|     // anonymous data used for the client | ||||
|     #[ApiProperty()] | ||||
|     #[Column(type: 'json')] | ||||
|     public mixed $properties; | ||||
| 
 | ||||
|     public function __construct(string $authName) | ||||
|     { | ||||
|         $this->authName = $authName; | ||||
|         $this->properties = []; | ||||
|     } | ||||
| 
 | ||||
|     public function getProperties(): array | ||||
|     { | ||||
|         return $this->properties; | ||||
|     } | ||||
| 
 | ||||
|     public function setProperties(array $properties): static | ||||
|     { | ||||
|         $this->properties = $properties; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getDojo(): ?Dojo | ||||
|     { | ||||
|         return $this->dojo; | ||||
|     } | ||||
| 
 | ||||
|     public function setDojo(?Dojo $dojo): static | ||||
|     { | ||||
|         $this->dojo = $dojo; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getUserIdentifier(): string | ||||
|     { | ||||
|         return $this->id; | ||||
|     } | ||||
| 
 | ||||
|     public function eraseCredentials(): void | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     public function getRoles(): array | ||||
|     { | ||||
|         $array = [ | ||||
|             "ROLE_USER" | ||||
|         ]; | ||||
| 
 | ||||
|         if ($this->authName == 'dehecht' || $this->authName == '.radiskull') { | ||||
|             $array[] = 'ROLE_ADMIN'; | ||||
|         } | ||||
| 
 | ||||
|         return $array; | ||||
|     } | ||||
| 
 | ||||
|     public function getAuthName(): ?string | ||||
|     { | ||||
|         return $this->authName; | ||||
|     } | ||||
| 
 | ||||
|     public function setAuthName(string $authName): static | ||||
|     { | ||||
|         $this->authName = $authName; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,71 @@ | ||||
| <?php | ||||
| namespace App\Entity; | ||||
| 
 | ||||
| use Doctrine\Common\Collections\ArrayCollection; | ||||
| use Doctrine\Common\Collections\Collection; | ||||
| use Doctrine\ORM\Mapping\Entity; | ||||
| use Doctrine\ORM\Mapping\JoinColumn; | ||||
| use Doctrine\ORM\Mapping\ManyToOne; | ||||
| use Doctrine\ORM\Mapping\OneToMany; | ||||
| 
 | ||||
| #[Entity] | ||||
| class Village extends Thing | ||||
| { | ||||
| 
 | ||||
|     /** @var Dojo[] */ | ||||
|     #[OneToMany(targetEntity: Dojo::class, mappedBy: 'village')] | ||||
|     #[JoinColumn(onDelete: 'cascade', nullable: false)] | ||||
|     public iterable $dojos; | ||||
| 
 | ||||
|     #[ManyToOne()] | ||||
|     #[JoinColumn(onDelete: 'cascade', nullable: false)] | ||||
|     public Prefecture $prefecture; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->dojos = new ArrayCollection(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return Collection<int, Dojo> | ||||
|      */ | ||||
|     public function getDojos(): Collection | ||||
|     { | ||||
|         return $this->dojos; | ||||
|     } | ||||
| 
 | ||||
|     public function addDojo(Dojo $dojo): static | ||||
|     { | ||||
|         if (!$this->dojos->contains($dojo)) { | ||||
|             $this->dojos->add($dojo); | ||||
|             $dojo->setVillage($this); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function removeDojo(Dojo $dojo): static | ||||
|     { | ||||
|         if ($this->dojos->removeElement($dojo)) { | ||||
|             // set the owning side to null (unless already changed) | ||||
|             if ($dojo->getVillage() === $this) { | ||||
|                 $dojo->setVillage(null); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     public function getPrefecture(): ?Prefecture | ||||
|     { | ||||
|         return $this->prefecture; | ||||
|     } | ||||
| 
 | ||||
|     public function setPrefecture(?Prefecture $prefecture): static | ||||
|     { | ||||
|         $this->prefecture = $prefecture; | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,73 @@ | ||||
| <?php | ||||
| namespace App\Factory; | ||||
| 
 | ||||
| use App\Entity\Character; | ||||
| use Zenstruck\Foundry\ModelFactory; | ||||
| 
 | ||||
| /** | ||||
|  * | ||||
|  * @extends ModelFactory<Character> | ||||
|  * | ||||
|  * @method Character|Proxy create(array|callable $attributes = []) | ||||
|  * @method static Character|Proxy createOne(array $attributes = []) | ||||
|  * @method static Character|Proxy find(object|array|mixed $criteria) | ||||
|  * @method static Character|Proxy findOrCreate(array $attributes) | ||||
|  * @method static Character|Proxy first(string $sortedField = 'id') | ||||
|  * @method static Character|Proxy last(string $sortedField = 'id') | ||||
|  * @method static Character|Proxy random(array $attributes = []) | ||||
|  * @method static Character|Proxy randomOrCreate(array $attributes = []) | ||||
|  * @method static EntityRepository|RepositoryProxy repository() | ||||
|  * @method static Character[]|Proxy[] all() | ||||
|  * @method static Character[]|Proxy[] createMany(int $number, array|callable $attributes = []) | ||||
|  * @method static Character[]|Proxy[] createSequence(iterable|callable $sequence) | ||||
|  * @method static Character[]|Proxy[] findBy(array $attributes) | ||||
|  * @method static Character[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) | ||||
|  * @method static Character[]|Proxy[] randomSet(int $number, array $attributes = []) | ||||
|  */ | ||||
| final class CharacterFactory extends ModelFactory | ||||
| { | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services | ||||
|      * | ||||
|      * @todo inject services if required | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories | ||||
|      * | ||||
|      * @todo add your default values here | ||||
|      */ | ||||
|     protected function getDefaults(): array | ||||
|     { | ||||
|         return [ | ||||
|             'name' => self::faker()->firstName(), | ||||
|             'strength' => self::faker()->numberBetween(1, 4), | ||||
|             'constitution' => self::faker()->numberBetween(1, 4), | ||||
|             'agility' => self::faker()->numberBetween(1, 4), | ||||
|             'chi' => self::faker()->numberBetween(1, 4), | ||||
|             'techniques' => TechniqueFactory::createMany(2) | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization | ||||
|      */ | ||||
|     protected function initialize(): self | ||||
|     { | ||||
|         return $this; | ||||
|         // ->afterInstantiate(function(Character $character): void {}) | ||||
|     } | ||||
| 
 | ||||
|     protected static function getClass(): string | ||||
|     { | ||||
|         return Character::class; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,69 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Factory; | ||||
| 
 | ||||
| use App\Entity\Dojo; | ||||
| use Doctrine\ORM\EntityRepository; | ||||
| use Zenstruck\Foundry\ModelFactory; | ||||
| use Zenstruck\Foundry\Proxy; | ||||
| use Zenstruck\Foundry\RepositoryProxy; | ||||
| 
 | ||||
| /** | ||||
|  * @extends ModelFactory<Dojo> | ||||
|  * | ||||
|  * @method        Dojo|Proxy                       create(array|callable $attributes = []) | ||||
|  * @method static Dojo|Proxy                       createOne(array $attributes = []) | ||||
|  * @method static Dojo|Proxy                       find(object|array|mixed $criteria) | ||||
|  * @method static Dojo|Proxy                       findOrCreate(array $attributes) | ||||
|  * @method static Dojo|Proxy                       first(string $sortedField = 'id') | ||||
|  * @method static Dojo|Proxy                       last(string $sortedField = 'id') | ||||
|  * @method static Dojo|Proxy                       random(array $attributes = []) | ||||
|  * @method static Dojo|Proxy                       randomOrCreate(array $attributes = []) | ||||
|  * @method static EntityRepository|RepositoryProxy repository() | ||||
|  * @method static Dojo[]|Proxy[]                   all() | ||||
|  * @method static Dojo[]|Proxy[]                   createMany(int $number, array|callable $attributes = []) | ||||
|  * @method static Dojo[]|Proxy[]                   createSequence(iterable|callable $sequence) | ||||
|  * @method static Dojo[]|Proxy[]                   findBy(array $attributes) | ||||
|  * @method static Dojo[]|Proxy[]                   randomRange(int $min, int $max, array $attributes = []) | ||||
|  * @method static Dojo[]|Proxy[]                   randomSet(int $number, array $attributes = []) | ||||
|  */ | ||||
| final class DojoFactory extends ModelFactory | ||||
| { | ||||
|     /** | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services | ||||
|      * | ||||
|      * @todo inject services if required | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories | ||||
|      * | ||||
|      * @todo add your default values here | ||||
|      */ | ||||
|     protected function getDefaults(): array | ||||
|     { | ||||
|         return [ | ||||
|             'name' => self::faker()->text(), | ||||
|             'owner' => UserFactory::new(), | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization | ||||
|      */ | ||||
|     protected function initialize(): self | ||||
|     { | ||||
|         return $this | ||||
|             // ->afterInstantiate(function(Dojo $dojo): void {}) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     protected static function getClass(): string | ||||
|     { | ||||
|         return Dojo::class; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,71 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Factory; | ||||
| 
 | ||||
| use App\Entity\Fight; | ||||
| use App\Repository\FightRepository; | ||||
| use Zenstruck\Foundry\ModelFactory; | ||||
| use Zenstruck\Foundry\Proxy; | ||||
| use Zenstruck\Foundry\RepositoryProxy; | ||||
| 
 | ||||
| /** | ||||
|  * @extends ModelFactory<Fight> | ||||
|  * | ||||
|  * @method        Fight|Proxy                     create(array|callable $attributes = []) | ||||
|  * @method static Fight|Proxy                     createOne(array $attributes = []) | ||||
|  * @method static Fight|Proxy                     find(object|array|mixed $criteria) | ||||
|  * @method static Fight|Proxy                     findOrCreate(array $attributes) | ||||
|  * @method static Fight|Proxy                     first(string $sortedField = 'id') | ||||
|  * @method static Fight|Proxy                     last(string $sortedField = 'id') | ||||
|  * @method static Fight|Proxy                     random(array $attributes = []) | ||||
|  * @method static Fight|Proxy                     randomOrCreate(array $attributes = []) | ||||
|  * @method static FightRepository|RepositoryProxy repository() | ||||
|  * @method static Fight[]|Proxy[]                 all() | ||||
|  * @method static Fight[]|Proxy[]                 createMany(int $number, array|callable $attributes = []) | ||||
|  * @method static Fight[]|Proxy[]                 createSequence(iterable|callable $sequence) | ||||
|  * @method static Fight[]|Proxy[]                 findBy(array $attributes) | ||||
|  * @method static Fight[]|Proxy[]                 randomRange(int $min, int $max, array $attributes = []) | ||||
|  * @method static Fight[]|Proxy[]                 randomSet(int $number, array $attributes = []) | ||||
|  */ | ||||
| final class FightFactory extends ModelFactory | ||||
| { | ||||
|     /** | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services | ||||
|      * | ||||
|      * @todo inject services if required | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories | ||||
|      * | ||||
|      * @todo add your default values here | ||||
|      */ | ||||
|     protected function getDefaults(): array | ||||
|     { | ||||
|         return [ | ||||
|             'events' => [], | ||||
|             'startDate' => self::faker()->dateTime(), | ||||
|             'tournament' => TournamentFactory::new(), | ||||
|             'winner' => CharacterFactory::new(), | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization | ||||
|      */ | ||||
|     protected function initialize(): self | ||||
|     { | ||||
|         return $this | ||||
|             // ->afterInstantiate(function(Fight $fight): void {}) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     protected static function getClass(): string | ||||
|     { | ||||
|         return Fight::class; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,73 @@ | ||||
| <?php | ||||
| namespace App\Factory; | ||||
| 
 | ||||
| use App\Entity\Technique; | ||||
| use Zenstruck\Foundry\ModelFactory; | ||||
| 
 | ||||
| /** | ||||
|  * | ||||
|  * @extends ModelFactory<Technique> | ||||
|  * | ||||
|  * @method Technique|Proxy create(array|callable $attributes = []) | ||||
|  * @method static Technique|Proxy createOne(array $attributes = []) | ||||
|  * @method static Technique|Proxy find(object|array|mixed $criteria) | ||||
|  * @method static Technique|Proxy findOrCreate(array $attributes) | ||||
|  * @method static Technique|Proxy first(string $sortedField = 'id') | ||||
|  * @method static Technique|Proxy last(string $sortedField = 'id') | ||||
|  * @method static Technique|Proxy random(array $attributes = []) | ||||
|  * @method static Technique|Proxy randomOrCreate(array $attributes = []) | ||||
|  * @method static EntityRepository|RepositoryProxy repository() | ||||
|  * @method static Technique[]|Proxy[] all() | ||||
|  * @method static Technique[]|Proxy[] createMany(int $number, array|callable $attributes = []) | ||||
|  * @method static Technique[]|Proxy[] createSequence(iterable|callable $sequence) | ||||
|  * @method static Technique[]|Proxy[] findBy(array $attributes) | ||||
|  * @method static Technique[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) | ||||
|  * @method static Technique[]|Proxy[] randomSet(int $number, array $attributes = []) | ||||
|  */ | ||||
| final class TechniqueFactory extends ModelFactory | ||||
| { | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services | ||||
|      * | ||||
|      * @todo inject services if required | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories | ||||
|      * | ||||
|      * @todo add your default values here | ||||
|      */ | ||||
|     protected function getDefaults(): array | ||||
|     { | ||||
|         return [ | ||||
|             'name' => self::faker()->ean13(), | ||||
|             'accuracy' => self::faker()->text(), | ||||
|             'costs' => self::faker()->numberBetween(1, 2), | ||||
|             'damage' => self::faker()->text(), | ||||
|             'energy' => self::faker()->text(), | ||||
|             'prerequisite' => self::faker()->boolean() ? NULL : self::new() | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization | ||||
|      */ | ||||
|     protected function initialize(): self | ||||
|     { | ||||
|         return $this; | ||||
|         // ->afterInstantiate(function(Technique $technique): void {}) | ||||
|     } | ||||
| 
 | ||||
|     protected static function getClass(): string | ||||
|     { | ||||
|         return Technique::class; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,69 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Factory; | ||||
| 
 | ||||
| use App\Entity\Tournament; | ||||
| use App\Repository\TournamentRepository; | ||||
| use Zenstruck\Foundry\ModelFactory; | ||||
| use Zenstruck\Foundry\Proxy; | ||||
| use Zenstruck\Foundry\RepositoryProxy; | ||||
| 
 | ||||
| /** | ||||
|  * @extends ModelFactory<Tournament> | ||||
|  * | ||||
|  * @method        Tournament|Proxy                     create(array|callable $attributes = []) | ||||
|  * @method static Tournament|Proxy                     createOne(array $attributes = []) | ||||
|  * @method static Tournament|Proxy                     find(object|array|mixed $criteria) | ||||
|  * @method static Tournament|Proxy                     findOrCreate(array $attributes) | ||||
|  * @method static Tournament|Proxy                     first(string $sortedField = 'id') | ||||
|  * @method static Tournament|Proxy                     last(string $sortedField = 'id') | ||||
|  * @method static Tournament|Proxy                     random(array $attributes = []) | ||||
|  * @method static Tournament|Proxy                     randomOrCreate(array $attributes = []) | ||||
|  * @method static TournamentRepository|RepositoryProxy repository() | ||||
|  * @method static Tournament[]|Proxy[]                 all() | ||||
|  * @method static Tournament[]|Proxy[]                 createMany(int $number, array|callable $attributes = []) | ||||
|  * @method static Tournament[]|Proxy[]                 createSequence(iterable|callable $sequence) | ||||
|  * @method static Tournament[]|Proxy[]                 findBy(array $attributes) | ||||
|  * @method static Tournament[]|Proxy[]                 randomRange(int $min, int $max, array $attributes = []) | ||||
|  * @method static Tournament[]|Proxy[]                 randomSet(int $number, array $attributes = []) | ||||
|  */ | ||||
| final class TournamentFactory extends ModelFactory | ||||
| { | ||||
|     /** | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services | ||||
|      * | ||||
|      * @todo inject services if required | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories | ||||
|      * | ||||
|      * @todo add your default values here | ||||
|      */ | ||||
|     protected function getDefaults(): array | ||||
|     { | ||||
|         return [ | ||||
|             'name' => self::faker()->text(255), | ||||
|             'startDate' => self::faker()->dateTime(), | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization | ||||
|      */ | ||||
|     protected function initialize(): self | ||||
|     { | ||||
|         return $this | ||||
|             // ->afterInstantiate(function(Tournament $tournament): void {}) | ||||
|         ; | ||||
|     } | ||||
| 
 | ||||
|     protected static function getClass(): string | ||||
|     { | ||||
|         return Tournament::class; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,69 @@ | ||||
| <?php | ||||
| namespace App\Factory; | ||||
| 
 | ||||
| use App\Entity\User; | ||||
| use Zenstruck\Foundry\ModelFactory; | ||||
| 
 | ||||
| /** | ||||
|  * | ||||
|  * @extends ModelFactory<User> | ||||
|  * | ||||
|  * @method User|Proxy create(array|callable $attributes = []) | ||||
|  * @method static User|Proxy createOne(array $attributes = []) | ||||
|  * @method static User|Proxy find(object|array|mixed $criteria) | ||||
|  * @method static User|Proxy findOrCreate(array $attributes) | ||||
|  * @method static User|Proxy first(string $sortedField = 'id') | ||||
|  * @method static User|Proxy last(string $sortedField = 'id') | ||||
|  * @method static User|Proxy random(array $attributes = []) | ||||
|  * @method static User|Proxy randomOrCreate(array $attributes = []) | ||||
|  * @method static EntityRepository|RepositoryProxy repository() | ||||
|  * @method static User[]|Proxy[] all() | ||||
|  * @method static User[]|Proxy[] createMany(int $number, array|callable $attributes = []) | ||||
|  * @method static User[]|Proxy[] createSequence(iterable|callable $sequence) | ||||
|  * @method static User[]|Proxy[] findBy(array $attributes) | ||||
|  * @method static User[]|Proxy[] randomRange(int $min, int $max, array $attributes = []) | ||||
|  * @method static User[]|Proxy[] randomSet(int $number, array $attributes = []) | ||||
|  */ | ||||
| final class UserFactory extends ModelFactory | ||||
| { | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services | ||||
|      * | ||||
|      * @todo inject services if required | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories | ||||
|      * | ||||
|      * @todo add your default values here | ||||
|      */ | ||||
|     protected function getDefaults(): array | ||||
|     { | ||||
|         return [ | ||||
|             'authName' => self::faker()->userName(), | ||||
|             'properties' => [] | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization | ||||
|      */ | ||||
|     protected function initialize(): self | ||||
|     { | ||||
|         return $this; | ||||
|         // ->afterInstantiate(function(User $user): void {}) | ||||
|     } | ||||
| 
 | ||||
|     protected static function getClass(): string | ||||
|     { | ||||
|         return User::class; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App; | ||||
| 
 | ||||
| use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; | ||||
| use Symfony\Component\HttpKernel\Kernel as BaseKernel; | ||||
| 
 | ||||
| class Kernel extends BaseKernel | ||||
| { | ||||
|     use MicroKernelTrait; | ||||
| } | ||||
| @ -0,0 +1,48 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Repository; | ||||
| 
 | ||||
| use App\Entity\Fight; | ||||
| use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | ||||
| use Doctrine\Persistence\ManagerRegistry; | ||||
| 
 | ||||
| /** | ||||
|  * @extends ServiceEntityRepository<Fight> | ||||
|  * | ||||
|  * @method Fight|null find($id, $lockMode = null, $lockVersion = null) | ||||
|  * @method Fight|null findOneBy(array $criteria, array $orderBy = null) | ||||
|  * @method Fight[]    findAll() | ||||
|  * @method Fight[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) | ||||
|  */ | ||||
| class FightRepository extends ServiceEntityRepository | ||||
| { | ||||
|     public function __construct(ManagerRegistry $registry) | ||||
|     { | ||||
|         parent::__construct($registry, Fight::class); | ||||
|     } | ||||
| 
 | ||||
| //    /** | ||||
| //     * @return Fight[] Returns an array of Fight objects | ||||
| //     */ | ||||
| //    public function findByExampleField($value): array | ||||
| //    { | ||||
| //        return $this->createQueryBuilder('f') | ||||
| //            ->andWhere('f.exampleField = :val') | ||||
| //            ->setParameter('val', $value) | ||||
| //            ->orderBy('f.id', 'ASC') | ||||
| //            ->setMaxResults(10) | ||||
| //            ->getQuery() | ||||
| //            ->getResult() | ||||
| //        ; | ||||
| //    } | ||||
| 
 | ||||
| //    public function findOneBySomeField($value): ?Fight | ||||
| //    { | ||||
| //        return $this->createQueryBuilder('f') | ||||
| //            ->andWhere('f.exampleField = :val') | ||||
| //            ->setParameter('val', $value) | ||||
| //            ->getQuery() | ||||
| //            ->getOneOrNullResult() | ||||
| //        ; | ||||
| //    } | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| <?php | ||||
| namespace App\Repository; | ||||
| 
 | ||||
| use App\Entity\Technique; | ||||
| use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | ||||
| use Doctrine\Persistence\ManagerRegistry; | ||||
| 
 | ||||
| /** | ||||
|  * | ||||
|  * @extends ServiceEntityRepository<Technique> | ||||
|  * | ||||
|  * @method Technique|null find($id, $lockMode = null, $lockVersion = null) | ||||
|  * @method Technique|null findOneBy(array $criteria, array $orderBy = null) | ||||
|  * @method Technique[] findAll() | ||||
|  * @method Technique[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) | ||||
|  */ | ||||
| class TechniqueRepository extends ServiceEntityRepository | ||||
| { | ||||
| 
 | ||||
|     public function __construct(ManagerRegistry $registry) | ||||
|     { | ||||
|         parent::__construct($registry, Technique::class); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,48 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Repository; | ||||
| 
 | ||||
| use App\Entity\Tournament; | ||||
| use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | ||||
| use Doctrine\Persistence\ManagerRegistry; | ||||
| 
 | ||||
| /** | ||||
|  * @extends ServiceEntityRepository<Tournament> | ||||
|  * | ||||
|  * @method Tournament|null find($id, $lockMode = null, $lockVersion = null) | ||||
|  * @method Tournament|null findOneBy(array $criteria, array $orderBy = null) | ||||
|  * @method Tournament[]    findAll() | ||||
|  * @method Tournament[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) | ||||
|  */ | ||||
| class TournamentRepository extends ServiceEntityRepository | ||||
| { | ||||
|     public function __construct(ManagerRegistry $registry) | ||||
|     { | ||||
|         parent::__construct($registry, Tournament::class); | ||||
|     } | ||||
| 
 | ||||
| //    /** | ||||
| //     * @return Tournament[] Returns an array of Tournament objects | ||||
| //     */ | ||||
| //    public function findByExampleField($value): array | ||||
| //    { | ||||
| //        return $this->createQueryBuilder('t') | ||||
| //            ->andWhere('t.exampleField = :val') | ||||
| //            ->setParameter('val', $value) | ||||
| //            ->orderBy('t.id', 'ASC') | ||||
| //            ->setMaxResults(10) | ||||
| //            ->getQuery() | ||||
| //            ->getResult() | ||||
| //        ; | ||||
| //    } | ||||
| 
 | ||||
| //    public function findOneBySomeField($value): ?Tournament | ||||
| //    { | ||||
| //        return $this->createQueryBuilder('t') | ||||
| //            ->andWhere('t.exampleField = :val') | ||||
| //            ->setParameter('val', $value) | ||||
| //            ->getQuery() | ||||
| //            ->getOneOrNullResult() | ||||
| //        ; | ||||
| //    } | ||||
| } | ||||
| @ -0,0 +1,48 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Repository; | ||||
| 
 | ||||
| use App\Entity\User; | ||||
| use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | ||||
| use Doctrine\Persistence\ManagerRegistry; | ||||
| 
 | ||||
| /** | ||||
|  * @extends ServiceEntityRepository<User> | ||||
|  * | ||||
|  * @method User|null find($id, $lockMode = null, $lockVersion = null) | ||||
|  * @method User|null findOneBy(array $criteria, array $orderBy = null) | ||||
|  * @method User[]    findAll() | ||||
|  * @method User[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) | ||||
|  */ | ||||
| class UserRepository extends ServiceEntityRepository | ||||
| { | ||||
|     public function __construct(ManagerRegistry $registry) | ||||
|     { | ||||
|         parent::__construct($registry, User::class); | ||||
|     } | ||||
| 
 | ||||
| //    /** | ||||
| //     * @return User[] Returns an array of User objects | ||||
| //     */ | ||||
| //    public function findByExampleField($value): array | ||||
| //    { | ||||
| //        return $this->createQueryBuilder('u') | ||||
| //            ->andWhere('u.exampleField = :val') | ||||
| //            ->setParameter('val', $value) | ||||
| //            ->orderBy('u.id', 'ASC') | ||||
| //            ->setMaxResults(10) | ||||
| //            ->getQuery() | ||||
| //            ->getResult() | ||||
| //        ; | ||||
| //    } | ||||
| 
 | ||||
| //    public function findOneBySomeField($value): ?User | ||||
| //    { | ||||
| //        return $this->createQueryBuilder('u') | ||||
| //            ->andWhere('u.exampleField = :val') | ||||
| //            ->setParameter('val', $value) | ||||
| //            ->getQuery() | ||||
| //            ->getOneOrNullResult() | ||||
| //        ; | ||||
| //    } | ||||
| } | ||||
| @ -0,0 +1,59 @@ | ||||
| <?php | ||||
| namespace App\Security; | ||||
| 
 | ||||
| use App\Entity\User; | ||||
| use App\Repository\UserRepository; | ||||
| use Psr\Log\LoggerInterface; | ||||
| use Symfony\Component\Security\Core\Exception\BadCredentialsException; | ||||
| use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface; | ||||
| use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; | ||||
| use DateInterval; | ||||
| use DateTimeImmutable; | ||||
| use DateTimeZone; | ||||
| 
 | ||||
| class AccessTokenHandler implements AccessTokenHandlerInterface | ||||
| { | ||||
| 
 | ||||
|     public function __construct(private UserRepository $userRepository, private LoggerInterface $logger) | ||||
|     {} | ||||
| 
 | ||||
|     public function getUserBadgeFrom(string $accessToken): UserBadge | ||||
|     { | ||||
|         if ($accessToken === FALSE) { | ||||
|             throw new BadCredentialsException('Missing credentials.'); | ||||
|         } | ||||
| 
 | ||||
|         $sign_seed = sodium_base642bin($_ENV['AUTH_SEED'], SODIUM_BASE64_VARIANT_ORIGINAL); | ||||
|         $sign_pair = sodium_crypto_sign_seed_keypair($sign_seed); | ||||
|         $sign_public = sodium_crypto_sign_publickey($sign_pair); | ||||
|         $message_signed = sodium_base642bin($accessToken, SODIUM_BASE64_VARIANT_URLSAFE); | ||||
|         $message = sodium_crypto_sign_open($message_signed, $sign_public); | ||||
| 
 | ||||
|         if ($message === FALSE) { | ||||
|             throw new BadCredentialsException('Invalid credentials.'); | ||||
|         } | ||||
| 
 | ||||
|         $arr = explode('|', $message); | ||||
| 
 | ||||
|         $ts = new DateTimeImmutable($arr[1], new DateTimeZone("UTC")); | ||||
|         $now = new DateTimeImmutable("now", new DateTimeZone("UTC")); | ||||
| 
 | ||||
|         $ts = $ts->add(DateInterval::createFromDateString('5 min')); | ||||
|         if ($ts < $now) { | ||||
|             throw new BadCredentialsException('Token has already expired.'); | ||||
|         } | ||||
| 
 | ||||
|         $auth_name = $arr[0]; | ||||
|         return new UserBadge($auth_name, fn (string $id) => $this->getOrCreateUser($id)); | ||||
|     } | ||||
| 
 | ||||
|     private function getOrCreateUser(string $authName): User | ||||
|     { | ||||
|         $user = $this->userRepository->findOneByAuthName($authName); | ||||
|         if (NULL === $user) { | ||||
|             $user = new User($authName); | ||||
|         } | ||||
|         return $user; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,21 @@ | ||||
| <?php | ||||
| namespace App\Security; | ||||
| 
 | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
| use Symfony\Component\Security\Http\AccessToken\AccessTokenExtractorInterface; | ||||
| 
 | ||||
| class CustomTokenExtractor implements AccessTokenExtractorInterface | ||||
| { | ||||
| 
 | ||||
|     public function __construct() | ||||
|     {} | ||||
| 
 | ||||
|     public function extractAccessToken(Request $request): ?string | ||||
|     { | ||||
|         if ($request->headers->has('X-AUTH-TOKEN')) { | ||||
|             return $request->headers->get('X-AUTH-TOKEN'); | ||||
|         } | ||||
|         return NULL; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,40 @@ | ||||
| <?php | ||||
| namespace App\State; | ||||
| 
 | ||||
| use ApiPlatform\Metadata\DeleteOperationInterface; | ||||
| use ApiPlatform\Metadata\Operation; | ||||
| use ApiPlatform\State\ProcessorInterface; | ||||
| use App\Entity\Dojo; | ||||
| use Psr\Log\LoggerInterface; | ||||
| use Symfony\Bundle\SecurityBundle\Security; | ||||
| use Symfony\Component\DependencyInjection\Attribute\Autowire; | ||||
| 
 | ||||
| /** | ||||
|  * | ||||
|  * @implements ProcessorInterface<Dojo, Dojo|void> | ||||
|  */ | ||||
| class DojoPostProcessor implements ProcessorInterface | ||||
| { | ||||
| 
 | ||||
|     public function __construct( | ||||
|         #[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')] private ProcessorInterface $persistProcessor, | ||||
|         #[Autowire(service: 'api_platform.doctrine.orm.state.remove_processor')] private ProcessorInterface $removeProcessor, | ||||
|         private Security $security, private LoggerInterface $logger) | ||||
|     {} | ||||
| 
 | ||||
|     public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): Dojo | ||||
|     { | ||||
|         if ($operation instanceof DeleteOperationInterface) { | ||||
|             return $this->removeProcessor->process($data, $operation, $uriVariables, $context); | ||||
|         } | ||||
|         $this->updateDojo($data); | ||||
|         $result = $this->persistProcessor->process($data, $operation, $uriVariables, $context); | ||||
| 
 | ||||
|         return $result; | ||||
|     } | ||||
| 
 | ||||
|     private function updateDojo(Dojo $dojo): void | ||||
|     { | ||||
|         $dojo->setOwner($this->security->getUser()); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,41 @@ | ||||
| <?php | ||||
| namespace App\State; | ||||
| 
 | ||||
| use ApiPlatform\Metadata\Operation; | ||||
| use ApiPlatform\State\ProcessorInterface; | ||||
| use App\Entity\TournamentRegistration; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Symfony\Component\DependencyInjection\Attribute\Autowire; | ||||
| use Symfony\Component\HttpFoundation\Exception\BadRequestException; | ||||
| 
 | ||||
| class TournamentRegistrationStateProcessor implements ProcessorInterface | ||||
| { | ||||
| 
 | ||||
|     public function __construct( | ||||
|         #[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')] private ProcessorInterface $persistProcessor, | ||||
|         private EntityManagerInterface $em) | ||||
|     {} | ||||
| 
 | ||||
|     public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): TournamentRegistration | ||||
|     { | ||||
|         $this->updateData($data); | ||||
|         $result = $this->persistProcessor->process($data, $operation, $uriVariables, $context); | ||||
| 
 | ||||
|         return $result; | ||||
|     } | ||||
| 
 | ||||
|     private function updateData(TournamentRegistration $registration): void | ||||
|     { | ||||
|         $tournament = $registration->tournament; | ||||
|         $character = $registration->character; | ||||
| 
 | ||||
|         if ($tournament->getCharacters()->contains($character)) { | ||||
|             throw new BadRequestException("Character is already registered!"); | ||||
|         } | ||||
| 
 | ||||
|         $registration->tournament->addCharacter($registration->character); | ||||
|         $this->em->persist($registration->tournament); | ||||
|         $this->em->persist($registration->character); | ||||
|         $this->em->flush(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| <?php | ||||
| namespace App\Validator; | ||||
| 
 | ||||
| use Symfony\Component\Validator\Constraint; | ||||
| 
 | ||||
| #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)] | ||||
| class CharacterOwned extends Constraint | ||||
| { | ||||
| 
 | ||||
|     public string $invalidOwnerMessage = 'Character is not from the dojo of the current user'; | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,33 @@ | ||||
| <?php | ||||
| namespace App\Validator; | ||||
| 
 | ||||
| use App\Entity\Character; | ||||
| use Symfony\Bundle\SecurityBundle\Security; | ||||
| use Symfony\Component\Validator\Constraint; | ||||
| use Symfony\Component\Validator\ConstraintValidator; | ||||
| use Symfony\Component\Validator\Exception\UnexpectedValueException; | ||||
| 
 | ||||
| class CharacterOwnedValidator extends ConstraintValidator | ||||
| { | ||||
| 
 | ||||
|     public function __construct(private Security $security) | ||||
|     {} | ||||
| 
 | ||||
|     public function validate($character, Constraint $constraint): void | ||||
|     { | ||||
|         if (! $character instanceof Character) { | ||||
|             throw new UnexpectedValueException($character, Character::class); | ||||
|         } | ||||
| 
 | ||||
|         if (! $constraint instanceof CharacterOwned) { | ||||
|             throw new UnexpectedValueException($constraint, CharacterOwned::class); | ||||
|         } | ||||
| 
 | ||||
|         if ($character->getDojo() | ||||
|             ->getOwner() | ||||
|             ->getId() != $this->security->getUser()->getUserIdentifier()) { | ||||
|             $this->context->buildViolation($constraint->invalidOwnerMessage)->addViolation(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,16 @@ | ||||
| <?php | ||||
| namespace App\Validator; | ||||
| 
 | ||||
| use Symfony\Component\Validator\Constraint; | ||||
| 
 | ||||
| class CharacterStats extends Constraint | ||||
| { | ||||
| 
 | ||||
|     public string $noMoreSkillPointsMessage = 'Character spent more skill points than available'; | ||||
| 
 | ||||
|     public function getTargets(): string | ||||
|     { | ||||
|         return self::CLASS_CONSTRAINT; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,32 @@ | ||||
| <?php | ||||
| namespace App\Validator; | ||||
| 
 | ||||
| use App\Entity\Character; | ||||
| use Symfony\Component\Validator\Constraint; | ||||
| use Symfony\Component\Validator\ConstraintValidator; | ||||
| use Symfony\Component\Validator\Exception\UnexpectedValueException; | ||||
| 
 | ||||
| class CharacterStatsValidator extends ConstraintValidator | ||||
| { | ||||
| 
 | ||||
|     public function __construct() | ||||
|     {} | ||||
| 
 | ||||
|     public function validate(mixed $character, Constraint $constraint): void | ||||
|     { | ||||
|         if (! $character instanceof Character) { | ||||
|             throw new UnexpectedValueException($character, Character::class); | ||||
|         } | ||||
| 
 | ||||
|         if (! $constraint instanceof CharacterStats) { | ||||
|             throw new UnexpectedValueException($constraint, CharacterStats::class); | ||||
|         } | ||||
| 
 | ||||
|         if ($character->getFreeSkillPoints() < 0) { | ||||
|             $this->context->buildViolation($constraint->noMoreSkillPointsMessage) | ||||
|                 ->atPath('character.freeSkillPoints') | ||||
|                 ->addViolation(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,12 @@ | ||||
| <?php | ||||
| namespace App\Validator; | ||||
| 
 | ||||
| use Symfony\Component\Validator\Constraint; | ||||
| 
 | ||||
| #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)] | ||||
| class StartDateInFuture extends Constraint | ||||
| { | ||||
| 
 | ||||
|     public string $invalidStartDateMessage = 'Start date has already passed'; | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,27 @@ | ||||
| <?php | ||||
| namespace App\Validator; | ||||
| 
 | ||||
| use Symfony\Component\Validator\Constraint; | ||||
| use Symfony\Component\Validator\ConstraintValidator; | ||||
| use Symfony\Component\Validator\Exception\UnexpectedValueException; | ||||
| use DateTimeImmutable; | ||||
| use DateTimeZone; | ||||
| 
 | ||||
| class StartDateInFutureValidator extends ConstraintValidator | ||||
| { | ||||
| 
 | ||||
|     public function __construct() | ||||
|     {} | ||||
| 
 | ||||
|     public function validate(mixed $entity, Constraint $constraint): void | ||||
|     { | ||||
|         if (! $constraint instanceof StartDateInFuture) { | ||||
|             throw new UnexpectedValueException($constraint, CharacterStats::class); | ||||
|         } | ||||
| 
 | ||||
|         if ($entity->getStartDate() < new DateTimeImmutable("now", new DateTimeZone("UTC"))) { | ||||
|             $this->context->buildViolation($constraint->invalidStartDateMessage)->addViolation(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,3 @@ | ||||
| #!/bin/bash | ||||
| 
 | ||||
| podman run --rm --name ag-dojo-postgres -e POSTGRES_USER=foo -e POSTGRES_PASSWORD=bar -e POSTGRES_HOST_AUTH_METHOD=trust -p "5432:5432" docker.io/library/postgres:14.10 | ||||
| @ -0,0 +1,234 @@ | ||||
| { | ||||
|     "api-platform/core": { | ||||
|         "version": "3.1", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "3.1", | ||||
|             "ref": "adf0c75f4bed8b0043a6680376323404953578c5" | ||||
|         }, | ||||
|         "files": [ | ||||
|             "config/packages/api_platform.yaml", | ||||
|             "config/routes/api_platform.yaml", | ||||
|             "src/ApiResource/.gitignore" | ||||
|         ] | ||||
|     }, | ||||
|     "dama/doctrine-test-bundle": { | ||||
|         "version": "7.3", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes-contrib", | ||||
|             "branch": "main", | ||||
|             "version": "4.0", | ||||
|             "ref": "2c920f73a217f30bd4a37833c91071f4d3dc1ecd" | ||||
|         }, | ||||
|         "files": [ | ||||
|             "config/packages/test/dama_doctrine_test_bundle.yaml" | ||||
|         ] | ||||
|     }, | ||||
|     "doctrine/doctrine-bundle": { | ||||
|         "version": "2.10", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "2.10", | ||||
|             "ref": "e025a6cb69b195970543820b2f18ad21724473fa" | ||||
|         }, | ||||
|         "files": [ | ||||
|             "config/packages/doctrine.yaml", | ||||
|             "src/Entity/.gitignore", | ||||
|             "src/Repository/.gitignore" | ||||
|         ] | ||||
|     }, | ||||
|     "doctrine/doctrine-fixtures-bundle": { | ||||
|         "version": "3.5", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "3.0", | ||||
|             "ref": "1f5514cfa15b947298df4d771e694e578d4c204d" | ||||
|         }, | ||||
|         "files": [ | ||||
|             "src/DataFixtures/AppFixtures.php" | ||||
|         ] | ||||
|     }, | ||||
|     "doctrine/doctrine-migrations-bundle": { | ||||
|         "version": "3.2", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "3.1", | ||||
|             "ref": "1d01ec03c6ecbd67c3375c5478c9a423ae5d6a33" | ||||
|         }, | ||||
|         "files": [ | ||||
|             "config/packages/doctrine_migrations.yaml", | ||||
|             "migrations/.gitignore" | ||||
|         ] | ||||
|     }, | ||||
|     "nelmio/cors-bundle": { | ||||
|         "version": "2.3", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "1.5", | ||||
|             "ref": "6bea22e6c564fba3a1391615cada1437d0bde39c" | ||||
|         }, | ||||
|         "files": [ | ||||
|             "config/packages/nelmio_cors.yaml" | ||||
|         ] | ||||
|     }, | ||||
|     "phpunit/phpunit": { | ||||
|         "version": "9.6", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "9.6", | ||||
|             "ref": "7364a21d87e658eb363c5020c072ecfdc12e2326" | ||||
|         }, | ||||
|         "files": [ | ||||
|             ".env.test", | ||||
|             "phpunit.xml.dist", | ||||
|             "tests/bootstrap.php" | ||||
|         ] | ||||
|     }, | ||||
|     "symfony/console": { | ||||
|         "version": "6.2", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "5.3", | ||||
|             "ref": "da0c8be8157600ad34f10ff0c9cc91232522e047" | ||||
|         }, | ||||
|         "files": [ | ||||
|             "bin/console" | ||||
|         ] | ||||
|     }, | ||||
|     "symfony/flex": { | ||||
|         "version": "2.2", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "1.0", | ||||
|             "ref": "146251ae39e06a95be0fe3d13c807bcf3938b172" | ||||
|         }, | ||||
|         "files": [ | ||||
|             ".env" | ||||
|         ] | ||||
|     }, | ||||
|     "symfony/framework-bundle": { | ||||
|         "version": "6.2", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "6.2", | ||||
|             "ref": "af47254c5e4cd543e6af3e4508298ffebbdaddd3" | ||||
|         }, | ||||
|         "files": [ | ||||
|             "config/packages/cache.yaml", | ||||
|             "config/packages/framework.yaml", | ||||
|             "config/preload.php", | ||||
|             "config/routes/framework.yaml", | ||||
|             "config/services.yaml", | ||||
|             "public/index.php", | ||||
|             "src/Controller/.gitignore", | ||||
|             "src/Kernel.php" | ||||
|         ] | ||||
|     }, | ||||
|     "symfony/maker-bundle": { | ||||
|         "version": "1.50", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "1.0", | ||||
|             "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f" | ||||
|         } | ||||
|     }, | ||||
|     "symfony/phpunit-bridge": { | ||||
|         "version": "7.0", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "6.3", | ||||
|             "ref": "1f5830c331065b6e4c9d5fa2105e322d29fcd573" | ||||
|         }, | ||||
|         "files": [ | ||||
|             ".env.test", | ||||
|             "bin/phpunit", | ||||
|             "phpunit.xml.dist", | ||||
|             "tests/bootstrap.php" | ||||
|         ] | ||||
|     }, | ||||
|     "symfony/routing": { | ||||
|         "version": "6.2", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "6.2", | ||||
|             "ref": "e0a11b4ccb8c9e70b574ff5ad3dfdcd41dec5aa6" | ||||
|         }, | ||||
|         "files": [ | ||||
|             "config/packages/routing.yaml", | ||||
|             "config/routes.yaml" | ||||
|         ] | ||||
|     }, | ||||
|     "symfony/security-bundle": { | ||||
|         "version": "6.2", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "6.0", | ||||
|             "ref": "8a5b112826f7d3d5b07027f93786ae11a1c7de48" | ||||
|         }, | ||||
|         "files": [ | ||||
|             "config/packages/security.yaml" | ||||
|         ] | ||||
|     }, | ||||
|     "symfony/twig-bundle": { | ||||
|         "version": "6.2", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "5.4", | ||||
|             "ref": "bb2178c57eee79e6be0b297aa96fc0c0def81387" | ||||
|         }, | ||||
|         "files": [ | ||||
|             "config/packages/twig.yaml", | ||||
|             "templates/base.html.twig" | ||||
|         ] | ||||
|     }, | ||||
|     "symfony/uid": { | ||||
|         "version": "6.2", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "6.2", | ||||
|             "ref": "d294ad4add3e15d7eb1bae0221588ca89b38e558" | ||||
|         }, | ||||
|         "files": [ | ||||
|             "config/packages/uid.yaml" | ||||
|         ] | ||||
|     }, | ||||
|     "symfony/validator": { | ||||
|         "version": "6.2", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "5.3", | ||||
|             "ref": "c32cfd98f714894c4f128bb99aa2530c1227603c" | ||||
|         }, | ||||
|         "files": [ | ||||
|             "config/packages/validator.yaml" | ||||
|         ] | ||||
|     }, | ||||
|     "zenstruck/foundry": { | ||||
|         "version": "1.36", | ||||
|         "recipe": { | ||||
|             "repo": "github.com/symfony/recipes", | ||||
|             "branch": "main", | ||||
|             "version": "1.10", | ||||
|             "ref": "37c2f894cc098ab4c08874b80cccc8e2f8de7976" | ||||
|         }, | ||||
|         "files": [ | ||||
|             "config/packages/zenstruck_foundry.yaml" | ||||
|         ] | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,19 @@ | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
|     <head> | ||||
|         <meta charset="UTF-8"> | ||||
|         <title>{% block title %}Welcome!{% endblock %}</title> | ||||
|         <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>"> | ||||
|         {# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #} | ||||
|         {% block stylesheets %} | ||||
|             {{ encore_entry_link_tags('app') }} | ||||
|         {% endblock %} | ||||
| 
 | ||||
|         {% block javascripts %} | ||||
|             {{ encore_entry_script_tags('app') }} | ||||
|         {% endblock %} | ||||
|     </head> | ||||
|     <body> | ||||
|         {% block body %}{% endblock %} | ||||
|     </body> | ||||
| </html> | ||||
| @ -0,0 +1,58 @@ | ||||
| <?php | ||||
| namespace App\Tests; | ||||
| 
 | ||||
| use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; | ||||
| use ApiPlatform\Symfony\Bundle\Test\Client; | ||||
| use App\Entity\Thing; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Zenstruck\Foundry\Proxy; | ||||
| use Zenstruck\Foundry\Test\Factories; | ||||
| use Zenstruck\Foundry\Test\ResetDatabase; | ||||
| use DateTimeZone; | ||||
| 
 | ||||
| abstract class AbstractTest extends ApiTestCase | ||||
| { | ||||
|     // This trait provided by Foundry will take care of refreshing the database content to a known state before each test | ||||
|     use ResetDatabase, Factories; | ||||
| 
 | ||||
|     public function setUp(): void | ||||
|     { | ||||
|         self::bootKernel(); | ||||
|     } | ||||
| 
 | ||||
|     private static function generateAuthToken(string $authName) | ||||
|     { | ||||
|         $sign_seed = sodium_base642bin($_ENV['AUTH_SEED'], SODIUM_BASE64_VARIANT_ORIGINAL); | ||||
|         $sign_pair = sodium_crypto_sign_seed_keypair($sign_seed); | ||||
|         $sign_secret = sodium_crypto_sign_secretkey($sign_pair); | ||||
|         $now = new \DateTimeImmutable("now", new DateTimeZone("UTC")); | ||||
|         $message = $authName . "|" . $now->format("c"); | ||||
| 
 | ||||
|         return sodium_bin2base64(sodium_crypto_sign($message, $sign_secret), SODIUM_BASE64_VARIANT_URLSAFE); | ||||
|     } | ||||
| 
 | ||||
|     protected static function createClientWithToken($authName = "FooMan"): Client | ||||
|     { | ||||
|         return static::createClient([], | ||||
|             [ | ||||
|                 'headers' => [ | ||||
|                     'accept' => 'application/json', | ||||
|                     'X-AUTH-TOKEN' => static::generateAuthToken($authName) | ||||
|                 ] | ||||
|             ]); | ||||
|     } | ||||
| 
 | ||||
|     protected function getIri(Thing|Proxy $thing) | ||||
|     { | ||||
|         if ($thing instanceof Proxy) { | ||||
|             return $this->getIriFromResource($thing->object()); | ||||
|         } | ||||
|         return $this->getIriFromResource($thing); | ||||
|     } | ||||
| 
 | ||||
|     protected function getEntityManager(): EntityManagerInterface | ||||
|     { | ||||
|         return static::$kernel->getContainer()->get('doctrine.orm.entity_manager'); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,190 @@ | ||||
| <?php | ||||
| namespace App\Tests; | ||||
| 
 | ||||
| use App\Factory\CharacterFactory; | ||||
| use App\Factory\DojoFactory; | ||||
| use App\Factory\TechniqueFactory; | ||||
| use App\Factory\UserFactory; | ||||
| 
 | ||||
| class CharacterTest extends AbstractTest | ||||
| { | ||||
| 
 | ||||
|     /** | ||||
|      * Requirement: A user should be able see all characters from a dojo, but only the public fields! | ||||
|      */ | ||||
|     public function testRetrieveCharactersFromDojoPublic(): void | ||||
|     { | ||||
|         $requestUser = UserFactory::createOne(); | ||||
|         $dojo = DojoFactory::createOne(); | ||||
|         CharacterFactory::createMany(4, [ | ||||
|             'dojo' => $dojo | ||||
|         ]); | ||||
|         CharacterFactory::createMany(10); | ||||
| 
 | ||||
|         $response = static::createClientWithToken($requestUser->authName)->request('GET', | ||||
|             $this->getIri($dojo) . '/characters'); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(200); | ||||
| 
 | ||||
|         $this->assertNotEquals("[[],[],[],[]]", $response->getContent()); | ||||
| 
 | ||||
|         // Because test fixtures are automatically loaded between each test, you can assert on them | ||||
|         $this->assertCount(4, $response->toArray()); | ||||
| 
 | ||||
|         $chars = $response->toArray(); | ||||
|         $this->assertEquals(4, count($chars[0])); | ||||
|         $this->assertArrayHasKey('id', $chars[0]); | ||||
|         $this->assertArrayHasKey('name', $chars[0]); | ||||
|         $this->assertEquals('/api/dojos/' . $dojo->getId() | ||||
|             ->toBase32(), $chars[0]['dojo']); | ||||
|         $this->assertArrayHasKey('age', $chars[0]); | ||||
|         $this->assertArrayNotHasKey('freeSkillPoints', $chars[0]); | ||||
|         $this->assertArrayNotHasKey('strength', $chars[0]); // not accessible via this route | ||||
|         $this->assertArrayNotHasKey('constitution', $chars[0]); // not accessible via this route | ||||
|         $this->assertArrayNotHasKey('agility', $chars[0]); // not accessible via this route | ||||
|         $this->assertArrayNotHasKey('chi', $chars[0]); // not accessible via this route | ||||
|         $this->assertArrayNotHasKey('techniques', $chars[0]); // not accessible via this route | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Requirement: A user should be able see all of the characters from his dojo, not restricted by public fields! | ||||
|      */ | ||||
|     public function testRetrieveCharactersFromOwnDojoDetail(): void | ||||
|     { | ||||
|         $dojo = DojoFactory::createOne([ | ||||
|             'owner' => UserFactory::createOne() | ||||
|         ]); | ||||
| 
 | ||||
|         $technique = TechniqueFactory::createOne(); | ||||
| 
 | ||||
|         $foo = CharacterFactory::createMany(4, [ | ||||
|             'dojo' => $dojo, | ||||
|             'techniques' => [ | ||||
|                 $technique | ||||
|             ] | ||||
|         ]); | ||||
| 
 | ||||
|         $this->assertEquals(1, count($foo[0]->getTechniques())); | ||||
| 
 | ||||
|         CharacterFactory::createMany(10); | ||||
| 
 | ||||
|         $response = static::createClientWithToken($dojo->getOwner()->authName)->request('GET', '/api/dojo/characters'); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(200); | ||||
| 
 | ||||
|         $this->assertNotEquals("[[],[],[],[]]", $response->getContent()); | ||||
| 
 | ||||
|         $chars = $response->toArray(); | ||||
|         $this->assertEquals(10, count($chars[0])); | ||||
|         $this->assertArrayHasKey('id', $chars[0]); | ||||
|         $this->assertArrayHasKey('name', $chars[0]); | ||||
|         $this->assertArrayHasKey('dojo', $chars[0]); | ||||
|         $this->assertEquals('/api/dojos/' . $dojo->getId() | ||||
|             ->toBase32(), $chars[0]['dojo']); | ||||
|         $this->assertArrayHasKey('age', $chars[0]); | ||||
|         $this->assertArrayHasKey('freeSkillPoints', $chars[0]); | ||||
|         $this->assertArrayHasKey('strength', $chars[0]); | ||||
|         $this->assertArrayHasKey('constitution', $chars[0]); | ||||
|         $this->assertArrayHasKey('agility', $chars[0]); | ||||
|         $this->assertArrayHasKey('chi', $chars[0]); | ||||
|         $this->assertArrayHasKey('techniques', $chars[0]); | ||||
|         $this->assertEquals('/api/techniques/' . $technique->getId() | ||||
|             ->toBase32(), $chars[0]['techniques'][0]); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Requirement: MVP only (in the future the recuitment will be different). | ||||
|      * A user should be able to create a single character. | ||||
|      */ | ||||
|     public function testCreateCharacter(): void | ||||
|     { | ||||
|         $dojo = DojoFactory::createOne([ | ||||
|             'owner' => UserFactory::createOne() | ||||
|         ]); | ||||
| 
 | ||||
|         $tech = TechniqueFactory::createOne([ | ||||
|             'prerequisite' => NULL | ||||
|         ]); | ||||
| 
 | ||||
|         $response = static::createClientWithToken($dojo->getOwner()->authName)->request('POST', '/api/characters', | ||||
|             [ | ||||
|                 'json' => [ | ||||
|                     'name' => 'Dude', | ||||
|                     'strength' => 1, | ||||
|                     'constitution' => 1, | ||||
|                     'agility' => 1, | ||||
|                     'chi' => 1, | ||||
|                     'techniques' => [ | ||||
|                         $this->getIri($tech) | ||||
|                     ] | ||||
|                 ] | ||||
|             ]); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(201); | ||||
| 
 | ||||
|         $this->assertArrayHasKey('id', $response->toArray()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Requirement: MVP only (in the future the recuitment will be different). | ||||
|      * A user should NOT be able spent more skill points than available. | ||||
|      */ | ||||
|     public function testCreateCharacterStatsTooHigh(): void | ||||
|     { | ||||
|         $dojo = DojoFactory::createOne([ | ||||
|             'owner' => UserFactory::createOne() | ||||
|         ]); | ||||
| 
 | ||||
|         $tech = TechniqueFactory::createOne([ | ||||
|             'prerequisite' => NULL | ||||
|         ]); | ||||
| 
 | ||||
|         static::createClientWithToken($dojo->getOwner()->authName)->request('POST', '/api/characters', | ||||
|             [ | ||||
|                 'json' => [ | ||||
|                     'name' => 'Dude', | ||||
|                     'strength' => 99, | ||||
|                     'constitution' => 1, | ||||
|                     'agility' => 1, | ||||
|                     'chi' => 1, | ||||
|                     'techniques' => [ | ||||
|                         $this->getIri($tech) | ||||
|                     ] | ||||
|                 ] | ||||
|             ]); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(422); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Requirement: MVP only (in the future the recuitment will be different). | ||||
|      * A user should NOT be able spent more skill points than available. | ||||
|      */ | ||||
|     public function testCreateCharacterTooExpensiveTechnique(): void | ||||
|     { | ||||
|         $dojo = DojoFactory::createOne([ | ||||
|             'owner' => UserFactory::createOne() | ||||
|         ]); | ||||
| 
 | ||||
|         $tech = TechniqueFactory::createOne([ | ||||
|             'costs' => 99 | ||||
|         ]); | ||||
| 
 | ||||
|         static::createClientWithToken($dojo->getOwner()->authName)->request('POST', '/api/characters', | ||||
|             [ | ||||
|                 'json' => [ | ||||
|                     'name' => 'Dude', | ||||
|                     'strength' => 1, | ||||
|                     'constitution' => 1, | ||||
|                     'agility' => 1, | ||||
|                     'chi' => 1, | ||||
|                     'techniques' => [ | ||||
|                         $this->getIri($tech) | ||||
|                     ] | ||||
|                 ] | ||||
|             ]); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(422); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,131 @@ | ||||
| <?php | ||||
| namespace App\Tests; | ||||
| 
 | ||||
| use App\Factory\DojoFactory; | ||||
| use App\Factory\UserFactory; | ||||
| use App\Repository\UserRepository; | ||||
| 
 | ||||
| class DojoTest extends AbstractTest | ||||
| { | ||||
| 
 | ||||
|     /** | ||||
|      * Requirement: A user should be able to create a dojo! | ||||
|      */ | ||||
|     public function testCreateDojo(): void | ||||
|     { | ||||
|         $userName = "FooBarFigher"; | ||||
|         $dojoName = "BigFightDojo"; | ||||
|         $userRepository = $this->getContainer()->get(UserRepository::class); | ||||
| 
 | ||||
|         $this->assertCount(0, $userRepository->findByAuthName($userName)); | ||||
| 
 | ||||
|         static::createClientWithToken($userName)->request('POST', '/api/dojos', [ | ||||
|             'json' => [ | ||||
|                 'name' => $dojoName | ||||
|             ] | ||||
|         ]); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(201); | ||||
| 
 | ||||
|         $this->assertCount(1, $userRepository->findByAuthName($userName)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Requirement: A user should be request his own dojo! | ||||
|      */ | ||||
|     public function testUserReadDojo(): void | ||||
|     { | ||||
|         $userName = "FooBarFigher"; | ||||
|         $dojoName = "BigFightDojo"; | ||||
|         $dojo = DojoFactory::createOne( | ||||
|             [ | ||||
|                 'name' => $dojoName, | ||||
|                 'owner' => UserFactory::createOne([ | ||||
|                     'authName' => $userName | ||||
|                 ]) | ||||
|             ]); | ||||
| 
 | ||||
|         static::createClientWithToken($userName)->request('GET', '/api/dojo'); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(200); | ||||
| 
 | ||||
|         $this->assertJsonContains( | ||||
|             [ | ||||
|                 'name' => 'BigFightDojo', | ||||
|                 'members' => [], | ||||
|                 'owner' => $this->getIri($dojo->getOwner()), | ||||
|                 'id' => $dojo->getId() | ||||
|                     ->toBase32() | ||||
|             ]); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Requirement: A user should be request his own dojo! | ||||
|      * Fails, if the dojo is not yet created. | ||||
|      */ | ||||
|     public function testUserReadDojoNotYetCreated(): void | ||||
|     { | ||||
|         $userName = "FooBarFigher"; | ||||
|         $userRepository = $this->getContainer()->get(UserRepository::class); | ||||
| 
 | ||||
|         $this->assertCount(0, $userRepository->findByAuthName($userName)); | ||||
| 
 | ||||
|         static::createClientWithToken($userName)->request('GET', '/api/dojo'); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(404); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Requirement: A user should NOT be able to create more than one dojos! | ||||
|      */ | ||||
|     public function testUserCannotCreateMultipleDojos(): void | ||||
|     { | ||||
|         $userName = "FooBarFigher"; | ||||
|         $dojoName = "BigFightDojo"; | ||||
|         DojoFactory::createOne([ | ||||
|             'name' => $dojoName, | ||||
|             'owner' => UserFactory::createOne([ | ||||
|                 'authName' => $userName | ||||
|             ]) | ||||
|         ]); | ||||
| 
 | ||||
|         static::createClientWithToken($userName)->request('POST', '/api/dojos', [ | ||||
|             'json' => [ | ||||
|                 'name' => $dojoName | ||||
|             ] | ||||
|         ]); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(409); // 409 Conflict | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Requirement: A user should be able to change the dojos name! | ||||
|      * FIXME: Add limitation so users will not do this frequently. | ||||
|      */ | ||||
|     public function testChangeDojoName(): void | ||||
|     { | ||||
|         $userName = "FooBarFigher"; | ||||
|         $dojoName = "BigFightDojo"; | ||||
|         $newDojoName = "BigFightDojo"; | ||||
|         $dojo = DojoFactory::createOne( | ||||
|             [ | ||||
|                 'name' => $dojoName, | ||||
|                 'owner' => UserFactory::createOne([ | ||||
|                     'authName' => $userName | ||||
|                 ]) | ||||
|             ]); | ||||
| 
 | ||||
|         static::createClientWithToken($userName)->request('PATCH', '/api/dojos/' . $dojo->id, | ||||
|             [ | ||||
|                 'headers' => [ | ||||
|                     'content-type' => 'application/merge-patch+json' | ||||
|                 ], | ||||
|                 'json' => [ | ||||
|                     'name' => $newDojoName | ||||
|                 ] | ||||
|             ]); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(200); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,134 @@ | ||||
| <?php | ||||
| namespace App\Tests; | ||||
| 
 | ||||
| use App\Factory\TechniqueFactory; | ||||
| 
 | ||||
| class TechniqueTest extends AbstractTest | ||||
| { | ||||
| 
 | ||||
|     public function testListTechniques(): void | ||||
|     { | ||||
|         TechniqueFactory::createMany(10, [ | ||||
|             'prerequisite' => NULL | ||||
|         ]); | ||||
| 
 | ||||
|         $response = static::createClientWithToken()->request('GET', '/api/techniques'); | ||||
|         $this->assertEquals(10, count($response->toArray())); | ||||
|     } | ||||
| 
 | ||||
|     public function testListTechniquesWithPrerequisite(): void | ||||
|     { | ||||
|         $prerequisite = TechniqueFactory::createOne([ | ||||
|             'prerequisite' => NULL | ||||
|         ]); | ||||
| 
 | ||||
|         TechniqueFactory::createMany(2, [ | ||||
|             'prerequisite' => $prerequisite | ||||
|         ]); | ||||
| 
 | ||||
|         $response = static::createClientWithToken()->request('GET', '/api/techniques'); | ||||
|         $this->assertEquals(3, count($response->toArray())); | ||||
|     } | ||||
| 
 | ||||
|     public function testShowTechnique(): void | ||||
|     { | ||||
|         $technique = TechniqueFactory::createOne([ | ||||
|             'prerequisite' => NULL | ||||
|         ]); | ||||
| 
 | ||||
|         $response = static::createClientWithToken()->request('GET', | ||||
|             '/api/techniques/' . $technique->getId() | ||||
|                 ->toBase32()); | ||||
| 
 | ||||
|         $this->assertJsonEquals( | ||||
|             [ | ||||
|                 'id' => $technique->getId() | ||||
|                     ->toBase32(), | ||||
|                 'name' => $technique->getName(), | ||||
|                 'costs' => $technique->getCosts(), | ||||
|                 'damage' => $technique->getDamage(), | ||||
|                 'energy' => $technique->getEnergy(), | ||||
|                 'accuracy' => $technique->getAccuracy() | ||||
|             ], $response->getContent()); | ||||
|     } | ||||
| 
 | ||||
|     public function testShowTechniqueWithPrerequisite(): void | ||||
|     { | ||||
|         $prerequisite = TechniqueFactory::createOne([ | ||||
|             'prerequisite' => NULL | ||||
|         ]); | ||||
| 
 | ||||
|         $technique = TechniqueFactory::createOne([ | ||||
|             'prerequisite' => $prerequisite | ||||
|         ]); | ||||
| 
 | ||||
|         $response = static::createClientWithToken()->request('GET', | ||||
|             '/api/techniques/' . $technique->getId() | ||||
|                 ->toBase32()); | ||||
| 
 | ||||
|         $this->assertJsonEquals( | ||||
|             [ | ||||
|                 'id' => $technique->getId() | ||||
|                     ->toBase32(), | ||||
|                 'name' => $technique->getName(), | ||||
|                 'costs' => $technique->getCosts(), | ||||
|                 'damage' => $technique->getDamage(), | ||||
|                 'energy' => $technique->getEnergy(), | ||||
|                 'accuracy' => $technique->getAccuracy(), | ||||
|                 'prerequisite' => '/api/techniques/' . $prerequisite->getId() | ||||
|                     ->toBase32() | ||||
|             ], $response->getContent()); | ||||
|     } | ||||
| 
 | ||||
|     public function testCreateTechniqueAsAdmin(): void | ||||
|     { | ||||
|         $response = static::createClientWithToken("dehecht")->request('POST', '/api/techniques', | ||||
|             [ | ||||
|                 'json' => [ | ||||
|                     'name' => 'Drei-Schwert-Style', | ||||
|                     'costs' => 2, | ||||
|                     'damage' => '3 * strength', | ||||
|                     'energy' => '1.5 * constitution + 2 * strength', | ||||
|                     'accuracy' => '2 * agility' | ||||
|                 ] | ||||
|             ]); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(201); | ||||
| 
 | ||||
|         $this->assertArrayHasKey('id', $response->toArray()); | ||||
|     } | ||||
| 
 | ||||
|     public function testCreateTechniqueFailsAsUser(): void | ||||
|     { | ||||
|         static::createClientWithToken()->request('POST', '/api/techniques', | ||||
|             [ | ||||
|                 'json' => [ | ||||
|                     'name' => 'Drei-Schwert-Style', | ||||
|                     'costs' => 2, | ||||
|                     'damage' => '3 * strength', | ||||
|                     'energy' => '1.5 * constitution + 2 * strength', | ||||
|                     'accuracy' => '2 * agility' | ||||
|                 ] | ||||
|             ]); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(403); | ||||
|     } | ||||
| 
 | ||||
|     public function testFailToCreateTechniqueWithNonExistentPrerequisite(): void | ||||
|     { | ||||
|         $response = static::createClientWithToken("dehecht")->request('POST', '/api/techniques', | ||||
|             [ | ||||
|                 'json' => [ | ||||
|                     'name' => 'Drei-Schwert-Style', | ||||
|                     'costs' => 2, | ||||
|                     'damage' => '3 * strength', | ||||
|                     'energy' => '1.5 * constitution + 2 * strength', | ||||
|                     'accuracy' => '2 * agility', | ||||
|                     'prerequisite' => '/api/techniques/01ARZ3NDEKTSV4RRFFQ69G5FAV' | ||||
|                 ] | ||||
|             ]); | ||||
| 
 | ||||
|         $this->assertTrue($response->getStatusCode() / 100 != 2); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,197 @@ | ||||
| <?php | ||||
| namespace App\Tests; | ||||
| 
 | ||||
| use App\Entity\Character; | ||||
| use App\Entity\Tournament; | ||||
| use App\Entity\User; | ||||
| use App\Factory\CharacterFactory; | ||||
| use App\Factory\DojoFactory; | ||||
| use App\Factory\FightFactory; | ||||
| use App\Factory\TournamentFactory; | ||||
| use App\Factory\UserFactory; | ||||
| use Zenstruck\Foundry\Proxy; | ||||
| use DateTimeImmutable; | ||||
| use DateTimeZone; | ||||
| 
 | ||||
| // @note No need to create tournaments via API Endpoint ... will be done in cronjob | ||||
| class TournamentTest extends AbstractTest | ||||
| { | ||||
| 
 | ||||
|     private function createTournament(string $offset, array $characters = array()): Tournament|Proxy | ||||
|     { | ||||
|         $tournamentStartDate = new DateTimeImmutable("now", new DateTimeZone("UTC")); | ||||
|         $tournamentStartDate = $tournamentStartDate->sub(\DateInterval::createFromDateString($offset)); | ||||
| 
 | ||||
|         return TournamentFactory::createOne([ | ||||
|             'startDate' => $tournamentStartDate, | ||||
|             'characters' => $characters | ||||
|         ]); | ||||
|     } | ||||
| 
 | ||||
|     private function createCharacter(User|Proxy $user): Character|Proxy | ||||
|     { | ||||
|         $dojo = DojoFactory::createOne([ | ||||
|             'owner' => $user | ||||
|         ]); | ||||
| 
 | ||||
|         $character = CharacterFactory::createOne([ | ||||
|             'dojo' => $dojo | ||||
|         ]); | ||||
| 
 | ||||
|         return $character; | ||||
|     } | ||||
| 
 | ||||
|     public function testRegisterCharacter(): void | ||||
|     { | ||||
|         $tournament = $this->createTournament("-5 min"); | ||||
|         $user = UserFactory::createOne(); | ||||
|         $character = $this->createCharacter($user); | ||||
| 
 | ||||
|         static::createClientWithToken($user->authName)->request('POST', '/api/tournament_registrations', | ||||
|             [ | ||||
|                 'json' => [ | ||||
|                     'tournament' => $this->getIri($tournament), | ||||
|                     'character' => $this->getIri($character) | ||||
|                 ] | ||||
|             ]); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(201); | ||||
|     } | ||||
| 
 | ||||
|     public function testRegisterCharacterDifferentUser(): void | ||||
|     { | ||||
|         $tournament = $this->createTournament("-5 min"); | ||||
|         $characterOwner = UserFactory::createOne(); | ||||
|         $character = $this->createCharacter($characterOwner); | ||||
|         $user = UserFactory::createOne(); | ||||
| 
 | ||||
|         static::createClientWithToken($user->authName)->request('POST', '/api/tournament_registrations', | ||||
|             [ | ||||
|                 'json' => [ | ||||
|                     'tournament' => $this->getIri($tournament), | ||||
|                     'character' => $this->getIri($character) | ||||
|                 ] | ||||
|             ]); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(422); | ||||
|     } | ||||
| 
 | ||||
|     public function testRegisterCharacterOnPastTournament(): void | ||||
|     { | ||||
|         $tournament = $this->createTournament("5 min"); | ||||
|         $user = UserFactory::createOne(); | ||||
|         $character = $this->createCharacter($user); | ||||
| 
 | ||||
|         static::createClientWithToken($user->authName)->request('POST', '/api/tournament_registrations', | ||||
|             [ | ||||
|                 'json' => [ | ||||
|                     'tournament' => $this->getIri($tournament), | ||||
|                     'character' => $this->getIri($character) | ||||
|                 ] | ||||
|             ]); | ||||
| 
 | ||||
|         $this->assertResponseStatusCodeSame(422); | ||||
|     } | ||||
| 
 | ||||
|     public function testRegisterCharacterNotPossibleTwice(): void | ||||
|     { | ||||
|         $tournament = $this->createTournament("-5 min"); | ||||
|         $user = UserFactory::createOne(); | ||||
|         $character = $this->createCharacter($user); | ||||
| 
 | ||||
|         static::createClientWithToken($user->authName)->request('POST', '/api/tournament_registrations', | ||||
|             [ | ||||
|                 'json' => [ | ||||
|                     'tournament' => $this->getIri($tournament), | ||||
|                     'character' => $this->getIri($character) | ||||
|                 ] | ||||
|             ]); | ||||
| 
 | ||||
|         static::createClientWithToken($user->authName)->request('POST', '/api/tournament_registrations', | ||||
|             [ | ||||
|                 'json' => [ | ||||
|                     'tournament' => $this->getIri($tournament), | ||||
|                     'character' => $this->getIri($character) | ||||
|                 ] | ||||
|             ]); | ||||
| 
 | ||||
|         $this->assertEquals(1, | ||||
|             $this->getEntityManager() | ||||
|                 ->find(Tournament::class, $tournament->getId()) | ||||
|                 ->getCharacters() | ||||
|                 ->count()); | ||||
|         $this->assertResponseStatusCodeSame(400); | ||||
|     } | ||||
| 
 | ||||
|     public function testShowRegisteredCharacters(): void | ||||
|     { | ||||
|         $tournament = $this->createTournament("-5 min", CharacterFactory::createMany(4)); | ||||
| 
 | ||||
|         $response = static::createClientWithToken()->request('GET', $this->getIri($tournament)); | ||||
|         $this->assertResponseStatusCodeSame(200); | ||||
|         $this->assertCount(4, $response->toArray()['characters']); | ||||
|     } | ||||
| 
 | ||||
|     public function testListTournaments(): void | ||||
|     { | ||||
|         TournamentFactory::createMany(5); | ||||
|         $response = static::createClientWithToken()->request('GET', '/api/tournaments'); | ||||
|         $this->assertResponseStatusCodeSame(200); | ||||
|         $this->assertCount(5, $response->toArray()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Status is ... | ||||
|      * meta data like when it is starting, name, "location", Winner (nullable), etc. | ||||
|      */ | ||||
|     public function testTournamentStatus(): void | ||||
|     { | ||||
|         $tournament = TournamentFactory::createOne([ | ||||
|             'winner' => CharacterFactory::createOne() | ||||
|         ]); | ||||
|         $response = static::createClientWithToken()->request('GET', $this->getIri($tournament)); | ||||
|         $this->assertResponseStatusCodeSame(200); | ||||
| 
 | ||||
|         $arrayResponse = $response->toArray(); | ||||
|         $this->assertArrayHasKey('winner', $arrayResponse); | ||||
|     } | ||||
| 
 | ||||
|     public function testTournamentFights(): void | ||||
|     { | ||||
|         $tournament = TournamentFactory::createOne([ | ||||
|             'fights' => FightFactory::createMany(16) | ||||
|         ]); | ||||
| 
 | ||||
|         $response = static::createClientWithToken()->request('GET', $this->getIri($tournament) . '/fights'); | ||||
|         $this->assertResponseStatusCodeSame(200); | ||||
|         $this->assertCount(16, $response->toArray()); | ||||
|     } | ||||
| 
 | ||||
|     public function testTournamentFightsForCharacter(): void | ||||
|     { | ||||
|         $characters = CharacterFactory::createMany(16); | ||||
|         $tournament = TournamentFactory::createOne([ | ||||
|             'characters' => $characters | ||||
|         ]); | ||||
| 
 | ||||
|         $winner = $characters[0]; | ||||
|         for ($i = 1; $i < count($characters); ++ $i) { | ||||
|             $tournament->addFight( | ||||
|                 FightFactory::createOne([ | ||||
|                     'winner' => $winner, | ||||
|                     'characters' => array( | ||||
|                         $winner, | ||||
|                         $characters[$i] | ||||
|                     ) | ||||
|                 ])->object()); | ||||
|         } | ||||
|         $tournament->save(); | ||||
| 
 | ||||
|         $response = static::createClientWithToken()->request('GET', | ||||
|             $this->getIri($tournament) . '/characters/' . $winner->getId() | ||||
|                 ->toBase32() . '/fights'); | ||||
| 
 | ||||
|         $this->assertCount(15, $response->toArray()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,15 @@ | ||||
| <?php | ||||
| 
 | ||||
| use Symfony\Component\Dotenv\Dotenv; | ||||
| 
 | ||||
| require dirname(__DIR__).'/vendor/autoload.php'; | ||||
| 
 | ||||
| if (file_exists(dirname(__DIR__).'/config/bootstrap.php')) { | ||||
|     require dirname(__DIR__).'/config/bootstrap.php'; | ||||
| } elseif (method_exists(Dotenv::class, 'bootEnv')) { | ||||
|     (new Dotenv())->bootEnv(dirname(__DIR__).'/.env'); | ||||
| } | ||||
| 
 | ||||
| if ($_SERVER['APP_DEBUG']) { | ||||
|     umask(0000); | ||||
| } | ||||
					Loading…
					
					
				
		Reference in New Issue