mirror of
https://github.com/MarijnDoeve/TijdVoorDeTest.git
synced 2026-03-05 20:44:19 +01:00
WIP 3-3
This commit is contained in:
2
.env.dev
2
.env.dev
@@ -1,4 +1,4 @@
|
||||
|
||||
#APP_DEBUG=1
|
||||
###> symfony/framework-bundle ###
|
||||
APP_SECRET=e26b9552d9e7f969b160373effaa7690
|
||||
###< symfony/framework-bundle ###
|
||||
|
||||
38
.idea/TijdVoorDeTest.iml
generated
38
.idea/TijdVoorDeTest.iml
generated
@@ -97,8 +97,46 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/var-exporter" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/yaml" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/theseer/tokenizer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpstan/extension-installer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpstan/phpstan-doctrine" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpstan/phpstan-phpunit" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpstan/phpstan-symfony" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/clock" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/asset" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/clock" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/form" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/password-hasher" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-icu" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-uuid" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/property-access" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/property-info" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/security-bundle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/security-core" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/security-csrf" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/security-http" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/translation-contracts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/twig-bridge" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/twig-bundle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/type-info" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/uid" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/web-profiler-bundle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/thecodingmachine/phpstan-safe-rule" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/thecodingmachine/safe" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/twig/twig" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/easycorp/easyadmin-bundle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/intl" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mime" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-idn" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/translation" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/ux-twig-component" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/validator" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/twig/extra-bundle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/twig/html-extra" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/data-fixtures" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/doctrine-fixtures-bundle" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="bootstrap" level="application" />
|
||||
</component>
|
||||
</module>
|
||||
12
.idea/dataSources.xml
generated
Normal file
12
.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="app@localhost" uuid="7c4a7798-5e31-4429-bc70-035404e2d83b">
|
||||
<driver-ref>postgresql</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:postgresql://localhost:5432/app</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/jsLibraryMappings.xml
generated
Normal file
6
.idea/jsLibraryMappings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<file url="PROJECT" libraries="{bootstrap}" />
|
||||
</component>
|
||||
</project>
|
||||
44
.idea/php.xml
generated
44
.idea/php.xml
generated
@@ -1,5 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MessDetector">
|
||||
<phpmd_settings>
|
||||
<phpmd_by_interpreter asDefaultInterpreter="true" interpreter_id="c1266788-d465-407a-ac5d-1f67a9cf3e8a" timeout="30000" />
|
||||
</phpmd_settings>
|
||||
</component>
|
||||
<component name="MessDetectorOptionsConfiguration">
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
@@ -121,6 +126,43 @@
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-invoker" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-file-iterator" />
|
||||
<path value="$PROJECT_DIR$/vendor/staabm/side-effects-detector" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/twig-bundle" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/twig-bridge" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/translation-contracts" />
|
||||
<path value="$PROJECT_DIR$/vendor/twig/twig" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/uid" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-uuid" />
|
||||
<path value="$PROJECT_DIR$/vendor/psr/clock" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/security-bundle" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/type-info" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/property-access" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/property-info" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/password-hasher" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/clock" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/security-csrf" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/security-core" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/security-http" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/web-profiler-bundle" />
|
||||
<path value="$PROJECT_DIR$/vendor/thecodingmachine/safe" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpstan/extension-installer" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan-doctrine" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan-phpunit" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan-symfony" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/asset" />
|
||||
<path value="$PROJECT_DIR$/vendor/thecodingmachine/phpstan-safe-rule" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-icu" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/form" />
|
||||
<path value="$PROJECT_DIR$/vendor/twig/html-extra" />
|
||||
<path value="$PROJECT_DIR$/vendor/twig/extra-bundle" />
|
||||
<path value="$PROJECT_DIR$/vendor/easycorp/easyadmin-bundle" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/mime" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/validator" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/ux-twig-component" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-idn" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/intl" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/translation" />
|
||||
<path value="$PROJECT_DIR$/vendor/doctrine/data-fixtures" />
|
||||
<path value="$PROJECT_DIR$/vendor/doctrine/doctrine-fixtures-bundle" />
|
||||
</include_path>
|
||||
</component>
|
||||
<component name="PhpInterpreters">
|
||||
@@ -206,7 +248,7 @@
|
||||
</component>
|
||||
<component name="PhpStanOptionsConfiguration">
|
||||
<option name="config" value="$PROJECT_DIR$/phpstan.dist.neon" />
|
||||
<option name="level" value="6" />
|
||||
<option name="level" value="8" />
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
<component name="PhpUnit">
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$finder = (new PhpCsFixer\Finder())
|
||||
->in(__DIR__)
|
||||
->exclude('var')
|
||||
@@ -9,6 +11,7 @@ return (new PhpCsFixer\Config())
|
||||
->setRules([
|
||||
'@Symfony' => true,
|
||||
'@Symfony:risky' => true,
|
||||
'declare_strict_types' => true,
|
||||
'linebreak_after_opening_tag' => true,
|
||||
'mb_str_functions' => true,
|
||||
'no_php4_constructor' => true,
|
||||
@@ -20,8 +23,8 @@ return (new PhpCsFixer\Config())
|
||||
'strict_comparison' => true,
|
||||
'strict_param' => true,
|
||||
'blank_line_between_import_groups' => false,
|
||||
'phpdoc_line_span' => ['const' => 'single', 'method' => 'single', 'property' => 'single'],
|
||||
])
|
||||
->setRiskyAllowed(true)
|
||||
->setFinder($finder)
|
||||
;
|
||||
|
||||
|
||||
10
Makefile
10
Makefile
@@ -1,10 +1,16 @@
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
.PHONY: up
|
||||
up: ## Start application
|
||||
@docker compose up -d
|
||||
|
||||
stop: ## Stop application
|
||||
@docker compose stop
|
||||
|
||||
.PHONY: shell
|
||||
shell:
|
||||
shell: ## Start a shell inside the container
|
||||
@docker compose exec php bash
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-10s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
@@ -19,12 +19,12 @@ services:
|
||||
# Ensure that host.docker.internal is correctly defined on Linux
|
||||
- host.docker.internal:host-gateway
|
||||
tty: true
|
||||
|
||||
###> symfony/mercure-bundle ###
|
||||
###< symfony/mercure-bundle ###
|
||||
|
||||
###> doctrine/doctrine-bundle ###
|
||||
|
||||
###> symfony/mercure-bundle ###
|
||||
###< symfony/mercure-bundle ###
|
||||
|
||||
###> doctrine/doctrine-bundle ###
|
||||
database:
|
||||
ports:
|
||||
- "5432"
|
||||
- "5432:5432"
|
||||
###< doctrine/doctrine-bundle ###
|
||||
|
||||
@@ -13,25 +13,40 @@
|
||||
"doctrine/doctrine-bundle": "^2.13",
|
||||
"doctrine/doctrine-migrations-bundle": "^3.3",
|
||||
"doctrine/orm": "^3.3",
|
||||
"easycorp/easyadmin-bundle": "^4.23",
|
||||
"runtime/frankenphp-symfony": "^0.2.0",
|
||||
"symfony/asset": "7.2.*",
|
||||
"symfony/console": "7.2.*",
|
||||
"symfony/dotenv": "7.2.*",
|
||||
"symfony/flex": "^2",
|
||||
"symfony/flex": "^2.4.7",
|
||||
"symfony/form": "7.2.*",
|
||||
"symfony/framework-bundle": "7.2.*",
|
||||
"symfony/runtime": "7.2.*",
|
||||
"symfony/security-bundle": "7.2.*",
|
||||
"symfony/twig-bundle": "7.2.*",
|
||||
"symfony/yaml": "7.2.*"
|
||||
"symfony/uid": "7.2.*",
|
||||
"symfony/yaml": "7.2.*",
|
||||
"thecodingmachine/safe": "^2.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/doctrine-fixtures-bundle": "^4.0",
|
||||
"friendsofphp/php-cs-fixer": "^3.65",
|
||||
"phpstan/extension-installer": "^1.4",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
"phpstan/phpstan-doctrine": "^2.0",
|
||||
"phpstan/phpstan-phpunit": "^2.0",
|
||||
"phpstan/phpstan-symfony": "^2.0",
|
||||
"phpunit/phpunit": "^11",
|
||||
"rector/rector": "^2.0",
|
||||
"symfony/maker-bundle": "^1.61"
|
||||
"symfony/maker-bundle": "^1.62.1",
|
||||
"symfony/stopwatch": "7.2.*",
|
||||
"symfony/web-profiler-bundle": "7.2.*",
|
||||
"thecodingmachine/phpstan-safe-rule": "^1.3"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"php-http/discovery": true,
|
||||
"phpstan/extension-installer": true,
|
||||
"symfony/flex": true,
|
||||
"symfony/runtime": true
|
||||
},
|
||||
|
||||
3016
composer.lock
generated
3016
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -6,4 +6,10 @@ return [
|
||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
|
||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
|
||||
Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true],
|
||||
EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
|
||||
];
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
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
|
||||
11
config/packages/csrf.yaml
Normal file
11
config/packages/csrf.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
# Enable stateless CSRF protection for forms and logins/logouts
|
||||
framework:
|
||||
form:
|
||||
csrf_protection:
|
||||
token_id: submit
|
||||
|
||||
csrf_protection:
|
||||
stateless_token_ids:
|
||||
- submit
|
||||
- authenticate
|
||||
- logout
|
||||
@@ -24,8 +24,6 @@ doctrine:
|
||||
dir: '%kernel.project_dir%/src/Entity'
|
||||
prefix: 'App\Entity'
|
||||
alias: App
|
||||
controller_resolver:
|
||||
auto_mapping: false
|
||||
|
||||
when@test:
|
||||
doctrine:
|
||||
|
||||
52
config/packages/security.yaml
Normal file
52
config/packages/security.yaml
Normal file
@@ -0,0 +1,52 @@
|
||||
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:
|
||||
# used to reload user from session & other features (e.g. switch_user)
|
||||
app_user_provider:
|
||||
entity:
|
||||
class: App\Entity\User
|
||||
property: email
|
||||
# used to reload user from session & other features (e.g. switch_user)
|
||||
firewalls:
|
||||
dev:
|
||||
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
||||
security: false
|
||||
main:
|
||||
lazy: true
|
||||
provider: app_user_provider
|
||||
form_login:
|
||||
login_path: app_login
|
||||
check_path: app_login
|
||||
enable_csrf: true
|
||||
logout:
|
||||
path: app_logout
|
||||
# where to redirect after logout
|
||||
# target: app_any_route
|
||||
|
||||
# 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: ^/profile, 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
|
||||
7
config/packages/translation.yaml
Normal file
7
config/packages/translation.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
framework:
|
||||
default_locale: nl
|
||||
translator:
|
||||
default_path: '%kernel.project_dir%/translations'
|
||||
fallbacks:
|
||||
- en
|
||||
providers:
|
||||
@@ -1,5 +1,6 @@
|
||||
twig:
|
||||
file_name_pattern: '*.twig'
|
||||
form_themes: [ 'bootstrap_5_layout.html.twig' ]
|
||||
|
||||
when@test:
|
||||
twig:
|
||||
|
||||
5
config/packages/twig_component.yaml
Normal file
5
config/packages/twig_component.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
twig_component:
|
||||
anonymous_template_directory: 'components/'
|
||||
defaults:
|
||||
# Namespace & directory for components
|
||||
App\Twig\Components\: 'components/'
|
||||
4
config/packages/uid.yaml
Normal file
4
config/packages/uid.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
framework:
|
||||
uid:
|
||||
default_uuid_version: 7
|
||||
time_based_uuid_version: 7
|
||||
11
config/packages/validator.yaml
Normal file
11
config/packages/validator.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
framework:
|
||||
validation:
|
||||
# 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
|
||||
17
config/packages/web_profiler.yaml
Normal file
17
config/packages/web_profiler.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
when@dev:
|
||||
web_profiler:
|
||||
toolbar: true
|
||||
intercept_redirects: false
|
||||
|
||||
framework:
|
||||
profiler:
|
||||
only_exceptions: false
|
||||
collect_serializer_data: true
|
||||
|
||||
when@test:
|
||||
web_profiler:
|
||||
toolbar: false
|
||||
intercept_redirects: false
|
||||
|
||||
framework:
|
||||
profiler: { collect: false }
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
if (file_exists(dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php')) {
|
||||
require dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php';
|
||||
}
|
||||
|
||||
9
config/routes/attributes.yaml
Normal file
9
config/routes/attributes.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
controllers:
|
||||
resource:
|
||||
path: ../../src/Controller/
|
||||
namespace: App\Controller
|
||||
type: attribute
|
||||
|
||||
kernel:
|
||||
resource: App\Kernel
|
||||
type: attribute
|
||||
3
config/routes/easyadmin.yaml
Normal file
3
config/routes/easyadmin.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
easyadmin:
|
||||
resource: .
|
||||
type: easyadmin.routes
|
||||
3
config/routes/security.yaml
Normal file
3
config/routes/security.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
_security_logout:
|
||||
resource: security.route_loader.logout
|
||||
type: service
|
||||
8
config/routes/web_profiler.yaml
Normal file
8
config/routes/web_profiler.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
when@dev:
|
||||
web_profiler_wdt:
|
||||
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
|
||||
prefix: /_wdt
|
||||
|
||||
web_profiler_profiler:
|
||||
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
|
||||
prefix: /_profiler
|
||||
@@ -1,5 +1,5 @@
|
||||
expose_php = 0
|
||||
date.timezone = UTC
|
||||
date.timezone = Europe/Amsterdam
|
||||
apc.enable_cli = 1
|
||||
session.use_strict_mode = 1
|
||||
zend.detect_unicode = 0
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
; The `client_host` below may optionally be replaced with `discover_client_host=yes`
|
||||
; Add `start_with_request=yes` to start debug session on each request
|
||||
xdebug.client_host = host.docker.internal
|
||||
memory_limit = 2048M
|
||||
|
||||
@@ -2,26 +2,6 @@
|
||||
set -e
|
||||
|
||||
if [ "$1" = 'frankenphp' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then
|
||||
# Install the project the first time PHP is started
|
||||
# After the installation, the following block can be deleted
|
||||
if [ ! -f composer.json ]; then
|
||||
rm -Rf tmp/
|
||||
composer create-project "symfony/skeleton $SYMFONY_VERSION" tmp --stability="$STABILITY" --prefer-dist --no-progress --no-interaction --no-install
|
||||
|
||||
cd tmp
|
||||
cp -Rp . ..
|
||||
cd -
|
||||
rm -Rf tmp/
|
||||
|
||||
composer require "php:>=$PHP_VERSION" runtime/frankenphp-symfony
|
||||
composer config --json extra.symfony.docker 'true'
|
||||
|
||||
if grep -q ^DATABASE_URL= .env; then
|
||||
echo 'To finish the installation please press Ctrl+C to stop Docker Compose and run: docker compose up --build -d --wait'
|
||||
sleep infinity
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$(ls -A 'vendor/' 2>/dev/null)" ]; then
|
||||
composer install --prefer-dist --no-progress --no-interaction
|
||||
fi
|
||||
|
||||
90
migrations/Version20241229195702.php
Normal file
90
migrations/Version20241229195702.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20241229195702 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE answer (id UUID NOT NULL, question_id UUID NOT NULL, text VARCHAR(255) NOT NULL, is_right_answer BOOLEAN NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_DADD4A251E27F6BF ON answer (question_id)');
|
||||
$this->addSql('COMMENT ON COLUMN answer.id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN answer.question_id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('CREATE TABLE answer_candidate (answer_id UUID NOT NULL, candidate_id UUID NOT NULL, PRIMARY KEY(answer_id, candidate_id))');
|
||||
$this->addSql('CREATE INDEX IDX_F54D5192AA334807 ON answer_candidate (answer_id)');
|
||||
$this->addSql('CREATE INDEX IDX_F54D519291BD8781 ON answer_candidate (candidate_id)');
|
||||
$this->addSql('COMMENT ON COLUMN answer_candidate.answer_id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN answer_candidate.candidate_id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('CREATE TABLE candidate (id UUID NOT NULL, season_id UUID NOT NULL, name VARCHAR(16) NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_C8B28E444EC001D1 ON candidate (season_id)');
|
||||
$this->addSql('COMMENT ON COLUMN candidate.id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN candidate.season_id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('CREATE TABLE given_answer (id UUID NOT NULL, candidate_id UUID NOT NULL, quiz_id UUID NOT NULL, answer_id UUID NOT NULL, created TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_9AC61A3091BD8781 ON given_answer (candidate_id)');
|
||||
$this->addSql('CREATE INDEX IDX_9AC61A30853CD175 ON given_answer (quiz_id)');
|
||||
$this->addSql('CREATE INDEX IDX_9AC61A30AA334807 ON given_answer (answer_id)');
|
||||
$this->addSql('COMMENT ON COLUMN given_answer.id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN given_answer.candidate_id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN given_answer.quiz_id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN given_answer.answer_id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('CREATE TABLE question (id UUID NOT NULL, quiz_id UUID NOT NULL, question VARCHAR(255) NOT NULL, enabled BOOLEAN NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_B6F7494E853CD175 ON question (quiz_id)');
|
||||
$this->addSql('COMMENT ON COLUMN question.id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN question.quiz_id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('CREATE TABLE quiz (id UUID NOT NULL, season_id UUID NOT NULL, name VARCHAR(64) NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_A412FA924EC001D1 ON quiz (season_id)');
|
||||
$this->addSql('COMMENT ON COLUMN quiz.id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN quiz.season_id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('CREATE TABLE season (id UUID NOT NULL, name VARCHAR(64) NOT NULL, season_code VARCHAR(5) NOT NULL, preregister_candidates BOOLEAN NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('COMMENT ON COLUMN season.id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('CREATE TABLE "user" (id UUID NOT NULL, email VARCHAR(180) NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_EMAIL ON "user" (email)');
|
||||
$this->addSql('COMMENT ON COLUMN "user".id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('ALTER TABLE answer ADD CONSTRAINT FK_DADD4A251E27F6BF FOREIGN KEY (question_id) REFERENCES question (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE answer_candidate ADD CONSTRAINT FK_F54D5192AA334807 FOREIGN KEY (answer_id) REFERENCES answer (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE answer_candidate ADD CONSTRAINT FK_F54D519291BD8781 FOREIGN KEY (candidate_id) REFERENCES candidate (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE candidate ADD CONSTRAINT FK_C8B28E444EC001D1 FOREIGN KEY (season_id) REFERENCES season (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE given_answer ADD CONSTRAINT FK_9AC61A3091BD8781 FOREIGN KEY (candidate_id) REFERENCES candidate (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE given_answer ADD CONSTRAINT FK_9AC61A30853CD175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE given_answer ADD CONSTRAINT FK_9AC61A30AA334807 FOREIGN KEY (answer_id) REFERENCES answer (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE question ADD CONSTRAINT FK_B6F7494E853CD175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE quiz ADD CONSTRAINT FK_A412FA924EC001D1 FOREIGN KEY (season_id) REFERENCES season (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE answer DROP CONSTRAINT FK_DADD4A251E27F6BF');
|
||||
$this->addSql('ALTER TABLE answer_candidate DROP CONSTRAINT FK_F54D5192AA334807');
|
||||
$this->addSql('ALTER TABLE answer_candidate DROP CONSTRAINT FK_F54D519291BD8781');
|
||||
$this->addSql('ALTER TABLE candidate DROP CONSTRAINT FK_C8B28E444EC001D1');
|
||||
$this->addSql('ALTER TABLE given_answer DROP CONSTRAINT FK_9AC61A3091BD8781');
|
||||
$this->addSql('ALTER TABLE given_answer DROP CONSTRAINT FK_9AC61A30853CD175');
|
||||
$this->addSql('ALTER TABLE given_answer DROP CONSTRAINT FK_9AC61A30AA334807');
|
||||
$this->addSql('ALTER TABLE question DROP CONSTRAINT FK_B6F7494E853CD175');
|
||||
$this->addSql('ALTER TABLE quiz DROP CONSTRAINT FK_A412FA924EC001D1');
|
||||
$this->addSql('DROP TABLE answer');
|
||||
$this->addSql('DROP TABLE answer_candidate');
|
||||
$this->addSql('DROP TABLE candidate');
|
||||
$this->addSql('DROP TABLE given_answer');
|
||||
$this->addSql('DROP TABLE question');
|
||||
$this->addSql('DROP TABLE quiz');
|
||||
$this->addSql('DROP TABLE season');
|
||||
$this->addSql('DROP TABLE "user"');
|
||||
}
|
||||
}
|
||||
40
migrations/Version20241229201314.php
Normal file
40
migrations/Version20241229201314.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20241229201314 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE season_user (season_id UUID NOT NULL, user_id UUID NOT NULL, PRIMARY KEY(season_id, user_id))');
|
||||
$this->addSql('CREATE INDEX IDX_BDA4AD74EC001D1 ON season_user (season_id)');
|
||||
$this->addSql('CREATE INDEX IDX_BDA4AD7A76ED395 ON season_user (user_id)');
|
||||
$this->addSql('COMMENT ON COLUMN season_user.season_id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN season_user.user_id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('ALTER TABLE season_user ADD CONSTRAINT FK_BDA4AD74EC001D1 FOREIGN KEY (season_id) REFERENCES season (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE season_user ADD CONSTRAINT FK_BDA4AD7A76ED395 FOREIGN KEY (user_id) REFERENCES "user" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE season_user DROP CONSTRAINT FK_BDA4AD74EC001D1');
|
||||
$this->addSql('ALTER TABLE season_user DROP CONSTRAINT FK_BDA4AD7A76ED395');
|
||||
$this->addSql('DROP TABLE season_user');
|
||||
}
|
||||
}
|
||||
41
migrations/Version20241229202103.php
Normal file
41
migrations/Version20241229202103.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20241229202103 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE correction (id UUID NOT NULL, candidate_id UUID NOT NULL, quiz_id UUID NOT NULL, amount DOUBLE PRECISION NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_A29DA1B891BD8781 ON correction (candidate_id)');
|
||||
$this->addSql('CREATE INDEX IDX_A29DA1B8853CD175 ON correction (quiz_id)');
|
||||
$this->addSql('COMMENT ON COLUMN correction.id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN correction.candidate_id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('COMMENT ON COLUMN correction.quiz_id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('ALTER TABLE correction ADD CONSTRAINT FK_A29DA1B891BD8781 FOREIGN KEY (candidate_id) REFERENCES candidate (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE correction ADD CONSTRAINT FK_A29DA1B8853CD175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE correction DROP CONSTRAINT FK_A29DA1B891BD8781');
|
||||
$this->addSql('ALTER TABLE correction DROP CONSTRAINT FK_A29DA1B8853CD175');
|
||||
$this->addSql('DROP TABLE correction');
|
||||
}
|
||||
}
|
||||
32
migrations/Version20241229202155.php
Normal file
32
migrations/Version20241229202155.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20241229202155 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_A29DA1B891BD8781853CD175 ON correction (candidate_id, quiz_id)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('DROP INDEX UNIQ_A29DA1B891BD8781853CD175');
|
||||
}
|
||||
}
|
||||
34
migrations/Version20241229204335.php
Normal file
34
migrations/Version20241229204335.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20241229204335 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE given_answer ALTER answer_id DROP NOT NULL');
|
||||
$this->addSql('ALTER TABLE given_answer ALTER created DROP NOT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE given_answer ALTER answer_id SET NOT NULL');
|
||||
$this->addSql('ALTER TABLE given_answer ALTER created SET NOT NULL');
|
||||
}
|
||||
}
|
||||
39
migrations/Version20250303221227.php
Normal file
39
migrations/Version20250303221227.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20250303221227 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE given_answer ALTER created SET NOT NULL');
|
||||
$this->addSql('ALTER TABLE season ADD active_quiz_id UUID DEFAULT NULL');
|
||||
$this->addSql('COMMENT ON COLUMN season.active_quiz_id IS \'(DC2Type:uuid)\'');
|
||||
$this->addSql('ALTER TABLE season ADD CONSTRAINT FK_F0E45BA96706D6B FOREIGN KEY (active_quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX IDX_F0E45BA96706D6B ON season (active_quiz_id)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE season DROP CONSTRAINT FK_F0E45BA96706D6B');
|
||||
$this->addSql('DROP INDEX IDX_F0E45BA96706D6B');
|
||||
$this->addSql('ALTER TABLE season DROP active_quiz_id');
|
||||
$this->addSql('ALTER TABLE given_answer ALTER created DROP NOT NULL');
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
parameters:
|
||||
level: 6
|
||||
level: 8
|
||||
paths:
|
||||
- bin/
|
||||
- config/
|
||||
|
||||
BIN
public/img/background.png
Normal file
BIN
public/img/background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 164 KiB |
@@ -1,7 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Kernel;
|
||||
|
||||
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
|
||||
|
||||
return fn (array $context): \App\Kernel => new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
|
||||
return static function (array $context): Kernel {
|
||||
$appEnv = !empty($context['APP_ENV']) ? (string) $context['APP_ENV'] : 'prod';
|
||||
$appDebug = !empty($context['APP_DEBUG']) ? filter_var($context['APP_DEBUG'], \FILTER_VALIDATE_BOOL) : 'prod' !== $appEnv;
|
||||
|
||||
return new Kernel($appEnv, $appDebug);
|
||||
};
|
||||
|
||||
19
rector.php
19
rector.php
@@ -12,5 +12,20 @@ return RectorConfig::configure()
|
||||
__DIR__.'/tests',
|
||||
])
|
||||
->withPhpSets()
|
||||
->withPreparedSets(deadCode: true, codeQuality: true, codingStyle: true, typeDeclarations: true, privatization: true, instanceOf: true, earlyReturn: true, strictBooleans: true, phpunitCodeQuality: true, doctrineCodeQuality: true, symfonyCodeQuality: true)
|
||||
->withComposerBased(twig: true, doctrine: true, phpunit: true);
|
||||
->withPreparedSets(
|
||||
deadCode: true,
|
||||
codeQuality: true,
|
||||
codingStyle: true,
|
||||
typeDeclarations: true,
|
||||
privatization: true,
|
||||
instanceOf: true,
|
||||
earlyReturn: true,
|
||||
strictBooleans: true,
|
||||
rectorPreset: true,
|
||||
phpunitCodeQuality: true,
|
||||
doctrineCodeQuality: true,
|
||||
symfonyCodeQuality: true,
|
||||
)
|
||||
->withComposerBased(twig: true, doctrine: true, phpunit: true)
|
||||
->withAttributesSets()
|
||||
;
|
||||
|
||||
28
src/Controller/Admin/AnswerCrudController.php
Normal file
28
src/Controller/Admin/AnswerCrudController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\Answer;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
|
||||
class AnswerCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return Answer::class;
|
||||
}
|
||||
|
||||
/*
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
return [
|
||||
IdField::new('id'),
|
||||
TextField::new('title'),
|
||||
TextEditorField::new('description'),
|
||||
];
|
||||
}
|
||||
*/
|
||||
}
|
||||
28
src/Controller/Admin/CandidateCrudController.php
Normal file
28
src/Controller/Admin/CandidateCrudController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\Candidate;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
|
||||
class CandidateCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return Candidate::class;
|
||||
}
|
||||
|
||||
/*
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
return [
|
||||
IdField::new('id'),
|
||||
TextField::new('title'),
|
||||
TextEditorField::new('description'),
|
||||
];
|
||||
}
|
||||
*/
|
||||
}
|
||||
28
src/Controller/Admin/CorrectionCrudController.php
Normal file
28
src/Controller/Admin/CorrectionCrudController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\Correction;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
|
||||
class CorrectionCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return Correction::class;
|
||||
}
|
||||
|
||||
/*
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
return [
|
||||
IdField::new('id'),
|
||||
TextField::new('title'),
|
||||
TextEditorField::new('description'),
|
||||
];
|
||||
}
|
||||
*/
|
||||
}
|
||||
66
src/Controller/Admin/DashboardController.php
Normal file
66
src/Controller/Admin/DashboardController.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\Answer;
|
||||
use App\Entity\Candidate;
|
||||
use App\Entity\Correction;
|
||||
use App\Entity\GivenAnswer;
|
||||
use App\Entity\Question;
|
||||
use App\Entity\Quiz;
|
||||
use App\Entity\Season;
|
||||
use App\Entity\User;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class DashboardController extends AbstractDashboardController
|
||||
{
|
||||
#[Route('/admin', name: 'admin')]
|
||||
public function index(): Response
|
||||
{
|
||||
// return parent::index();
|
||||
|
||||
// Option 1. You can make your dashboard redirect to some common page of your backend
|
||||
//
|
||||
$adminUrlGenerator = $this->container->get(AdminUrlGenerator::class);
|
||||
|
||||
return $this->redirect($adminUrlGenerator->setController(SeasonCrudController::class)->generateUrl());
|
||||
|
||||
// Option 2. You can make your dashboard redirect to different pages depending on the user
|
||||
//
|
||||
// if ('jane' === $this->getUser()->getUsername()) {
|
||||
// return $this->redirect('...');
|
||||
// }
|
||||
|
||||
// Option 3. You can render some custom template to display a proper dashboard with widgets, etc.
|
||||
// (tip: it's easier if your template extends from @EasyAdmin/page/content.html.twig)
|
||||
//
|
||||
// return $this->render('some/path/my-dashboard.html.twig');
|
||||
}
|
||||
|
||||
public function configureDashboard(): Dashboard
|
||||
{
|
||||
return Dashboard::new()
|
||||
->setTitle('TijdVoorDeTest');
|
||||
}
|
||||
|
||||
public function configureMenuItems(): iterable
|
||||
{
|
||||
yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home');
|
||||
yield MenuItem::linkToCrud('Season', 'fas fa-list', Season::class);
|
||||
yield MenuItem::linkToCrud('Quiz', 'fas fa-list', Quiz::class);
|
||||
yield MenuItem::linkToCrud('Question', 'fas fa-list', Question::class);
|
||||
yield MenuItem::linkToCrud('Candidate', 'fas fa-list', Candidate::class);
|
||||
yield MenuItem::linkToCrud('Correction', 'fas fa-list', Correction::class);
|
||||
yield MenuItem::linkToCrud('User', 'fas fa-list', User::class);
|
||||
yield MenuItem::linkToCrud('Given Answer', 'fas fa-list', GivenAnswer::class);
|
||||
yield MenuItem::linkToCrud('Answer', 'fas fa-list', Answer::class);
|
||||
yield MenuItem::linkToLogout('Logout', 'fa fa-exit');
|
||||
}
|
||||
}
|
||||
28
src/Controller/Admin/GivenAnswerCrudController.php
Normal file
28
src/Controller/Admin/GivenAnswerCrudController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\GivenAnswer;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
|
||||
class GivenAnswerCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return GivenAnswer::class;
|
||||
}
|
||||
|
||||
/*
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
return [
|
||||
IdField::new('id'),
|
||||
TextField::new('title'),
|
||||
TextEditorField::new('description'),
|
||||
];
|
||||
}
|
||||
*/
|
||||
}
|
||||
28
src/Controller/Admin/QuestionCrudController.php
Normal file
28
src/Controller/Admin/QuestionCrudController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\Question;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
|
||||
class QuestionCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return Question::class;
|
||||
}
|
||||
|
||||
/*
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
return [
|
||||
IdField::new('id'),
|
||||
TextField::new('title'),
|
||||
TextEditorField::new('description'),
|
||||
];
|
||||
}
|
||||
*/
|
||||
}
|
||||
28
src/Controller/Admin/QuizCrudController.php
Normal file
28
src/Controller/Admin/QuizCrudController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\Quiz;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
|
||||
class QuizCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return Quiz::class;
|
||||
}
|
||||
|
||||
/*
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
return [
|
||||
IdField::new('id'),
|
||||
TextField::new('title'),
|
||||
TextEditorField::new('description'),
|
||||
];
|
||||
}
|
||||
*/
|
||||
}
|
||||
28
src/Controller/Admin/SeasonCrudController.php
Normal file
28
src/Controller/Admin/SeasonCrudController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\Season;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
|
||||
class SeasonCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return Season::class;
|
||||
}
|
||||
|
||||
/*
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
return [
|
||||
IdField::new('id'),
|
||||
TextField::new('title'),
|
||||
TextEditorField::new('description'),
|
||||
];
|
||||
}
|
||||
*/
|
||||
}
|
||||
28
src/Controller/Admin/UserCrudController.php
Normal file
28
src/Controller/Admin/UserCrudController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\User;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
|
||||
class UserCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return User::class;
|
||||
}
|
||||
|
||||
/*
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
return [
|
||||
IdField::new('id'),
|
||||
TextField::new('title'),
|
||||
TextEditorField::new('description'),
|
||||
];
|
||||
}
|
||||
*/
|
||||
}
|
||||
34
src/Controller/LoginController.php
Normal file
34
src/Controller/LoginController.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
|
||||
class LoginController extends AbstractController
|
||||
{
|
||||
#[Route(path: '/login', name: 'app_login')]
|
||||
public function login(AuthenticationUtils $authenticationUtils): Response
|
||||
{
|
||||
// get the login error if there is one
|
||||
$error = $authenticationUtils->getLastAuthenticationError();
|
||||
|
||||
// last username entered by the user
|
||||
$lastUsername = $authenticationUtils->getLastUsername();
|
||||
|
||||
return $this->render('login/login.html.twig', [
|
||||
'last_username' => $lastUsername,
|
||||
'error' => $error,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route(path: '/logout', name: 'app_logout')]
|
||||
public function logout(): void
|
||||
{
|
||||
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
|
||||
}
|
||||
}
|
||||
72
src/Controller/QuizController.php
Normal file
72
src/Controller/QuizController.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Season;
|
||||
use App\Form\EnterNameType;
|
||||
use App\Form\SelectSeasonType;
|
||||
use App\Helpers\Base64;
|
||||
use Safe\Exceptions\UrlException;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Attribute\AsController;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
#[AsController]
|
||||
class QuizController extends AbstractController
|
||||
{
|
||||
public const string SEASON_CODE_REGEX = '[A-Za-z\d]{5}';
|
||||
private const string CANDIDATE_HASH_REGEX = '[\w\-=]+';
|
||||
|
||||
#[Route(path: '/', name: 'select_season', methods: ['GET', 'POST'])]
|
||||
public function selectSeason(Request $request): Response
|
||||
{
|
||||
$form = $this->createForm(SelectSeasonType::class);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$data = $form->getData();
|
||||
|
||||
return $this->redirectToRoute('enter_name', ['seasonCode' => $data['season_code']]);
|
||||
}
|
||||
|
||||
return $this->render('quiz/select_season.html.twig', ['form' => $form]);
|
||||
}
|
||||
|
||||
#[Route(path: '/{seasonCode}', name: 'enter_name', requirements: ['seasonCode' => self::SEASON_CODE_REGEX])]
|
||||
public function enterName(
|
||||
Request $request,
|
||||
Season $season,
|
||||
): Response {
|
||||
$form = $this->createForm(EnterNameType::class);
|
||||
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$data = $form->getData();
|
||||
$name = $data['name'];
|
||||
|
||||
return $this->redirectToRoute('quiz_page', ['seasonCode' => $season->getSeasonCode(), 'nameHash' => Base64::base64_url_encode($name)]);
|
||||
}
|
||||
|
||||
return $this->render('quiz/enter_name.twig', ['season' => $season, 'form' => $form]);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
path: '/{seasonCode}/{nameHash}',
|
||||
name: 'quiz_page',
|
||||
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'nameHash' => self::CANDIDATE_HASH_REGEX],
|
||||
)]
|
||||
public function quizPage(Season $season, string $nameHash)
|
||||
{
|
||||
try {
|
||||
$name = Base64::base64_url_decode($nameHash);
|
||||
} catch (UrlException $e) {
|
||||
}
|
||||
|
||||
return $this->render('quiz/question.twig', ['season' => $season, 'name' => $name]);
|
||||
}
|
||||
}
|
||||
185
src/DataFixtures/KrtekFixtures.php
Normal file
185
src/DataFixtures/KrtekFixtures.php
Normal file
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\DataFixtures;
|
||||
|
||||
use App\Entity\Answer;
|
||||
use App\Entity\Candidate;
|
||||
use App\Entity\Question;
|
||||
use App\Entity\Quiz;
|
||||
use App\Entity\Season;
|
||||
use Doctrine\Bundle\FixturesBundle\Fixture;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
|
||||
class KrtekFixtures extends Fixture
|
||||
{
|
||||
public function load(ObjectManager $manager): void
|
||||
{
|
||||
$season = new Season();
|
||||
$manager->persist($season);
|
||||
|
||||
$season->setName('Krtek Weekend')
|
||||
->setSeasonCode('12345')
|
||||
->setPreregisterCandidates(true);
|
||||
|
||||
$quiz1 = new Quiz();
|
||||
$manager->persist($quiz1);
|
||||
$quiz1->setName('Quiz 1')
|
||||
->setSeason($season);
|
||||
|
||||
$season->setActiveQuiz($quiz1)
|
||||
->addCandidate(new Candidate('Claudia'))
|
||||
->addCandidate(new Candidate('Eelco'))
|
||||
->addCandidate(new Candidate('Elise'))
|
||||
->addCandidate(new Candidate('Gert-Jan'))
|
||||
->addCandidate(new Candidate('Iris'))
|
||||
->addCandidate(new Candidate('Jari'))
|
||||
->addCandidate(new Candidate('Lara'))
|
||||
->addCandidate(new Candidate('Lotte'))
|
||||
->addCandidate(new Candidate('Myrthe'))
|
||||
->addCandidate(new Candidate('Philine'))
|
||||
->addCandidate(new Candidate('Remy'))
|
||||
->addCandidate(new Candidate('Robbert'))
|
||||
->addCandidate(new Candidate('Tom'))
|
||||
;
|
||||
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('Is de Krtek een man of een vrouw?')
|
||||
->addAnswer(new Answer('Ja', true))
|
||||
->addAnswer(new Answer('Nee'))
|
||||
);
|
||||
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('Hoeveel broers heeft de Krtek?')
|
||||
->addAnswer(new Answer('Geen', true))
|
||||
->addAnswer(new Answer('1'))
|
||||
->addAnswer(new Answer('2'))
|
||||
);
|
||||
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('Wat is de lievelingsfeestdag van de Krtek?')
|
||||
->addAnswer(new Answer('Geen'))
|
||||
->addAnswer(new Answer('Diens eigen verjaardag'))
|
||||
->addAnswer(new Answer('Koningsdag'))
|
||||
->addAnswer(new Answer('Kerst', true))
|
||||
->addAnswer(new Answer('Oud en Nieuw'))
|
||||
);
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('Hoe kwam de Krtek naar Kersteren vandaag?')
|
||||
->addAnswer(new Answer('Met het OV', true))
|
||||
->addAnswer(new Answer('Met de auto'))
|
||||
);
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('Met wie keek de Kretek video bij binnenkomst?')
|
||||
->addAnswer(new Answer('Claudia'))
|
||||
->addAnswer(new Answer('Eelco'))
|
||||
->addAnswer(new Answer('Elise'))
|
||||
->addAnswer(new Answer('Gert-Jan'))
|
||||
->addAnswer(new Answer('Iris'))
|
||||
->addAnswer(new Answer('Jari'))
|
||||
->addAnswer(new Answer('Lara'))
|
||||
->addAnswer(new Answer('Lotte'))
|
||||
->addAnswer(new Answer('Myrthe'))
|
||||
->addAnswer(new Answer('Philine'))
|
||||
->addAnswer(new Answer('Remy'))
|
||||
->addAnswer(new Answer('Robbert'))
|
||||
->addAnswer(new Answer('Tom', true))
|
||||
);
|
||||
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('Welk advies zou de Krtek zichzelf als kind geven?')
|
||||
->addAnswer(new Answer('Geef je vader een knuffel.'))
|
||||
->addAnswer(new Answer('Trek je wat minder aan van anderen.'))
|
||||
->addAnswer(new Answer('Luister meer naar je eigen gevoel in plaats van naar wat anderen vinden.'))
|
||||
->addAnswer(new Answer('Stel niet alles tot het laatste moment uit.'))
|
||||
->addAnswer(new Answer('Altijd doorgaan.'))
|
||||
->addAnswer(new Answer('Probeer ook eens buiten de lijntjes te kleuren', true))
|
||||
->addAnswer(new Answer('Ga als je groot bent op groepsreis! '))
|
||||
->addAnswer(new Answer('Trek minder aan van de mening van anderen, het is oké om anders te zijn.'))
|
||||
);
|
||||
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('Wat voor soort schoenen droeg de Krtek bij het diner?')
|
||||
->addAnswer(new Answer('Sneakers'))
|
||||
->addAnswer(new Answer('Wandel-/bergschoenen', true))
|
||||
->addAnswer(new Answer('Lederen schoenen'))
|
||||
->addAnswer(new Answer('Pantoffels'))
|
||||
->addAnswer(new Answer('Hakken'))
|
||||
->addAnswer(new Answer('Geen schoenen, alleen sokken'))
|
||||
);
|
||||
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('Met welk vervoersmiddel reist de Krtek het liefste?')
|
||||
->addAnswer(new Answer('Fiets', true))
|
||||
->addAnswer(new Answer('Auto'))
|
||||
->addAnswer(new Answer('Trein'))
|
||||
);
|
||||
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('Heeft de Krtek een eigen auto?')
|
||||
->addAnswer(new Answer('Ja'))
|
||||
->addAnswer(new Answer('Nee', true))
|
||||
);
|
||||
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('Van wie is de quote die de Krtek gepakt heeft')
|
||||
->addAnswer(new Answer('Karen'))
|
||||
->addAnswer(new Answer('Gilles de Coster'))
|
||||
->addAnswer(new Answer('Kees Tol'))
|
||||
->addAnswer(new Answer('Harry en John'))
|
||||
->addAnswer(new Answer('Georgina Verbaan'))
|
||||
->addAnswer(new Answer('Marc-Marie Huijbregts'))
|
||||
->addAnswer(new Answer('Fresia Cousiño Arias, Rik van de Westelaken'))
|
||||
->addAnswer(new Answer('Ellie Lust'))
|
||||
->addAnswer(new Answer('Bouba'))
|
||||
->addAnswer(new Answer('Jan Versteegh'))
|
||||
->addAnswer(new Answer('Dick Jol'))
|
||||
->addAnswer(new Answer('Karin de Groot'))
|
||||
->addAnswer(new Answer('Pieter'))
|
||||
->addAnswer(new Answer('Renée Fokker'))
|
||||
->addAnswer(new Answer('Sam, Davy', true))
|
||||
);
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('Zou de Krtek molboekjes, jokers, vrijstellingen of topito’s uit iemands rugzak stelen om te kunnen winnen?')
|
||||
->addAnswer(new Answer('Ja'))
|
||||
->addAnswer(new Answer('Nee', true))
|
||||
);
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('In wat voor bed slaapt de Krtek dit weekend?')
|
||||
->addAnswer(new Answer('Éénpersoons, losstaand bed'))
|
||||
->addAnswer(new Answer('Éénpersoonsbed, tegen een ander bed aan', true))
|
||||
->addAnswer(new Answer('Tweepersoons bed'))
|
||||
);
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('Hoeveel jaar heeft de Krtek gedaan over de middelbare school?')
|
||||
->addAnswer(new Answer('5'))
|
||||
->addAnswer(new Answer('6', true))
|
||||
->addAnswer(new Answer('7'))
|
||||
->addAnswer(new Answer('8'))
|
||||
);
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('Waar zat de Krtek aan tafel bij het diner?')
|
||||
->addAnswer(new Answer('Met de rug naar de accommodatie'))
|
||||
->addAnswer(new Answer('Met de rug naar de buitenmuur', true))
|
||||
);
|
||||
$quiz1->addQuestion((new Question())
|
||||
->setQuestion('Wie is de Krtek?')
|
||||
->addAnswer(new Answer('Claudia', true))
|
||||
->addAnswer(new Answer('Eelco'))
|
||||
->addAnswer(new Answer('Elise'))
|
||||
->addAnswer(new Answer('Gert-Jan'))
|
||||
->addAnswer(new Answer('Iris'))
|
||||
->addAnswer(new Answer('Jari'))
|
||||
->addAnswer(new Answer('Lara'))
|
||||
->addAnswer(new Answer('Lotte'))
|
||||
->addAnswer(new Answer('Myrthe'))
|
||||
->addAnswer(new Answer('Philine'))
|
||||
->addAnswer(new Answer('Remy'))
|
||||
->addAnswer(new Answer('Robbert'))
|
||||
->addAnswer(new Answer('Tom'))
|
||||
);
|
||||
|
||||
$manager->flush();
|
||||
}
|
||||
}
|
||||
124
src/Entity/Answer.php
Normal file
124
src/Entity/Answer.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\AnswerRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
|
||||
use Symfony\Bridge\Doctrine\Types\UuidType;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
#[ORM\Entity(repositoryClass: AnswerRepository::class)]
|
||||
class Answer
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: UuidType::NAME)]
|
||||
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
|
||||
#[ORM\CustomIdGenerator(class: UuidGenerator::class)]
|
||||
private Uuid $id;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'answers')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private Question $question;
|
||||
|
||||
/** @var Collection<int, Candidate> */
|
||||
#[ORM\ManyToMany(targetEntity: Candidate::class, inversedBy: 'answersOnCandidate')]
|
||||
private Collection $candidates;
|
||||
|
||||
/** @var Collection<int, GivenAnswer> */
|
||||
#[ORM\OneToMany(targetEntity: GivenAnswer::class, mappedBy: 'answer', orphanRemoval: true)]
|
||||
private Collection $givenAnswers;
|
||||
|
||||
public function __construct(
|
||||
#[ORM\Column(length: 255)]
|
||||
private string $text,
|
||||
#[ORM\Column]
|
||||
private bool $isRightAnswer = false,
|
||||
) {
|
||||
$this->candidates = new ArrayCollection();
|
||||
$this->givenAnswers = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): Uuid
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getText(): string
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
public function setText(string $text): static
|
||||
{
|
||||
$this->text = $text;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getQuestion(): Question
|
||||
{
|
||||
return $this->question;
|
||||
}
|
||||
|
||||
public function setQuestion(Question $question): static
|
||||
{
|
||||
$this->question = $question;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isRightAnswer(): ?bool
|
||||
{
|
||||
return $this->isRightAnswer;
|
||||
}
|
||||
|
||||
public function setRightAnswer(bool $isRightAnswer): static
|
||||
{
|
||||
$this->isRightAnswer = $isRightAnswer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, Candidate> */
|
||||
public function getCandidates(): Collection
|
||||
{
|
||||
return $this->candidates;
|
||||
}
|
||||
|
||||
public function addCandidate(Candidate $candidate): static
|
||||
{
|
||||
if (!$this->candidates->contains($candidate)) {
|
||||
$this->candidates->add($candidate);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeCandidate(Candidate $candidate): static
|
||||
{
|
||||
$this->candidates->removeElement($candidate);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, GivenAnswer> */
|
||||
public function getGivenAnswers(): Collection
|
||||
{
|
||||
return $this->givenAnswers;
|
||||
}
|
||||
|
||||
public function addGivenAnswer(GivenAnswer $givenAnswer): static
|
||||
{
|
||||
if (!$this->givenAnswers->contains($givenAnswer)) {
|
||||
$this->givenAnswers->add($givenAnswer);
|
||||
$givenAnswer->setAnswer($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
140
src/Entity/Candidate.php
Normal file
140
src/Entity/Candidate.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Helpers\Base64;
|
||||
use App\Repository\CandidateRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
|
||||
use Symfony\Bridge\Doctrine\Types\UuidType;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
#[ORM\Entity(repositoryClass: CandidateRepository::class)]
|
||||
class Candidate
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: UuidType::NAME, unique: true)]
|
||||
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
|
||||
#[ORM\CustomIdGenerator(class: UuidGenerator::class)]
|
||||
private ?Uuid $id = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'candidates')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private Season $season;
|
||||
|
||||
/** @var Collection<int, Answer> */
|
||||
#[ORM\ManyToMany(targetEntity: Answer::class, mappedBy: 'candidates')]
|
||||
private Collection $answersOnCandidate;
|
||||
|
||||
/** @var Collection<int, GivenAnswer> */
|
||||
#[ORM\OneToMany(targetEntity: GivenAnswer::class, mappedBy: 'candidate', orphanRemoval: true)]
|
||||
private Collection $givenAnswers;
|
||||
|
||||
/** @var Collection<int, Correction> */
|
||||
#[ORM\OneToMany(targetEntity: Correction::class, mappedBy: 'candidate', orphanRemoval: true)]
|
||||
private Collection $corrections;
|
||||
|
||||
public function __construct(
|
||||
#[ORM\Column(length: 16)]
|
||||
private string $name,
|
||||
) {
|
||||
$this->answersOnCandidate = new ArrayCollection();
|
||||
$this->givenAnswers = new ArrayCollection();
|
||||
$this->corrections = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?Uuid
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getSeason(): Season
|
||||
{
|
||||
return $this->season;
|
||||
}
|
||||
|
||||
public function setSeason(Season $season): static
|
||||
{
|
||||
$this->season = $season;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName(string $name): static
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, Answer> */
|
||||
public function getAnswersOnCandidate(): Collection
|
||||
{
|
||||
return $this->answersOnCandidate;
|
||||
}
|
||||
|
||||
public function addAnswersOnCandidate(Answer $answersOnCandidate): static
|
||||
{
|
||||
if (!$this->answersOnCandidate->contains($answersOnCandidate)) {
|
||||
$this->answersOnCandidate->add($answersOnCandidate);
|
||||
$answersOnCandidate->addCandidate($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeAnswersOnCandidate(Answer $answersOnCandidate): static
|
||||
{
|
||||
if ($this->answersOnCandidate->removeElement($answersOnCandidate)) {
|
||||
$answersOnCandidate->removeCandidate($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, GivenAnswer> */
|
||||
public function getGivenAnswers(): Collection
|
||||
{
|
||||
return $this->givenAnswers;
|
||||
}
|
||||
|
||||
public function addGivenAnswer(GivenAnswer $givenAnswer): static
|
||||
{
|
||||
if (!$this->givenAnswers->contains($givenAnswer)) {
|
||||
$this->givenAnswers->add($givenAnswer);
|
||||
$givenAnswer->setCandidate($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, Correction> */
|
||||
public function getCorrections(): Collection
|
||||
{
|
||||
return $this->corrections;
|
||||
}
|
||||
|
||||
public function addCorrection(Correction $correction): static
|
||||
{
|
||||
if (!$this->corrections->contains($correction)) {
|
||||
$this->corrections->add($correction);
|
||||
$correction->setCandidate($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNameHash(): string
|
||||
{
|
||||
return Base64::base64_url_encode($this->name);
|
||||
}
|
||||
}
|
||||
74
src/Entity/Correction.php
Normal file
74
src/Entity/Correction.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\CorrectionRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
|
||||
use Symfony\Bridge\Doctrine\Types\UuidType;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
#[ORM\Entity(repositoryClass: CorrectionRepository::class)]
|
||||
#[ORM\UniqueConstraint(columns: ['candidate_id', 'quiz_id'])]
|
||||
class Correction
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: UuidType::NAME, unique: true)]
|
||||
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
|
||||
#[ORM\CustomIdGenerator(class: UuidGenerator::class)]
|
||||
private ?Uuid $id = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'corrections')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private Candidate $candidate;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'corrections')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private Quiz $quiz;
|
||||
|
||||
#[ORM\Column]
|
||||
private float $amount = 0;
|
||||
|
||||
public function getId(): ?Uuid
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getCandidate(): Candidate
|
||||
{
|
||||
return $this->candidate;
|
||||
}
|
||||
|
||||
public function setCandidate(Candidate $candidate): static
|
||||
{
|
||||
$this->candidate = $candidate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getQuiz(): Quiz
|
||||
{
|
||||
return $this->quiz;
|
||||
}
|
||||
|
||||
public function setQuiz(Quiz $quiz): static
|
||||
{
|
||||
$this->quiz = $quiz;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAmount(): ?float
|
||||
{
|
||||
return $this->amount;
|
||||
}
|
||||
|
||||
public function setAmount(float $amount): static
|
||||
{
|
||||
$this->amount = $amount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
91
src/Entity/GivenAnswer.php
Normal file
91
src/Entity/GivenAnswer.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\GivenAnswerRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Safe\DateTimeImmutable;
|
||||
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
|
||||
use Symfony\Bridge\Doctrine\Types\UuidType;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
#[ORM\Entity(repositoryClass: GivenAnswerRepository::class)]
|
||||
#[ORM\HasLifecycleCallbacks]
|
||||
class GivenAnswer
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: UuidType::NAME, unique: true)]
|
||||
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
|
||||
#[ORM\CustomIdGenerator(class: UuidGenerator::class)]
|
||||
private ?Uuid $id = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'givenAnswers')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private Candidate $candidate;
|
||||
|
||||
#[ORM\ManyToOne]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private Quiz $quiz;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'givenAnswers')]
|
||||
#[ORM\JoinColumn(nullable: true)]
|
||||
private ?Answer $answer = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: false)]
|
||||
private \DateTimeInterface $created;
|
||||
|
||||
public function getId(): ?Uuid
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getCandidate(): Candidate
|
||||
{
|
||||
return $this->candidate;
|
||||
}
|
||||
|
||||
public function setCandidate(Candidate $candidate): static
|
||||
{
|
||||
$this->candidate = $candidate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getQuiz(): ?Quiz
|
||||
{
|
||||
return $this->quiz;
|
||||
}
|
||||
|
||||
public function setQuiz(Quiz $quiz): static
|
||||
{
|
||||
$this->quiz = $quiz;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAnswer(): ?Answer
|
||||
{
|
||||
return $this->answer;
|
||||
}
|
||||
|
||||
public function setAnswer(?Answer $answer): static
|
||||
{
|
||||
$this->answer = $answer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreated(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
#[ORM\PrePersist]
|
||||
public function setCreatedAtValue(): void
|
||||
{
|
||||
$this->created = new DateTimeImmutable();
|
||||
}
|
||||
}
|
||||
99
src/Entity/Question.php
Normal file
99
src/Entity/Question.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\QuestionRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
|
||||
use Symfony\Bridge\Doctrine\Types\UuidType;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
#[ORM\Entity(repositoryClass: QuestionRepository::class)]
|
||||
class Question
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: UuidType::NAME)]
|
||||
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
|
||||
#[ORM\CustomIdGenerator(class: UuidGenerator::class)]
|
||||
private ?Uuid $id = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: false)]
|
||||
private string $question;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'questions')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private Quiz $quiz;
|
||||
|
||||
#[ORM\Column]
|
||||
private bool $enabled = true;
|
||||
|
||||
/** @var Collection<int, Answer> */
|
||||
#[ORM\OneToMany(targetEntity: Answer::class, mappedBy: 'question', cascade: ['persist'], orphanRemoval: true)]
|
||||
private Collection $answers;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->answers = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?Uuid
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getQuestion(): string
|
||||
{
|
||||
return $this->question;
|
||||
}
|
||||
|
||||
public function setQuestion(string $question): static
|
||||
{
|
||||
$this->question = $question;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getQuiz(): Quiz
|
||||
{
|
||||
return $this->quiz;
|
||||
}
|
||||
|
||||
public function setQuiz(Quiz $quiz): static
|
||||
{
|
||||
$this->quiz = $quiz;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isEnabled(): ?bool
|
||||
{
|
||||
return $this->enabled;
|
||||
}
|
||||
|
||||
public function setEnabled(bool $enabled): static
|
||||
{
|
||||
$this->enabled = $enabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, Answer> */
|
||||
public function getAnswers(): Collection
|
||||
{
|
||||
return $this->answers;
|
||||
}
|
||||
|
||||
public function addAnswer(Answer $answer): static
|
||||
{
|
||||
if (!$this->answers->contains($answer)) {
|
||||
$this->answers->add($answer);
|
||||
$answer->setQuestion($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
105
src/Entity/Quiz.php
Normal file
105
src/Entity/Quiz.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\QuizRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
|
||||
use Symfony\Bridge\Doctrine\Types\UuidType;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
#[ORM\Entity(repositoryClass: QuizRepository::class)]
|
||||
class Quiz
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: UuidType::NAME)]
|
||||
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
|
||||
#[ORM\CustomIdGenerator(class: UuidGenerator::class)]
|
||||
private ?Uuid $id = null;
|
||||
|
||||
#[ORM\Column(length: 64)]
|
||||
private string $name;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'quizzes')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private Season $season;
|
||||
|
||||
/** @var Collection<int, Question> */
|
||||
#[ORM\OneToMany(targetEntity: Question::class, mappedBy: 'quiz', cascade: ['persist'], orphanRemoval: true)]
|
||||
private Collection $questions;
|
||||
|
||||
/** @var Collection<int, Correction> */
|
||||
#[ORM\OneToMany(targetEntity: Correction::class, mappedBy: 'quiz', orphanRemoval: true)]
|
||||
private Collection $corrections;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->questions = new ArrayCollection();
|
||||
$this->corrections = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?Uuid
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName(string $name): static
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSeason(): Season
|
||||
{
|
||||
return $this->season;
|
||||
}
|
||||
|
||||
public function setSeason(Season $season): static
|
||||
{
|
||||
$this->season = $season;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, Question> */
|
||||
public function getQuestions(): Collection
|
||||
{
|
||||
return $this->questions;
|
||||
}
|
||||
|
||||
public function addQuestion(Question $question): static
|
||||
{
|
||||
if (!$this->questions->contains($question)) {
|
||||
$this->questions->add($question);
|
||||
$question->setQuiz($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, Correction> */
|
||||
public function getCorrections(): Collection
|
||||
{
|
||||
return $this->corrections;
|
||||
}
|
||||
|
||||
public function addCorrection(Correction $correction): static
|
||||
{
|
||||
if (!$this->corrections->contains($correction)) {
|
||||
$this->corrections->add($correction);
|
||||
$correction->setQuiz($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
161
src/Entity/Season.php
Normal file
161
src/Entity/Season.php
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\SeasonRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
|
||||
use Symfony\Bridge\Doctrine\Types\UuidType;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
#[ORM\Entity(repositoryClass: SeasonRepository::class)]
|
||||
class Season
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: UuidType::NAME)]
|
||||
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
|
||||
#[ORM\CustomIdGenerator(class: UuidGenerator::class)]
|
||||
private ?Uuid $id = null;
|
||||
|
||||
#[ORM\Column(length: 64)]
|
||||
private string $name;
|
||||
|
||||
#[ORM\Column(length: 5)]
|
||||
private string $seasonCode;
|
||||
|
||||
#[ORM\Column]
|
||||
private bool $preregisterCandidates;
|
||||
|
||||
/** @var Collection<int, Quiz> */
|
||||
#[ORM\OneToMany(targetEntity: Quiz::class, mappedBy: 'season', orphanRemoval: true)]
|
||||
private Collection $quizzes;
|
||||
|
||||
/** @var Collection<int, Candidate> */
|
||||
#[ORM\OneToMany(targetEntity: Candidate::class, mappedBy: 'season', cascade: ['persist'], orphanRemoval: true)]
|
||||
private Collection $candidates;
|
||||
|
||||
/** @var Collection<int, User> */
|
||||
#[ORM\ManyToMany(targetEntity: User::class, inversedBy: 'seasons')]
|
||||
private Collection $owners;
|
||||
|
||||
#[ORM\ManyToOne]
|
||||
private ?Quiz $ActiveQuiz = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->quizzes = new ArrayCollection();
|
||||
$this->candidates = new ArrayCollection();
|
||||
$this->owners = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?Uuid
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName(string $name): static
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSeasonCode(): ?string
|
||||
{
|
||||
return $this->seasonCode;
|
||||
}
|
||||
|
||||
public function setSeasonCode(string $seasonCode): static
|
||||
{
|
||||
$this->seasonCode = $seasonCode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isPreregisterCandidates(): bool
|
||||
{
|
||||
return $this->preregisterCandidates;
|
||||
}
|
||||
|
||||
public function setPreregisterCandidates(bool $preregisterCandidates): static
|
||||
{
|
||||
$this->preregisterCandidates = $preregisterCandidates;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, Quiz> */
|
||||
public function getQuizzes(): Collection
|
||||
{
|
||||
return $this->quizzes;
|
||||
}
|
||||
|
||||
public function addQuiz(Quiz $quiz): static
|
||||
{
|
||||
if (!$this->quizzes->contains($quiz)) {
|
||||
$this->quizzes->add($quiz);
|
||||
$quiz->setSeason($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, Candidate> */
|
||||
public function getCandidates(): Collection
|
||||
{
|
||||
return $this->candidates;
|
||||
}
|
||||
|
||||
public function addCandidate(Candidate $candidate): static
|
||||
{
|
||||
if (!$this->candidates->contains($candidate)) {
|
||||
$this->candidates->add($candidate);
|
||||
$candidate->setSeason($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, User> */
|
||||
public function getOwners(): Collection
|
||||
{
|
||||
return $this->owners;
|
||||
}
|
||||
|
||||
public function addOwner(User $owner): static
|
||||
{
|
||||
if (!$this->owners->contains($owner)) {
|
||||
$this->owners->add($owner);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeOwner(User $owner): static
|
||||
{
|
||||
$this->owners->removeElement($owner);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getActiveQuiz(): ?Quiz
|
||||
{
|
||||
return $this->ActiveQuiz;
|
||||
}
|
||||
|
||||
public function setActiveQuiz(?Quiz $ActiveQuiz): static
|
||||
{
|
||||
$this->ActiveQuiz = $ActiveQuiz;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
143
src/Entity/User.php
Normal file
143
src/Entity/User.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\UserRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
|
||||
use Symfony\Bridge\Doctrine\Types\UuidType;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
#[ORM\Entity(repositoryClass: UserRepository::class)]
|
||||
#[ORM\Table(name: '`user`')]
|
||||
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_EMAIL', fields: ['email'])]
|
||||
class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: UuidType::NAME, unique: true)]
|
||||
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
|
||||
#[ORM\CustomIdGenerator(class: UuidGenerator::class)]
|
||||
private ?Uuid $id = null;
|
||||
|
||||
#[ORM\Column(length: 180)]
|
||||
private string $email;
|
||||
|
||||
/** @var list<string> The user roles */
|
||||
#[ORM\Column]
|
||||
private array $roles = [];
|
||||
|
||||
/** @var string The hashed password */
|
||||
#[ORM\Column]
|
||||
private string $password;
|
||||
|
||||
/** @var Collection<int, Season> */
|
||||
#[ORM\ManyToMany(targetEntity: Season::class, mappedBy: 'owners')]
|
||||
private Collection $seasons;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->seasons = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?Uuid
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getEmail(): ?string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function setEmail(string $email): static
|
||||
{
|
||||
$this->email = $email;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A visual identifier that represents this user.
|
||||
*
|
||||
* @see UserInterface
|
||||
*
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function getUserIdentifier(): string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UserInterface
|
||||
*
|
||||
* @return non-empty-list<string>
|
||||
*/
|
||||
public function getRoles(): array
|
||||
{
|
||||
$roles = $this->roles;
|
||||
// guarantee every user at least has ROLE_USER
|
||||
$roles[] = 'ROLE_USER';
|
||||
|
||||
return array_unique($roles);
|
||||
}
|
||||
|
||||
/** @param list<string> $roles */
|
||||
public function setRoles(array $roles): static
|
||||
{
|
||||
$this->roles = $roles;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @see PasswordAuthenticatedUserInterface */
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
public function setPassword(string $password): static
|
||||
{
|
||||
$this->password = $password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @see UserInterface */
|
||||
public function eraseCredentials(): void
|
||||
{
|
||||
// If you store any temporary, sensitive data on the user, clear it here
|
||||
// $this->plainPassword = null;
|
||||
}
|
||||
|
||||
/** @return Collection<int, Season> */
|
||||
public function getSeasons(): Collection
|
||||
{
|
||||
return $this->seasons;
|
||||
}
|
||||
|
||||
public function addSeason(Season $season): static
|
||||
{
|
||||
if (!$this->seasons->contains($season)) {
|
||||
$this->seasons->add($season);
|
||||
$season->addOwner($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeSeason(Season $season): static
|
||||
{
|
||||
if ($this->seasons->removeElement($season)) {
|
||||
$season->removeOwner($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
35
src/Form/EnterNameType.php
Normal file
35
src/Form/EnterNameType.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class EnterNameType extends AbstractType
|
||||
{
|
||||
public function __construct(private TranslatorInterface $translator)
|
||||
{
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('name', TextType::class,
|
||||
['required' => true, 'label' => $this->translator->trans('Enter your name')],
|
||||
)
|
||||
// ->add('submit', SubmitType::class, ['label' => 'Start quiz'])
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
// Configure your form options here
|
||||
]);
|
||||
}
|
||||
}
|
||||
31
src/Form/SelectSeasonType.php
Normal file
31
src/Form/SelectSeasonType.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\Regex;
|
||||
|
||||
class SelectSeasonType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('season_code', TextType::class,
|
||||
['required' => true, 'constraints' => new Regex(pattern: "/^[A-Za-z\d]{5}$/")],
|
||||
)
|
||||
// ->add('submit', SubmitType::class, ['label' => 'Start quiz'])
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
// Configure your form options here
|
||||
]);
|
||||
}
|
||||
}
|
||||
25
src/Helpers/Base64.php
Normal file
25
src/Helpers/Base64.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
use Safe\Exceptions\UrlException;
|
||||
|
||||
class Base64
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public static function base64_url_encode(string $input): string
|
||||
{
|
||||
return strtr(base64_encode($input), '+/=', '-_.');
|
||||
}
|
||||
|
||||
/** @throws UrlException */
|
||||
public static function base64_url_decode(string $input): string
|
||||
{
|
||||
return \Safe\base64_decode(strtr($input, '-_.', '+/='), true);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
|
||||
|
||||
20
src/Repository/AnswerRepository.php
Normal file
20
src/Repository/AnswerRepository.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Answer;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Answer>
|
||||
*/
|
||||
class AnswerRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Answer::class);
|
||||
}
|
||||
}
|
||||
34
src/Repository/CandidateRepository.php
Normal file
34
src/Repository/CandidateRepository.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Candidate;
|
||||
use App\Entity\Season;
|
||||
use App\Helpers\Base64;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Safe\Exceptions\UrlException;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Candidate>
|
||||
*/
|
||||
class CandidateRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Candidate::class);
|
||||
}
|
||||
|
||||
public function getCandidateByHash(Season $season, string $hash): ?Candidate
|
||||
{
|
||||
try {
|
||||
$name = Base64::base64_url_decode($hash);
|
||||
} catch (UrlException) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->findOneBy(['season' => $season, 'name' => $name]);
|
||||
}
|
||||
}
|
||||
20
src/Repository/CorrectionRepository.php
Normal file
20
src/Repository/CorrectionRepository.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Correction;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Correction>
|
||||
*/
|
||||
class CorrectionRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Correction::class);
|
||||
}
|
||||
}
|
||||
20
src/Repository/GivenAnswerRepository.php
Normal file
20
src/Repository/GivenAnswerRepository.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\GivenAnswer;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<GivenAnswer>
|
||||
*/
|
||||
class GivenAnswerRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, GivenAnswer::class);
|
||||
}
|
||||
}
|
||||
20
src/Repository/QuestionRepository.php
Normal file
20
src/Repository/QuestionRepository.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Question;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Question>
|
||||
*/
|
||||
class QuestionRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Question::class);
|
||||
}
|
||||
}
|
||||
20
src/Repository/QuizRepository.php
Normal file
20
src/Repository/QuizRepository.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Quiz;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Quiz>
|
||||
*/
|
||||
class QuizRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Quiz::class);
|
||||
}
|
||||
}
|
||||
20
src/Repository/SeasonRepository.php
Normal file
20
src/Repository/SeasonRepository.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Season;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Season>
|
||||
*/
|
||||
class SeasonRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Season::class);
|
||||
}
|
||||
}
|
||||
32
src/Repository/UserRepository.php
Normal file
32
src/Repository/UserRepository.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\User;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<User>
|
||||
*
|
||||
* @implements PasswordUpgraderInterface<User>
|
||||
*/
|
||||
class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, User::class);
|
||||
}
|
||||
|
||||
/** Used to upgrade (rehash) the user's password automatically over time. */
|
||||
public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void
|
||||
{
|
||||
$user->setPassword($newHashedPassword);
|
||||
$this->getEntityManager()->persist($user);
|
||||
$this->getEntityManager()->flush();
|
||||
}
|
||||
}
|
||||
108
symfony.lock
108
symfony.lock
@@ -13,6 +13,18 @@
|
||||
"src/Repository/.gitignore"
|
||||
]
|
||||
},
|
||||
"doctrine/doctrine-fixtures-bundle": {
|
||||
"version": "4.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "3.0",
|
||||
"ref": "1f5514cfa15b947298df4d771e694e578d4c204d"
|
||||
},
|
||||
"files": [
|
||||
"src/DataFixtures/AppFixtures.php"
|
||||
]
|
||||
},
|
||||
"doctrine/doctrine-migrations-bundle": {
|
||||
"version": "3.3",
|
||||
"recipe": {
|
||||
@@ -26,6 +38,15 @@
|
||||
"migrations/.gitignore"
|
||||
]
|
||||
},
|
||||
"easycorp/easyadmin-bundle": {
|
||||
"version": "4.23",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "3.0",
|
||||
"ref": "b131e6cbfe1b898a508987851963fff485986285"
|
||||
}
|
||||
},
|
||||
"friendsofphp/php-cs-fixer": {
|
||||
"version": "3.65",
|
||||
"recipe": {
|
||||
@@ -89,6 +110,18 @@
|
||||
".env.dev"
|
||||
]
|
||||
},
|
||||
"symfony/form": {
|
||||
"version": "7.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "7.2",
|
||||
"ref": "7d86a6723f4a623f59e2bf966b6aad2fc461d36b"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/csrf.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/framework-bundle": {
|
||||
"version": "7.2",
|
||||
"recipe": {
|
||||
@@ -130,6 +163,32 @@
|
||||
"config/routes.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/security-bundle": {
|
||||
"version": "7.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "6.4",
|
||||
"ref": "2ae08430db28c8eb4476605894296c82a642028f"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/security.yaml",
|
||||
"config/routes/security.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/translation": {
|
||||
"version": "7.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "6.3",
|
||||
"ref": "e28e27f53663cc34f0be2837aba18e3a1bef8e7b"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/translation.yaml",
|
||||
"translations/.gitignore"
|
||||
]
|
||||
},
|
||||
"symfony/twig-bundle": {
|
||||
"version": "7.2",
|
||||
"recipe": {
|
||||
@@ -142,5 +201,54 @@
|
||||
"config/packages/twig.yaml",
|
||||
"templates/base.html.twig"
|
||||
]
|
||||
},
|
||||
"symfony/uid": {
|
||||
"version": "7.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "7.0",
|
||||
"ref": "0df5844274d871b37fc3816c57a768ffc60a43a5"
|
||||
}
|
||||
},
|
||||
"symfony/ux-twig-component": {
|
||||
"version": "2.22",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "2.13",
|
||||
"ref": "67814b5f9794798b885cec9d3f48631424449a01"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/twig_component.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/validator": {
|
||||
"version": "7.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "7.0",
|
||||
"ref": "8c1c4e28d26a124b0bb273f537ca8ce443472bfd"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/validator.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/web-profiler-bundle": {
|
||||
"version": "7.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "6.1",
|
||||
"ref": "e42b3f0177df239add25373083a564e5ead4e13a"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/web_profiler.yaml",
|
||||
"config/routes/web_profiler.yaml"
|
||||
]
|
||||
},
|
||||
"twig/extra-bundle": {
|
||||
"version": "v3.18.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
<!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><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>">
|
||||
{% block stylesheets %}
|
||||
{% endblock %}
|
||||
<html lang="en">
|
||||
<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><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>">
|
||||
{% block stylesheets %}
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% block body %}{% endblock %}
|
||||
</body>
|
||||
{% block javascripts %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% block body %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
41
templates/login/login.html.twig
Normal file
41
templates/login/login.html.twig
Normal file
@@ -0,0 +1,41 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Log in!{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form method="post">
|
||||
{% if error %}
|
||||
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if app.user %}
|
||||
<div class="mb-3">
|
||||
You are logged in as {{ app.user.userIdentifier }}, <a href="{{ path('app_logout') }}">Logout</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
|
||||
<label for="username">Email</label>
|
||||
<input type="email" value="{{ last_username }}" name="_username" id="username" class="form-control" autocomplete="email" required autofocus>
|
||||
<label for="password">Password</label>
|
||||
<input type="password" name="_password" id="password" class="form-control" autocomplete="current-password" required>
|
||||
|
||||
<input type="hidden" name="_csrf_token"
|
||||
value="{{ csrf_token('authenticate') }}"
|
||||
>
|
||||
|
||||
{#
|
||||
Uncomment this section and add a remember_me option below your firewall to activate remember me functionality.
|
||||
See https://symfony.com/doc/current/security/remember_me.html
|
||||
|
||||
<div class="checkbox mb-3">
|
||||
<input type="checkbox" name="_remember_me" id="_remember_me">
|
||||
<label for="_remember_me">Remember me</label>
|
||||
</div>
|
||||
#}
|
||||
|
||||
<button class="btn btn-lg btn-primary" type="submit">
|
||||
Sign in
|
||||
</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
0
templates/messages.html.twig
Normal file
0
templates/messages.html.twig
Normal file
42
templates/quiz/base.html.twig
Normal file
42
templates/quiz/base.html.twig
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-bs-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
|
||||
crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
|
||||
crossorigin="anonymous"></script>
|
||||
<title>
|
||||
{% block title %}
|
||||
Tijd voor de test
|
||||
{% endblock title %}
|
||||
</title>
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
background-image: url("{{ asset('/img/background.png') }}");
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-color: black;
|
||||
color: white;
|
||||
|
||||
display: grid;
|
||||
align-items: center;
|
||||
justify-self: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="container">
|
||||
{% block body %}
|
||||
{% endblock body %}
|
||||
</div>
|
||||
{% block script %}
|
||||
{% endblock script %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
5
templates/quiz/enter_name.twig
Normal file
5
templates/quiz/enter_name.twig
Normal file
@@ -0,0 +1,5 @@
|
||||
{% extends "quiz/base.html.twig" %}
|
||||
{% block body %}
|
||||
{{ season.name }}
|
||||
{{ form(form) }}
|
||||
{% endblock body %}
|
||||
6
templates/quiz/question.twig
Normal file
6
templates/quiz/question.twig
Normal file
@@ -0,0 +1,6 @@
|
||||
{% extends "quiz/base.html.twig" %}
|
||||
{% block body %}
|
||||
{{ season.name }}
|
||||
{{ name }}
|
||||
<h1>Hello World!</h1>
|
||||
{% endblock body %}
|
||||
4
templates/quiz/select_season.html.twig
Normal file
4
templates/quiz/select_season.html.twig
Normal file
@@ -0,0 +1,4 @@
|
||||
{% extends "quiz/base.html.twig" %}
|
||||
{% block body %}
|
||||
{{ form(form) }}
|
||||
{% endblock body %}
|
||||
85
tests/LoginControllerTest.php
Normal file
85
tests/LoginControllerTest.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests;
|
||||
|
||||
use App\Entity\User;
|
||||
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
|
||||
final class LoginControllerTest extends WebTestCase
|
||||
{
|
||||
private KernelBrowser $client;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->client = static::createClient();
|
||||
$container = static::getContainer();
|
||||
$em = $container->get('doctrine.orm.entity_manager');
|
||||
$userRepository = $em->getRepository(User::class);
|
||||
|
||||
// Remove any existing users from the test database
|
||||
foreach ($userRepository->findAll() as $user) {
|
||||
$em->remove($user);
|
||||
}
|
||||
|
||||
$em->flush();
|
||||
|
||||
// Create a User fixture
|
||||
/** @var UserPasswordHasherInterface $passwordHasher */
|
||||
$passwordHasher = $container->get('security.user_password_hasher');
|
||||
|
||||
$user = (new User())->setEmail('email@example.com');
|
||||
$user->setPassword($passwordHasher->hashPassword($user, 'password'));
|
||||
|
||||
$em->persist($user);
|
||||
$em->flush();
|
||||
}
|
||||
|
||||
public function testLogin(): void
|
||||
{
|
||||
// Denied - Can't login with invalid email address.
|
||||
$this->client->request('GET', '/login');
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$this->client->submitForm('Sign in', [
|
||||
'_username' => 'doesNotExist@example.com',
|
||||
'_password' => 'password',
|
||||
]);
|
||||
|
||||
$this->assertResponseRedirects('/login');
|
||||
$this->client->followRedirect();
|
||||
|
||||
// Ensure we do not reveal if the user exists or not.
|
||||
$this->assertSelectorTextContains('.alert-danger', 'Invalid credentials.');
|
||||
|
||||
// Denied - Can't login with invalid password.
|
||||
$this->client->request('GET', '/login');
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$this->client->submitForm('Sign in', [
|
||||
'_username' => 'email@example.com',
|
||||
'_password' => 'bad-password',
|
||||
]);
|
||||
|
||||
$this->assertResponseRedirects('/login');
|
||||
$this->client->followRedirect();
|
||||
|
||||
// Ensure we do not reveal the user exists but the password is wrong.
|
||||
$this->assertSelectorTextContains('.alert-danger', 'Invalid credentials.');
|
||||
|
||||
// Success - Login with valid credentials is allowed.
|
||||
$this->client->submitForm('Sign in', [
|
||||
'_username' => 'email@example.com',
|
||||
'_password' => 'password',
|
||||
]);
|
||||
|
||||
$this->assertResponseRedirects('/');
|
||||
$this->client->followRedirect();
|
||||
|
||||
$this->assertSelectorNotExists('.alert-danger');
|
||||
$this->assertResponseIsSuccessful();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Symfony\Component\Dotenv\Dotenv;
|
||||
|
||||
require dirname(__DIR__).'/vendor/autoload.php';
|
||||
|
||||
if (method_exists(Dotenv::class, 'bootEnv')) {
|
||||
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
|
||||
}
|
||||
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
|
||||
|
||||
if ($_SERVER['APP_DEBUG']) {
|
||||
umask(0000);
|
||||
|
||||
0
translations/.gitignore
vendored
Normal file
0
translations/.gitignore
vendored
Normal file
14
translations/messages+intl-icu.nl.xlf
Normal file
14
translations/messages+intl-icu.nl.xlf
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<file source-language="nl" target-language="nl" datatype="plaintext" original="file.ext">
|
||||
<header>
|
||||
<tool tool-id="symfony" tool-name="Symfony"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id="RnI7jJT" resname="Enter your name">
|
||||
<source>Enter your name</source>
|
||||
<target>Voor je naam in</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
Reference in New Issue
Block a user