43 Commits

Author SHA1 Message Date
858aa36773 Make all entity classes final 2025-10-21 22:13:08 +02:00
5749f85337 Upgrade dependencies
Some checks failed
CI / Tests (push) Failing after 38s
CI / Build and deploy to ${{ startsWith(github.ref, 'refs/tags/') && 'production' || (github.ref == 'refs/heads/main' && 'acceptance' || '') }} (push) Has been skipped
2025-10-21 22:02:17 +02:00
311fbf607c Change Dependabot schedule to daily and add Docker 2025-10-21 20:51:11 +02:00
dependabot[bot]
9f08209d39 Bump rector/rector from 2.1.7 to 2.2.3
Some checks failed
CI / Tests (push) Failing after 41s
CI / Build and deploy to ${{ startsWith(github.ref, 'refs/tags/') && 'production' || (github.ref == 'refs/heads/main' && 'acceptance' || '') }} (push) Has been skipped
Bumps [rector/rector](https://github.com/rectorphp/rector) from 2.1.7 to 2.2.3.
- [Release notes](https://github.com/rectorphp/rector/releases)
- [Commits](https://github.com/rectorphp/rector/compare/2.1.7...2.2.3)

---
updated-dependencies:
- dependency-name: rector/rector
  dependency-version: 2.2.3
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-21 08:06:11 +02:00
dependabot[bot]
a1fc86a6a0 Bump doctrine/doctrine-fixtures-bundle from 4.1.0 to 4.3.0
Bumps [doctrine/doctrine-fixtures-bundle](https://github.com/doctrine/DoctrineFixturesBundle) from 4.1.0 to 4.3.0.
- [Release notes](https://github.com/doctrine/DoctrineFixturesBundle/releases)
- [Commits](https://github.com/doctrine/DoctrineFixturesBundle/compare/4.1.0...4.3.0)

---
updated-dependencies:
- dependency-name: doctrine/doctrine-fixtures-bundle
  dependency-version: 4.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-21 08:05:35 +02:00
dependabot[bot]
961c7434ac Bump phpstan/phpstan-doctrine from 2.0.6 to 2.0.10
Bumps [phpstan/phpstan-doctrine](https://github.com/phpstan/phpstan-doctrine) from 2.0.6 to 2.0.10.
- [Release notes](https://github.com/phpstan/phpstan-doctrine/releases)
- [Commits](https://github.com/phpstan/phpstan-doctrine/compare/2.0.6...2.0.10)

---
updated-dependencies:
- dependency-name: phpstan/phpstan-doctrine
  dependency-version: 2.0.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-21 08:04:57 +02:00
ee1a15ee78 New pipeline
Some checks failed
CI / Tests (push) Failing after 36s
CI / Build and deploy to ${{ startsWith(github.ref, 'refs/tags/') && 'production' || (github.ref == 'refs/heads/main' && 'acceptance' || '') }} (push) Has been skipped
2025-10-21 00:06:23 +02:00
253729abc0 Add completions 2025-10-20 21:41:46 +02:00
b66d2f9e86 Refactor entities and codebase for native property usage
Some checks failed
CI / Tests (push) Failing after 35s
CI / Deploy (push) Has been skipped
- Replaced getters/setters with direct property access across entities and repositories.
- Added and configured `martin-georgiev/postgresql-for-doctrine` for PostgreSQL enhancements.
- Updated Doctrine configuration with types, mappings, and JSONB query functions.
- Removed unused `EliminationService` and related YAML configurations.
2025-10-08 20:50:33 +02:00
ab187a28b9 Add Symfony container support for Rector configuration
- Introduced `tests/symfony-container.php` for bootstrapping Symfony.
  - Updated `rector.php` with new container PHP path and kernel modifications.
  - Registered Symfony routes provider service in Rector.
2025-10-08 20:50:33 +02:00
ca460cca7f - Refactor ORM annotations across entities for consistency.
- Adjusted migrations to align with refactored ORM annotations.
- Added new PHPStan and PHP-CS-Fixer configurations, including stricter rules.
- Introduced `tests/object-manager.php` to improve test environment setup.
2025-10-04 12:40:45 +02:00
3e39550c90 Remove EasyAdmin integration
- Removed EasyAdmin controllers and configurations.
- Uninstalled `easycorp/easyadmin-bundle` and related dependencies.
- Cleaned up all associated routes, bundles, and vendor references.
2025-10-03 22:37:45 +02:00
0319224979 Dependey updates 2025-09-28 21:19:01 +02:00
81e471a760 Change namespace to Tvdt 2025-09-28 18:14:58 +02:00
cfb69c8dab Add Deploy step 2025-09-08 21:51:00 +02:00
35ec71302b Update bake 2025-09-08 19:53:23 +02:00
b7a570928a Update dependencies 2025-09-08 19:50:23 +02:00
d80436534f Update Symfony packages 2025-09-08 19:26:45 +02:00
dependabot[bot]
14e2dd490e Bump phpstan/phpstan-symfony from 2.0.6 to 2.0.7
Bumps [phpstan/phpstan-symfony](https://github.com/phpstan/phpstan-symfony) from 2.0.6 to 2.0.7.
- [Release notes](https://github.com/phpstan/phpstan-symfony/releases)
- [Commits](https://github.com/phpstan/phpstan-symfony/compare/2.0.6...2.0.7)

---
updated-dependencies:
- dependency-name: phpstan/phpstan-symfony
  dependency-version: 2.0.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-25 13:30:05 +02:00
dependabot[bot]
68e54b1110 Bump doctrine/doctrine-bundle from 2.14.0 to 2.15.0
Bumps [doctrine/doctrine-bundle](https://github.com/doctrine/DoctrineBundle) from 2.14.0 to 2.15.0.
- [Release notes](https://github.com/doctrine/DoctrineBundle/releases)
- [Commits](https://github.com/doctrine/DoctrineBundle/compare/2.14.0...2.15.0)

---
updated-dependencies:
- dependency-name: doctrine/doctrine-bundle
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-25 13:29:46 +02:00
dependabot[bot]
e615b2cdea Bump symfony/serializer from 7.3.0 to 7.3.1
Bumps [symfony/serializer](https://github.com/symfony/serializer) from 7.3.0 to 7.3.1.
- [Release notes](https://github.com/symfony/serializer/releases)
- [Changelog](https://github.com/symfony/serializer/blob/7.3/CHANGELOG.md)
- [Commits](https://github.com/symfony/serializer/compare/v7.3.0...v7.3.1)

---
updated-dependencies:
- dependency-name: symfony/serializer
  dependency-version: 7.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-25 13:29:34 +02:00
dependabot[bot]
4b45a2c557 Bump symfony/security-bundle from 7.3.0 to 7.3.1
Bumps [symfony/security-bundle](https://github.com/symfony/security-bundle) from 7.3.0 to 7.3.1.
- [Release notes](https://github.com/symfony/security-bundle/releases)
- [Changelog](https://github.com/symfony/security-bundle/blob/7.3/CHANGELOG.md)
- [Commits](https://github.com/symfony/security-bundle/compare/v7.3.0...v7.3.1)

---
updated-dependencies:
- dependency-name: symfony/security-bundle
  dependency-version: 7.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-25 13:29:24 +02:00
c6fe553341 Create dependabot.yml 2025-07-25 13:04:20 +02:00
69a2b9c811 Open db port 2025-06-15 02:18:27 +02:00
f31a7d527d Fix scores? 2025-06-14 21:34:52 +02:00
ed3cf7644f Correct sorting for scores 2025-06-14 12:32:18 +02:00
77d21b004f Update backoffice templates to dynamically include titles and improve candidate handling in SeasonController 2025-06-14 12:32:18 +02:00
379fafcd16 Fix cs 2025-06-12 15:03:01 +02:00
7586d2d8ac Merge branch 'main' of github.com:MarijnDoeve/TijdVoorDeTest 2025-06-11 18:27:04 +02:00
9e41376244 Translations! 2025-06-11 18:26:17 +02:00
2bfef94bbe Add settings management for seasons, update templates, migrations, and commands 2025-06-10 23:06:59 +02:00
a8c4cba968 Disable Turbo for now 2025-06-09 15:52:15 +02:00
d5566d4737 Refactor repositories to use DQL queries, simplify logic, and enhance query efficiency 2025-06-09 14:19:10 +02:00
366bc36520 Fix typo 2025-06-07 23:47:58 +02:00
79b24b0d44 Refine error handling 2025-06-07 23:45:40 +02:00
7f93680987 Allow mail send fail 2025-06-07 23:43:52 +02:00
bdbff32256 Add MAILER_DSN 2025-06-07 23:34:50 +02:00
ebadc24b59 PHP 8.4 2025-06-07 22:24:38 +02:00
e0075fdcdc Rector upgrades 2025-06-07 22:24:38 +02:00
06aafefffc Upgrade to Symfony 7.3 2025-06-07 22:24:38 +02:00
ff6534fa81 Improve links 2025-06-07 22:24:38 +02:00
6a77df402d Add quiz clearing and deletion functionality with UI enhancements
This commit introduces the ability to clear quiz results and delete quizzes directly from the backoffice. It includes new routes, controllers, modals for user confirmation, and updates to translations. The `QuizRepository` now supports dedicated methods for clearing results and deleting quizzes along with error handling. Related database migrations and front-end adjustments are also included.
2025-06-07 20:59:01 +02:00
79236d84e9 Add correction management to backoffice, refactor security voter logic, and enhance candidate scoring
This commit introduces functionality to manage candidate corrections in the backoffice, with updated templates and a new route handler. The SeasonVoter is refactored to support additional entities, and scoring logic is updated to incorporate corrections consistently. Includes test coverage for voter logic and UI improvements for score tables.
2025-06-07 16:09:13 +02:00
147 changed files with 3793 additions and 3315 deletions

View File

@@ -1,6 +0,0 @@
{
"image": "mcr.microsoft.com/devcontainers/universal:2",
"features": {"ghcr.io/devcontainers/features/php:1": {
"version": "8.3"
}}
}

View File

@@ -35,6 +35,9 @@ indent_size = 4
trim_trailing_whitespace = false trim_trailing_whitespace = false
indent_size = 2 indent_size = 2
[config/**/*.{yaml,yml}]
indent_size = 4
[.github/workflows/*.yml] [.github/workflows/*.yml]
indent_size = 2 indent_size = 2

View File

@@ -1,4 +1,4 @@
# define your env variables for the test env here # define your env variables for the test env here
KERNEL_CLASS='App\Kernel' KERNEL_CLASS='Tvdt\Kernel'
APP_SECRET='$ecretf0rt3st' APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999 SYMFONY_DEPRECATIONS_HELPER=999999

15
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "composer" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: "docker"
directtory: "/"
schedule:
interval: "daily"

View File

@@ -4,6 +4,8 @@ on:
push: push:
branches: branches:
- main - main
tags:
- '*'
pull_request: ~ pull_request: ~
workflow_dispatch: ~ workflow_dispatch: ~
@@ -11,6 +13,10 @@ concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true cancel-in-progress: true
permissions:
contents: read
packages: write
jobs: jobs:
tests: tests:
name: Tests name: Tests
@@ -18,10 +24,12 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Lint Dockerfile
uses: hadolint/hadolint-action@v3.1.0
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Build Docker images - name: Build Docker images
uses: docker/bake-action@v4 uses: docker/bake-action@v5
with: with:
pull: true pull: true
load: true load: true
@@ -50,16 +58,65 @@ jobs:
- name: Run migrations - name: Run migrations
run: docker compose exec -T php bin/console -e test doctrine:migrations:migrate --no-interaction run: docker compose exec -T php bin/console -e test doctrine:migrations:migrate --no-interaction
- name: Run PHPUnit - name: Run PHPUnit
if: false # Remove this line when the tests are ready
run: docker compose exec -T php vendor/bin/phpunit run: docker compose exec -T php vendor/bin/phpunit
- name: Doctrine Schema Validator - name: Doctrine Schema Validator
run: docker compose exec -T php bin/console -e test doctrine:schema:validate run: docker compose exec -T php bin/console -e test doctrine:schema:validate
lint:
name: Docker Lint build-deploy:
name: Build and deploy to ${{ startsWith(github.ref, 'refs/tags/') && 'production' || (github.ref == 'refs/heads/main' && 'acceptance' || '') }}
environment:
name: ${{ startsWith(github.ref, 'refs/tags/') && 'production' || (github.ref == 'refs/heads/main' && 'acceptance' || '') }}
url: ${{ vars.URL }}
needs: tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: (github.ref == 'refs/heads/main' && false) || startsWith(github.ref, 'refs/tags/')
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Lint Dockerfile - name: Set up Docker Buildx
uses: hadolint/hadolint-action@v3.1.0 uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
run: |
REPO_LOWER=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
TAG="${GITHUB_REF#refs/tags/}"
{
echo "tag=$TAG"
echo "full_name=ghcr.io/${REPO_LOWER}:$TAG"
} >> "$GITHUB_OUTPUT"
else
SHORT_SHA=$(git rev-parse --short HEAD)
{
echo "tag=$SHORT_SHA"
echo "full_name=ghcr.io/${REPO_LOWER}:$SHORT_SHA"
} >> "$GITHUB_OUTPUT"
fi
- name: Build and Push Docker images
uses: docker/bake-action@v5
with:
pull: true
push: true
files: |
compose.yaml
compose.build.yaml
set: |
*.cache-from=type=gha,scope=${{github.ref}}
*.cache-from=type=gha,scope=refs/heads/main
*.cache-to=type=gha,scope=${{github.ref}},mode=max
*.tags=${{ steps.meta.outputs.full_name }}
- name: Trigger Portainer Deployment
shell: bash
env:
PORTAINER_WEBHOOK: ${{secrets.PORTAINER_WEBHOOK}}
run: |
curl -v -X POST "$PORTAINER_WEBHOOK"?IMAGE_TAG=${{steps.meta.outputs.tag}} --fail-with-body

View File

@@ -2,8 +2,8 @@
<module type="WEB_MODULE" version="4"> <module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="App\" /> <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="Tvdt\" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="App\Tests\" /> <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="Tvdt\Tests\" />
<excludeFolder url="file://$MODULE_DIR$/vendor/clue/ndjson-react" /> <excludeFolder url="file://$MODULE_DIR$/vendor/clue/ndjson-react" />
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" /> <excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/collections" /> <excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/collections" />
@@ -116,15 +116,12 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/thecodingmachine/phpstan-safe-rule" /> <excludeFolder url="file://$MODULE_DIR$/vendor/thecodingmachine/phpstan-safe-rule" />
<excludeFolder url="file://$MODULE_DIR$/vendor/thecodingmachine/safe" /> <excludeFolder url="file://$MODULE_DIR$/vendor/thecodingmachine/safe" />
<excludeFolder url="file://$MODULE_DIR$/vendor/twig/twig" /> <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/intl" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mime" /> <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/polyfill-intl-idn" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/translation" /> <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/symfony/validator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/twig/extra-bundle" /> <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/data-fixtures" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/doctrine-fixtures-bundle" /> <excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/doctrine-fixtures-bundle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/vincentlanglet/twig-cs-fixer" /> <excludeFolder url="file://$MODULE_DIR$/vendor/vincentlanglet/twig-cs-fixer" />
@@ -134,7 +131,6 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/browser-kit" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/browser-kit" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/dom-crawler" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/dom-crawler" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mailer" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mailer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php84" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfonycasts/verify-email-bundle" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfonycasts/verify-email-bundle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" /> <excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
<excludeFolder url="file://$MODULE_DIR$/vendor/jean85/pretty-package-versions" /> <excludeFolder url="file://$MODULE_DIR$/vendor/jean85/pretty-package-versions" />
@@ -166,6 +162,9 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/twig/intl-extra" /> <excludeFolder url="file://$MODULE_DIR$/vendor/twig/intl-extra" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/stimulus-bundle" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/stimulus-bundle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/ux-turbo" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/ux-turbo" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-uuid" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/brevo-mailer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/martin-georgiev/postgresql-for-doctrine" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />

320
.idea/php.xml generated
View File

@@ -7,11 +7,6 @@
</laravel_pint_by_interpreter> </laravel_pint_by_interpreter>
</laravel_pint_settings> </laravel_pint_settings>
</component> </component>
<component name="MessDetector">
<phpmd_settings>
<phpmd_by_interpreter asDefaultInterpreter="true" interpreter_id="96512cb2-7b9e-4e1d-bfa2-bf7f3be424c8" timeout="30000" />
</phpmd_settings>
</component>
<component name="MessDetectorOptionsConfiguration"> <component name="MessDetectorOptionsConfiguration">
<option name="transferred" value="true" /> <option name="transferred" value="true" />
</component> </component>
@@ -41,171 +36,170 @@
</component> </component>
<component name="PhpIncludePathManager"> <component name="PhpIncludePathManager">
<include_path> <include_path>
<path value="$PROJECT_DIR$/vendor/react/stream" />
<path value="$PROJECT_DIR$/vendor/react/promise" />
<path value="$PROJECT_DIR$/vendor/react/event-loop" />
<path value="$PROJECT_DIR$/vendor/react/cache" />
<path value="$PROJECT_DIR$/vendor/react/child-process" />
<path value="$PROJECT_DIR$/vendor/jean85/pretty-package-versions" />
<path value="$PROJECT_DIR$/vendor/react/dns" />
<path value="$PROJECT_DIR$/vendor/react/socket" />
<path value="$PROJECT_DIR$/vendor/sentry/sentry" />
<path value="$PROJECT_DIR$/vendor/staabm/side-effects-detector" />
<path value="$PROJECT_DIR$/vendor/rector/rector" />
<path value="$PROJECT_DIR$/vendor/sentry/sentry-symfony" />
<path value="$PROJECT_DIR$/vendor/phar-io/manifest" />
<path value="$PROJECT_DIR$/vendor/phar-io/version" />
<path value="$PROJECT_DIR$/vendor/egulias/email-validator" />
<path value="$PROJECT_DIR$/vendor/myclabs/deep-copy" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan-symfony" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan-phpunit" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan-doctrine" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-timer" />
<path value="$PROJECT_DIR$/vendor/phpunit/phpunit" />
<path value="$PROJECT_DIR$/vendor/phpstan/extension-installer" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpdoc-parser" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-invoker" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-file-iterator" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-text-template" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-code-coverage" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-icu" />
<path value="$PROJECT_DIR$/vendor/symfony/form" />
<path value="$PROJECT_DIR$/vendor/runtime/frankenphp-symfony" />
<path value="$PROJECT_DIR$/vendor/symfony/framework-bundle" />
<path value="$PROJECT_DIR$/vendor/symfony/validator" />
<path value="$PROJECT_DIR$/vendor/symfony/cache" />
<path value="$PROJECT_DIR$/vendor/symfony/serializer" />
<path value="$PROJECT_DIR$/vendor/symfony/dotenv" />
<path value="$PROJECT_DIR$/vendor/symfony/security-bundle" />
<path value="$PROJECT_DIR$/vendor/symfony/intl" />
<path value="$PROJECT_DIR$/vendor/symfony/asset" />
<path value="$PROJECT_DIR$/vendor/symfony/browser-kit" />
<path value="$PROJECT_DIR$/vendor/symfony/config" />
<path value="$PROJECT_DIR$/vendor/symfony/security-http" />
<path value="$PROJECT_DIR$/vendor/symfony/translation" />
<path value="$PROJECT_DIR$/vendor/symfony/translation-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher" />
<path value="$PROJECT_DIR$/vendor/symfony/runtime" />
<path value="$PROJECT_DIR$/vendor/symfony/uid" />
<path value="$PROJECT_DIR$/vendor/symfony/asset-mapper" />
<path value="$PROJECT_DIR$/vendor/symfony/mailer" />
<path value="$PROJECT_DIR$/vendor/symfony/filesystem" />
<path value="$PROJECT_DIR$/vendor/symfony/phpunit-bridge" />
<path value="$PROJECT_DIR$/vendor/symfony/var-dumper" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-idn" />
<path value="$PROJECT_DIR$/vendor/symfony/maker-bundle" />
<path value="$PROJECT_DIR$/vendor/symfony/options-resolver" />
<path value="$PROJECT_DIR$/vendor/symfony/security-csrf" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-normalizer" />
<path value="$PROJECT_DIR$/vendor/symfony/css-selector" />
<path value="$PROJECT_DIR$/vendor/symfony/twig-bridge" />
<path value="$PROJECT_DIR$/vendor/symfony/web-profiler-bundle" />
<path value="$PROJECT_DIR$/vendor/symfony/property-access" />
<path value="$PROJECT_DIR$/vendor/symfony/twig-bundle" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/dom-crawler" />
<path value="$PROJECT_DIR$/vendor/symfony/http-client" />
<path value="$PROJECT_DIR$/vendor/symfony/stopwatch" />
<path value="$PROJECT_DIR$/vendor/symfony/finder" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php84" />
<path value="$PROJECT_DIR$/vendor/symfony/yaml" />
<path value="$PROJECT_DIR$/vendor/symfony/mime" />
<path value="$PROJECT_DIR$/vendor/symfony/psr-http-message-bridge" />
<path value="$PROJECT_DIR$/vendor/symfony/http-kernel" />
<path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/routing" />
<path value="$PROJECT_DIR$/vendor/symfony/string" />
<path value="$PROJECT_DIR$/vendor/symfony/security-core" />
<path value="$PROJECT_DIR$/vendor/symfony/property-info" />
<path value="$PROJECT_DIR$/vendor/symfony/type-info" />
<path value="$PROJECT_DIR$/vendor/symfony/service-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/http-client-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/var-exporter" />
<path value="$PROJECT_DIR$/vendor/symfony/flex" />
<path value="$PROJECT_DIR$/vendor/symfony/http-foundation" />
<path value="$PROJECT_DIR$/vendor/symfony/doctrine-bridge" />
<path value="$PROJECT_DIR$/vendor/symfony/dependency-injection" />
<path value="$PROJECT_DIR$/vendor/symfony/clock" />
<path value="$PROJECT_DIR$/vendor/symfony/error-handler" />
<path value="$PROJECT_DIR$/vendor/symfony/cache-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/console" />
<path value="$PROJECT_DIR$/vendor/symfony/ux-twig-component" />
<path value="$PROJECT_DIR$/vendor/symfony/password-hasher" />
<path value="$PROJECT_DIR$/vendor/symfony/process" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-grapheme" />
<path value="$PROJECT_DIR$/vendor/theseer/tokenizer" />
<path value="$PROJECT_DIR$/vendor/doctrine/doctrine-migrations-bundle" />
<path value="$PROJECT_DIR$/vendor/doctrine/lexer" />
<path value="$PROJECT_DIR$/vendor/doctrine/dbal" />
<path value="$PROJECT_DIR$/vendor/doctrine/migrations" />
<path value="$PROJECT_DIR$/vendor/doctrine/data-fixtures" />
<path value="$PROJECT_DIR$/vendor/doctrine/doctrine-fixtures-bundle" />
<path value="$PROJECT_DIR$/vendor/doctrine/instantiator" />
<path value="$PROJECT_DIR$/vendor/doctrine/inflector" />
<path value="$PROJECT_DIR$/vendor/doctrine/persistence" />
<path value="$PROJECT_DIR$/vendor/doctrine/collections" />
<path value="$PROJECT_DIR$/vendor/doctrine/sql-formatter" />
<path value="$PROJECT_DIR$/vendor/doctrine/orm" />
<path value="$PROJECT_DIR$/vendor/doctrine/event-manager" />
<path value="$PROJECT_DIR$/vendor/easycorp/easyadmin-bundle" />
<path value="$PROJECT_DIR$/vendor/evenement/evenement" />
<path value="$PROJECT_DIR$/vendor/doctrine/deprecations" />
<path value="$PROJECT_DIR$/vendor/doctrine/doctrine-bundle" />
<path value="$PROJECT_DIR$/vendor/markbaker/matrix" />
<path value="$PROJECT_DIR$/vendor/phpoffice/phpspreadsheet" />
<path value="$PROJECT_DIR$/vendor/maennchen/zipstream-php" />
<path value="$PROJECT_DIR$/vendor/markbaker/complex" />
<path value="$PROJECT_DIR$/vendor/sebastian/global-state" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-enumerator" />
<path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" />
<path value="$PROJECT_DIR$/vendor/sebastian/comparator" />
<path value="$PROJECT_DIR$/vendor/sebastian/diff" />
<path value="$PROJECT_DIR$/vendor/sebastian/recursion-context" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-reflector" />
<path value="$PROJECT_DIR$/vendor/sebastian/complexity" />
<path value="$PROJECT_DIR$/vendor/sebastian/version" />
<path value="$PROJECT_DIR$/vendor/sebastian/cli-parser" />
<path value="$PROJECT_DIR$/vendor/sebastian/lines-of-code" />
<path value="$PROJECT_DIR$/vendor/sebastian/type" />
<path value="$PROJECT_DIR$/vendor/webmozart/assert" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
<path value="$PROJECT_DIR$/vendor/sebastian/environment" />
<path value="$PROJECT_DIR$/vendor/sebastian/exporter" />
<path value="$PROJECT_DIR$/vendor/symfonycasts/verify-email-bundle" />
<path value="$PROJECT_DIR$/vendor/symfonycasts/sass-bundle" />
<path value="$PROJECT_DIR$/vendor/masterminds/html5" />
<path value="$PROJECT_DIR$/vendor/friendsofphp/php-cs-fixer" />
<path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-docblock" /> <path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-docblock" />
<path value="$PROJECT_DIR$/vendor/vincentlanglet/twig-cs-fixer" />
<path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-common" />
<path value="$PROJECT_DIR$/vendor/phpdocumentor/type-resolver" /> <path value="$PROJECT_DIR$/vendor/phpdocumentor/type-resolver" />
<path value="$PROJECT_DIR$/vendor/thecodingmachine/safe" /> <path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-common" />
<path value="$PROJECT_DIR$/vendor/symfonycasts/sass-bundle" />
<path value="$PROJECT_DIR$/vendor/thecodingmachine/phpstan-safe-rule" /> <path value="$PROJECT_DIR$/vendor/thecodingmachine/phpstan-safe-rule" />
<path value="$PROJECT_DIR$/vendor/thecodingmachine/safe" />
<path value="$PROJECT_DIR$/vendor/martin-georgiev/postgresql-for-doctrine" />
<path value="$PROJECT_DIR$/vendor/vincentlanglet/twig-cs-fixer" />
<path value="$PROJECT_DIR$/vendor/webmozart/assert" />
<path value="$PROJECT_DIR$/vendor/sebastian/exporter" />
<path value="$PROJECT_DIR$/vendor/sebastian/environment" />
<path value="$PROJECT_DIR$/vendor/sebastian/cli-parser" />
<path value="$PROJECT_DIR$/vendor/symfonycasts/verify-email-bundle" />
<path value="$PROJECT_DIR$/vendor/friendsofphp/php-cs-fixer" />
<path value="$PROJECT_DIR$/vendor/masterminds/html5" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
<path value="$PROJECT_DIR$/vendor/composer" /> <path value="$PROJECT_DIR$/vendor/composer" />
<path value="$PROJECT_DIR$/vendor/psr/http-message" /> <path value="$PROJECT_DIR$/vendor/psr/http-message" />
<path value="$PROJECT_DIR$/vendor/psr/container" />
<path value="$PROJECT_DIR$/vendor/psr/cache" />
<path value="$PROJECT_DIR$/vendor/psr/event-dispatcher" /> <path value="$PROJECT_DIR$/vendor/psr/event-dispatcher" />
<path value="$PROJECT_DIR$/vendor/psr/simple-cache" /> <path value="$PROJECT_DIR$/vendor/psr/cache" />
<path value="$PROJECT_DIR$/vendor/psr/clock" />
<path value="$PROJECT_DIR$/vendor/psr/http-client" />
<path value="$PROJECT_DIR$/vendor/psr/log" />
<path value="$PROJECT_DIR$/vendor/twig/extra-bundle" />
<path value="$PROJECT_DIR$/vendor/twig/intl-extra" />
<path value="$PROJECT_DIR$/vendor/psr/http-factory" />
<path value="$PROJECT_DIR$/vendor/clue/ndjson-react" />
<path value="$PROJECT_DIR$/vendor/fidry/cpu-core-counter" />
<path value="$PROJECT_DIR$/vendor/nikic/php-parser" /> <path value="$PROJECT_DIR$/vendor/nikic/php-parser" />
<path value="$PROJECT_DIR$/vendor/twig/html-extra" /> <path value="$PROJECT_DIR$/vendor/fidry/cpu-core-counter" />
<path value="$PROJECT_DIR$/vendor/twig/twig" /> <path value="$PROJECT_DIR$/vendor/twig/twig" />
<path value="$PROJECT_DIR$/vendor/twig/intl-extra" />
<path value="$PROJECT_DIR$/vendor/react/promise" />
<path value="$PROJECT_DIR$/vendor/react/stream" />
<path value="$PROJECT_DIR$/vendor/react/cache" />
<path value="$PROJECT_DIR$/vendor/react/event-loop" />
<path value="$PROJECT_DIR$/vendor/psr/simple-cache" />
<path value="$PROJECT_DIR$/vendor/psr/log" />
<path value="$PROJECT_DIR$/vendor/psr/http-client" />
<path value="$PROJECT_DIR$/vendor/psr/container" />
<path value="$PROJECT_DIR$/vendor/twig/extra-bundle" />
<path value="$PROJECT_DIR$/vendor/clue/ndjson-react" />
<path value="$PROJECT_DIR$/vendor/psr/http-factory" />
<path value="$PROJECT_DIR$/vendor/psr/clock" />
<path value="$PROJECT_DIR$/vendor/phar-io/version" />
<path value="$PROJECT_DIR$/vendor/phar-io/manifest" />
<path value="$PROJECT_DIR$/vendor/myclabs/deep-copy" />
<path value="$PROJECT_DIR$/vendor/egulias/email-validator" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan-phpunit" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan-symfony" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan-doctrine" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpstan" />
<path value="$PROJECT_DIR$/vendor/jean85/pretty-package-versions" />
<path value="$PROJECT_DIR$/vendor/react/child-process" />
<path value="$PROJECT_DIR$/vendor/react/socket" />
<path value="$PROJECT_DIR$/vendor/react/dns" />
<path value="$PROJECT_DIR$/vendor/staabm/side-effects-detector" />
<path value="$PROJECT_DIR$/vendor/sentry/sentry" />
<path value="$PROJECT_DIR$/vendor/sentry/sentry-symfony" />
<path value="$PROJECT_DIR$/vendor/rector/rector" />
<path value="$PROJECT_DIR$/vendor/symfony/form" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-icu" />
<path value="$PROJECT_DIR$/vendor/symfony/framework-bundle" />
<path value="$PROJECT_DIR$/vendor/runtime/frankenphp-symfony" />
<path value="$PROJECT_DIR$/vendor/symfony/validator" />
<path value="$PROJECT_DIR$/vendor/symfony/dotenv" />
<path value="$PROJECT_DIR$/vendor/symfony/serializer" />
<path value="$PROJECT_DIR$/vendor/symfony/ux-turbo" /> <path value="$PROJECT_DIR$/vendor/symfony/ux-turbo" />
<path value="$PROJECT_DIR$/vendor/phpunit/phpunit" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-timer" />
<path value="$PROJECT_DIR$/vendor/phpstan/phpdoc-parser" />
<path value="$PROJECT_DIR$/vendor/phpstan/extension-installer" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-file-iterator" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-invoker" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-code-coverage" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-text-template" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher" />
<path value="$PROJECT_DIR$/vendor/symfony/asset-mapper" />
<path value="$PROJECT_DIR$/vendor/symfony/uid" />
<path value="$PROJECT_DIR$/vendor/symfony/security-http" />
<path value="$PROJECT_DIR$/vendor/symfony/brevo-mailer" />
<path value="$PROJECT_DIR$/vendor/symfony/var-dumper" />
<path value="$PROJECT_DIR$/vendor/symfony/phpunit-bridge" />
<path value="$PROJECT_DIR$/vendor/symfony/runtime" />
<path value="$PROJECT_DIR$/vendor/symfony/security-bundle" />
<path value="$PROJECT_DIR$/vendor/symfony/browser-kit" />
<path value="$PROJECT_DIR$/vendor/symfony/asset" />
<path value="$PROJECT_DIR$/vendor/symfony/cache" />
<path value="$PROJECT_DIR$/vendor/symfony/config" />
<path value="$PROJECT_DIR$/vendor/symfony/translation-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/translation" />
<path value="$PROJECT_DIR$/vendor/symfony/intl" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/css-selector" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-normalizer" />
<path value="$PROJECT_DIR$/vendor/symfony/web-profiler-bundle" />
<path value="$PROJECT_DIR$/vendor/symfony/finder" />
<path value="$PROJECT_DIR$/vendor/symfony/twig-bundle" />
<path value="$PROJECT_DIR$/vendor/symfony/property-access" />
<path value="$PROJECT_DIR$/vendor/symfony/dom-crawler" />
<path value="$PROJECT_DIR$/vendor/symfony/security-csrf" />
<path value="$PROJECT_DIR$/vendor/symfony/options-resolver" />
<path value="$PROJECT_DIR$/vendor/symfony/filesystem" />
<path value="$PROJECT_DIR$/vendor/symfony/mailer" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-uuid" />
<path value="$PROJECT_DIR$/vendor/symfony/twig-bridge" />
<path value="$PROJECT_DIR$/vendor/symfony/maker-bundle" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-idn" />
<path value="$PROJECT_DIR$/vendor/symfony/http-client-contracts" />
<path value="$PROJECT_DIR$/vendor/doctrine/event-manager" />
<path value="$PROJECT_DIR$/vendor/symfony/service-contracts" />
<path value="$PROJECT_DIR$/vendor/doctrine/orm" />
<path value="$PROJECT_DIR$/vendor/symfony/routing" />
<path value="$PROJECT_DIR$/vendor/doctrine/instantiator" />
<path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
<path value="$PROJECT_DIR$/vendor/doctrine/doctrine-fixtures-bundle" />
<path value="$PROJECT_DIR$/vendor/symfony/doctrine-bridge" />
<path value="$PROJECT_DIR$/vendor/doctrine/doctrine-bundle" />
<path value="$PROJECT_DIR$/vendor/symfony/http-foundation" />
<path value="$PROJECT_DIR$/vendor/doctrine/deprecations" />
<path value="$PROJECT_DIR$/vendor/symfony/type-info" />
<path value="$PROJECT_DIR$/vendor/doctrine/sql-formatter" />
<path value="$PROJECT_DIR$/vendor/symfony/property-info" />
<path value="$PROJECT_DIR$/vendor/doctrine/collections" />
<path value="$PROJECT_DIR$/vendor/symfony/http-kernel" />
<path value="$PROJECT_DIR$/vendor/doctrine/data-fixtures" />
<path value="$PROJECT_DIR$/vendor/symfony/psr-http-message-bridge" />
<path value="$PROJECT_DIR$/vendor/doctrine/migrations" />
<path value="$PROJECT_DIR$/vendor/symfony/stopwatch" />
<path value="$PROJECT_DIR$/vendor/doctrine/doctrine-migrations-bundle" />
<path value="$PROJECT_DIR$/vendor/symfony/http-client" />
<path value="$PROJECT_DIR$/vendor/symfony/security-core" />
<path value="$PROJECT_DIR$/vendor/doctrine/persistence" />
<path value="$PROJECT_DIR$/vendor/symfony/string" />
<path value="$PROJECT_DIR$/vendor/doctrine/inflector" />
<path value="$PROJECT_DIR$/vendor/symfony/mime" />
<path value="$PROJECT_DIR$/vendor/doctrine/dbal" />
<path value="$PROJECT_DIR$/vendor/symfony/yaml" />
<path value="$PROJECT_DIR$/vendor/doctrine/lexer" />
<path value="$PROJECT_DIR$/vendor/theseer/tokenizer" />
<path value="$PROJECT_DIR$/vendor/sebastian/diff" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-grapheme" />
<path value="$PROJECT_DIR$/vendor/sebastian/complexity" />
<path value="$PROJECT_DIR$/vendor/symfony/console" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-reflector" />
<path value="$PROJECT_DIR$/vendor/symfony/process" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-enumerator" />
<path value="$PROJECT_DIR$/vendor/sebastian/version" />
<path value="$PROJECT_DIR$/vendor/sebastian/type" />
<path value="$PROJECT_DIR$/vendor/sebastian/lines-of-code" />
<path value="$PROJECT_DIR$/vendor/sebastian/recursion-context" />
<path value="$PROJECT_DIR$/vendor/symfony/cache-contracts" />
<path value="$PROJECT_DIR$/vendor/markbaker/matrix" />
<path value="$PROJECT_DIR$/vendor/symfony/error-handler" />
<path value="$PROJECT_DIR$/vendor/markbaker/complex" />
<path value="$PROJECT_DIR$/vendor/symfony/flex" />
<path value="$PROJECT_DIR$/vendor/maennchen/zipstream-php" />
<path value="$PROJECT_DIR$/vendor/symfony/var-exporter" />
<path value="$PROJECT_DIR$/vendor/evenement/evenement" />
<path value="$PROJECT_DIR$/vendor/symfony/password-hasher" />
<path value="$PROJECT_DIR$/vendor/sebastian/global-state" />
<path value="$PROJECT_DIR$/vendor/symfony/clock" />
<path value="$PROJECT_DIR$/vendor/sebastian/comparator" />
<path value="$PROJECT_DIR$/vendor/symfony/stimulus-bundle" /> <path value="$PROJECT_DIR$/vendor/symfony/stimulus-bundle" />
<path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" />
<path value="$PROJECT_DIR$/vendor/symfony/dependency-injection" />
<path value="$PROJECT_DIR$/vendor/phpoffice/phpspreadsheet" />
</include_path> </include_path>
</component> </component>
<component name="PhpInterpreters"> <component name="PhpInterpreters">
<interpreters> <interpreters>
<interpreter id="96512cb2-7b9e-4e1d-bfa2-bf7f3be424c8" name="Compose PHP 8.3" home="docker-compose://DATA" auto="false" debugger_id="php.debugger.XDebug"> <interpreter id="96512cb2-7b9e-4e1d-bfa2-bf7f3be424c8" name="Compose PHP 8.4" home="docker-compose://DATA" auto="false" debugger_id="php.debugger.XDebug">
<remote_data INTERPRETER_PATH="php" HELPERS_PATH="/opt/.phpstorm_helpers" VALID="true" RUN_AS_ROOT_VIA_SUDO="false" DOCKER_ACCOUNT_NAME="Colima" DOCKER_COMPOSE_SERVICE_NAME="php" DOCKER_REMOTE_PROJECT_PATH="/opt/project"> <remote_data INTERPRETER_PATH="php" HELPERS_PATH="/opt/.phpstorm_helpers" VALID="true" RUN_AS_ROOT_VIA_SUDO="false" DOCKER_ACCOUNT_NAME="Colima" DOCKER_COMPOSE_SERVICE_NAME="php" DOCKER_REMOTE_PROJECT_PATH="/opt/project">
<type_data command="EXEC" /> <type_data command="EXEC" />
<dockerComposeConfigurationPaths> <dockerComposeConfigurationPaths>
@@ -219,15 +213,15 @@
</component> </component>
<component name="PhpInterpretersPhpInfoCache"> <component name="PhpInterpretersPhpInfoCache">
<phpInfoCache> <phpInfoCache>
<interpreter name="Compose PHP 8.3"> <interpreter name="Compose PHP 8.4">
<phpinfo binary_type="PHP" php_cgi="/usr/local/bin/php-cgi" php_cli="/usr/local/bin/php" path_separator=":" version="8.3.19"> <phpinfo binary_type="PHP" php_cgi="/usr/local/bin/php-cgi" php_cli="/usr/local/bin/php" path_separator=":" version="8.4.8">
<additional_php_ini>/usr/local/etc/php/conf.d/docker-php-ext-apcu.ini, /usr/local/etc/php/conf.d/docker-php-ext-intl.ini, /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini, /usr/local/etc/php/conf.d/docker-php-ext-pdo_pgsql.ini, /usr/local/etc/php/conf.d/docker-php-ext-sodium.ini, /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini, /usr/local/etc/php/conf.d/docker-php-ext-zip.ini, /usr/local/etc/php/app.conf.d/10-app.ini, /usr/local/etc/php/app.conf.d/20-app.dev.ini</additional_php_ini> <additional_php_ini>/usr/local/etc/php/conf.d/docker-php-ext-apcu.ini, /usr/local/etc/php/conf.d/docker-php-ext-excimer.ini, /usr/local/etc/php/conf.d/docker-php-ext-gd.ini, /usr/local/etc/php/conf.d/docker-php-ext-intl.ini, /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini, /usr/local/etc/php/conf.d/docker-php-ext-pdo_pgsql.ini, /usr/local/etc/php/conf.d/docker-php-ext-sodium.ini, /usr/local/etc/php/conf.d/docker-php-ext-uuid.ini, /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini, /usr/local/etc/php/conf.d/docker-php-ext-zip.ini, /usr/local/etc/php/app.conf.d/10-app.ini, /usr/local/etc/php/app.conf.d/20-app.dev.ini</additional_php_ini>
<configuration_file>/usr/local/etc/php/php.ini</configuration_file> <configuration_file>/usr/local/etc/php/php.ini</configuration_file>
<configuration_options> <configuration_options>
<configuration_option name="include_path" value=".:/usr/local/lib/php" /> <configuration_option name="include_path" value=".:/usr/local/lib/php" />
</configuration_options> </configuration_options>
<debuggers> <debuggers>
<debugger_info debugger="xdebug" debugger_version="3.4.2"> <debugger_info debugger="xdebug" debugger_version="3.4.3">
<debug_extensions /> <debug_extensions />
</debugger_info> </debugger_info>
</debuggers> </debuggers>
@@ -244,8 +238,10 @@
<extension name="curl" /> <extension name="curl" />
<extension name="date" /> <extension name="date" />
<extension name="dom" /> <extension name="dom" />
<extension name="excimer" />
<extension name="fileinfo" /> <extension name="fileinfo" />
<extension name="filter" /> <extension name="filter" />
<extension name="gd" />
<extension name="hash" /> <extension name="hash" />
<extension name="iconv" /> <extension name="iconv" />
<extension name="intl" /> <extension name="intl" />
@@ -265,6 +261,7 @@
<extension name="sqlite3" /> <extension name="sqlite3" />
<extension name="standard" /> <extension name="standard" />
<extension name="tokenizer" /> <extension name="tokenizer" />
<extension name="uuid" />
<extension name="xdebug" /> <extension name="xdebug" />
<extension name="xml" /> <extension name="xml" />
<extension name="xmlreader" /> <extension name="xmlreader" />
@@ -276,7 +273,7 @@
</interpreter> </interpreter>
</phpInfoCache> </phpInfoCache>
</component> </component>
<component name="PhpProjectSharedConfiguration" php_language_level="8.3" /> <component name="PhpProjectSharedConfiguration" php_language_level="8.4" />
<component name="PhpStan"> <component name="PhpStan">
<PhpStan_settings> <PhpStan_settings>
<phpstan_by_interpreter interpreter_id="96512cb2-7b9e-4e1d-bfa2-bf7f3be424c8" tool_path="vendor/bin/phpstan" timeout="60000" /> <phpstan_by_interpreter interpreter_id="96512cb2-7b9e-4e1d-bfa2-bf7f3be424c8" tool_path="vendor/bin/phpstan" timeout="60000" />
@@ -292,6 +289,7 @@
<component name="PhpUnit"> <component name="PhpUnit">
<phpunit_settings> <phpunit_settings>
<phpunit_by_interpreter interpreter_id="96512cb2-7b9e-4e1d-bfa2-bf7f3be424c8" configuration_file_path="phpunit.xml.dist" custom_loader_path="vendor/autoload.php" phpunit_phar_path="" use_configuration_file="true" /> <phpunit_by_interpreter interpreter_id="96512cb2-7b9e-4e1d-bfa2-bf7f3be424c8" configuration_file_path="phpunit.xml.dist" custom_loader_path="vendor/autoload.php" phpunit_phar_path="" use_configuration_file="true" />
<PhpUnitSettings custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" />
</phpunit_settings> </phpunit_settings>
</component> </component>
<component name="Psalm"> <component name="Psalm">

1
.idea/symfony2.xml generated
View File

@@ -2,5 +2,6 @@
<project version="4"> <project version="4">
<component name="Symfony2PluginSettings"> <component name="Symfony2PluginSettings">
<option name="pluginEnabled" value="true" /> <option name="pluginEnabled" value="true" />
<option name="profilerCsvPath" value="" />
</component> </component>
</project> </project>

View File

@@ -5,13 +5,13 @@ use PhpCsFixer\Config;
use PhpCsFixer\Finder; use PhpCsFixer\Finder;
use PhpCsFixer\Runner\Parallel\ParallelConfigFactory; use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;
$finder = (new Finder()) $finder = new Finder()
->in(__DIR__) ->in(__DIR__)
->exclude('var') ->exclude('var')
->exclude('bin') ->exclude('bin')
; ;
return (new Config()) return new Config()
->setParallelConfig(ParallelConfigFactory::detect()) ->setParallelConfig(ParallelConfigFactory::detect())
->setRules([ ->setRules([
'@Symfony' => true, '@Symfony' => true,
@@ -30,8 +30,9 @@ return (new Config())
'single_line_empty_body' => true, 'single_line_empty_body' => true,
'strict_comparison' => true, 'strict_comparison' => true,
'strict_param' => true, 'strict_param' => true,
'ordered_attributes' => true,
'heredoc_indentation' => ['indentation' => 'start_plus_one'],
'trailing_comma_in_multiline' => ['after_heredoc' => true, 'elements' => ['arguments', 'array_destructuring', 'arrays', 'match', 'parameters']], 'trailing_comma_in_multiline' => ['after_heredoc' => true, 'elements' => ['arguments', 'array_destructuring', 'arrays', 'match', 'parameters']],
]) ])
->setRiskyAllowed(true) ->setRiskyAllowed(true)
->setFinder($finder) ->setFinder($finder)

View File

@@ -1,7 +1,7 @@
#syntax=docker/dockerfile:1 #syntax=docker/dockerfile:1
# Versions # Versions
FROM dunglas/frankenphp:1-php8.3 AS frankenphp_upstream FROM dunglas/frankenphp:1-php8.4 AS frankenphp_upstream
# The different stages of this Dockerfile are meant to be built into separate images # The different stages of this Dockerfile are meant to be built into separate images
# https://docs.docker.com/develop/develop-images/multistage-build/#stop-at-a-specific-build-stage # https://docs.docker.com/develop/develop-images/multistage-build/#stop-at-a-specific-build-stage
@@ -22,7 +22,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
file \ file \
gettext \ gettext \
git \ git \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
RUN set -eux; \ RUN set -eux; \
install-php-extensions \ install-php-extensions \
@@ -31,9 +31,8 @@ RUN set -eux; \
intl \ intl \
opcache \ opcache \
zip \ zip \
uuid \
gd \ gd \
excimer-1.2.3 \ excimer \
; ;
# https://getcomposer.org/doc/03-cli.md#composer-allow-superuser # https://getcomposer.org/doc/03-cli.md#composer-allow-superuser
@@ -61,6 +60,14 @@ FROM frankenphp_base AS frankenphp_dev
ENV APP_ENV=dev XDEBUG_MODE=off ENV APP_ENV=dev XDEBUG_MODE=off
# hadolint ignore=DL3008
RUN apt-get update && apt-get install -y --no-install-recommends \
bash-completion \
&& rm -rf /var/lib/apt/lists/*
COPY --link frankenphp/console-complete.bash /usr/share/bash-completion/completions/console
COPY --link frankenphp/composer-complete.bash /usr/share/bash-completion/completions/composer
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini" RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
RUN set -eux; \ RUN set -eux; \
@@ -77,6 +84,8 @@ CMD [ "frankenphp", "run", "--config", "/etc/caddy/Caddyfile", "--watch" ]
# Prod FrankenPHP image # Prod FrankenPHP image
FROM frankenphp_base AS frankenphp_prod FROM frankenphp_base AS frankenphp_prod
RUN rm -rf /var/lib/apt/lists/*
ENV APP_ENV=prod ENV APP_ENV=prod
ENV FRANKENPHP_CONFIG="import worker.Caddyfile" ENV FRANKENPHP_CONFIG="import worker.Caddyfile"

View File

@@ -1,8 +1,8 @@
up: up *args:
docker compose up -d docker compose up -d {{ args }}
down *args: down *args:
docker compose down {{ args }} --remove-orphans docker compose down --remove-orphans {{ args }}
stop: stop:
docker compose stop docker compose stop

View File

@@ -1,5 +1,5 @@
import * as bootstrap from 'bootstrap'
import './bootstrap.js'; import './bootstrap.js';
import 'bootstrap/dist/css/bootstrap.min.css' import 'bootstrap/dist/css/bootstrap.min.css'
import * as bootstrap from 'bootstrap'
import './styles/backoffice.scss'; import './styles/backoffice.scss';

View File

@@ -2,7 +2,7 @@
"controllers": { "controllers": {
"@symfony/ux-turbo": { "@symfony/ux-turbo": {
"turbo-core": { "turbo-core": {
"enabled": true, "enabled": false,
"fetch": "eager" "fetch": "eager"
}, },
"mercure-turbo-stream": { "mercure-turbo-stream": {

View File

@@ -0,0 +1,17 @@
import {Controller} from '@hotwired/stimulus';
import * as bootstrap from 'bootstrap'
export default class extends Controller {
connect() {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
}
clearQuiz() {
new bootstrap.Modal('#clearQuizModal').show();
}
deleteQuiz() {
new bootstrap.Modal('#deleteQuizModal').show();
}
}

View File

@@ -3,7 +3,7 @@
declare(strict_types=1); declare(strict_types=1);
use App\Kernel; use Tvdt\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Console\Application;
if (!is_dir(dirname(__DIR__).'/vendor')) { if (!is_dir(dirname(__DIR__).'/vendor')) {

5
compose.build.yaml Normal file
View File

@@ -0,0 +1,5 @@
services:
php:
build:
context: .
target: frankenphp_prod

View File

@@ -1,13 +1,12 @@
# Production environment override # Production environment override
services: services:
php: php:
build: image: ghcr.io/marijndoeve/tijdvoordetest:${IMAGE_TAG}
context: .
target: frankenphp_prod
environment: environment:
APP_SECRET: ${APP_SECRET} APP_SECRET: ${APP_SECRET}
MERCURE_PUBLISHER_JWT_KEY: ${CADDY_MERCURE_JWT_SECRET} MERCURE_PUBLISHER_JWT_KEY: ${CADDY_MERCURE_JWT_SECRET}
MERCURE_SUBSCRIBER_JWT_KEY: ${CADDY_MERCURE_JWT_SECRET} MERCURE_SUBSCRIBER_JWT_KEY: ${CADDY_MERCURE_JWT_SECRET}
MAILER_DSN: ${MAILER_DSN}
MAILER_SENDER: ${MAILER_SENDER} MAILER_SENDER: ${MAILER_SENDER}
SENTRY_DSN: ${SENTRY_DSN} SENTRY_DSN: ${SENTRY_DSN}
labels: labels:
@@ -23,6 +22,8 @@ services:
database: database:
networks: networks:
- internal - internal
ports:
- "5430:5432"
networks: networks:
web: web:
external: true external: true

View File

@@ -6,63 +6,66 @@
"minimum-stability": "stable", "minimum-stability": "stable",
"prefer-stable": true, "prefer-stable": true,
"require": { "require": {
"php": ">=8.3.15", "php": ">=8.4",
"ext-ctype": "*", "ext-ctype": "*",
"ext-iconv": "*", "ext-iconv": "*",
"doctrine/dbal": "^4.2.3", "doctrine/dbal": "^4.3.4",
"doctrine/doctrine-bundle": "^2.14.0", "doctrine/doctrine-bundle": "^3.0",
"doctrine/doctrine-migrations-bundle": "^3.4.2", "doctrine/doctrine-migrations-bundle": "^3.5.0",
"doctrine/orm": "^3.3.3", "doctrine/orm": "^3.5.2",
"easycorp/easyadmin-bundle": "^4.24.7", "martin-georgiev/postgresql-for-doctrine": "^3.5.1",
"phpdocumentor/reflection-docblock": "^5.6.2", "phpdocumentor/reflection-docblock": "^5.6.3",
"phpoffice/phpspreadsheet": "^4.2.0", "phpoffice/phpspreadsheet": "^5.1",
"phpstan/phpdoc-parser": "^2.1", "phpstan/phpdoc-parser": "^2.3",
"runtime/frankenphp-symfony": "^0.2.0", "runtime/frankenphp-symfony": "^0.2.0",
"sentry/sentry-symfony": "^5.2", "sentry/sentry-symfony": "^5.6",
"symfony/asset": "7.2.*", "symfony/asset": "7.3.*",
"symfony/asset-mapper": "7.2.*", "symfony/asset-mapper": "7.3.*",
"symfony/console": "7.2.*", "symfony/brevo-mailer": "7.3.*",
"symfony/dotenv": "7.2.*", "symfony/console": "7.3.*",
"symfony/flex": "^2.7.0", "symfony/dotenv": "7.3.*",
"symfony/form": "7.2.*", "symfony/flex": "^2.8.2",
"symfony/framework-bundle": "7.2.*", "symfony/form": "7.3.*",
"symfony/mailer": "7.2.*", "symfony/framework-bundle": "7.3.*",
"symfony/property-access": "7.2.*", "symfony/mailer": "7.3.*",
"symfony/property-info": "7.2.*", "symfony/property-access": "7.3.*",
"symfony/runtime": "7.2.*", "symfony/property-info": "7.3.*",
"symfony/security-bundle": "7.2.*", "symfony/runtime": "7.3.*",
"symfony/security-csrf": "7.2.*", "symfony/security-bundle": "7.3.*",
"symfony/serializer": "7.2.*", "symfony/security-csrf": "7.3.*",
"symfony/twig-bundle": "7.2.*", "symfony/serializer": "7.3.*",
"symfony/uid": "7.2.*", "symfony/translation": "7.3.*",
"symfony/ux-turbo": "^2.26", "symfony/twig-bundle": "7.3.*",
"symfony/yaml": "7.2.*", "symfony/uid": "7.3.*",
"symfonycasts/sass-bundle": "^0.8.2", "symfony/ux-turbo": "^2.30.0",
"symfonycasts/verify-email-bundle": "^1.17.3", "symfony/validator": "7.3.*",
"symfony/yaml": "7.3.*",
"symfonycasts/sass-bundle": "^0.8.3",
"symfonycasts/verify-email-bundle": "^1.17.4",
"thecodingmachine/safe": "^3.3.0", "thecodingmachine/safe": "^3.3.0",
"twig/extra-bundle": "^3.21", "twig/extra-bundle": "^3.21",
"twig/intl-extra": "^3.21", "twig/intl-extra": "^3.21",
"twig/twig": "^3.21.1" "twig/twig": "^3.21.1"
}, },
"require-dev": { "require-dev": {
"doctrine/doctrine-fixtures-bundle": "^4.1", "doctrine/doctrine-fixtures-bundle": "^4.3",
"friendsofphp/php-cs-fixer": "^3.75.0", "friendsofphp/php-cs-fixer": "^3.89.0",
"phpstan/extension-installer": "^1.4.3", "phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan": "^2.1.17", "phpstan/phpstan": "^2.1.31",
"phpstan/phpstan-doctrine": "^2.0.3", "phpstan/phpstan-doctrine": "^2.0.10",
"phpstan/phpstan-phpunit": "^2.0.6", "phpstan/phpstan-phpunit": "^2.0.7",
"phpstan/phpstan-symfony": "^2.0.6", "phpstan/phpstan-symfony": "^2.0.8",
"phpunit/phpunit": "^12.1.6", "phpunit/phpunit": "^12.4.1",
"rector/rector": "^2.0.16", "rector/rector": "^2.2.3",
"roave/security-advisories": "dev-latest", "roave/security-advisories": "dev-latest",
"symfony/browser-kit": "7.2.*", "symfony/browser-kit": "7.3.*",
"symfony/css-selector": "7.2.*", "symfony/css-selector": "7.3.*",
"symfony/maker-bundle": "^1.63.0", "symfony/maker-bundle": "^1.64.0",
"symfony/phpunit-bridge": "7.2.*", "symfony/phpunit-bridge": "7.3.*",
"symfony/stopwatch": "7.2.*", "symfony/stopwatch": "7.3.*",
"symfony/web-profiler-bundle": "7.2.*", "symfony/web-profiler-bundle": "7.3.*",
"thecodingmachine/phpstan-safe-rule": "^1.4.1", "thecodingmachine/phpstan-safe-rule": "^1.4.1",
"vincentlanglet/twig-cs-fixer": "^3.7.1" "vincentlanglet/twig-cs-fixer": "^3.10.0"
}, },
"config": { "config": {
"allow-plugins": { "allow-plugins": {
@@ -76,12 +79,12 @@
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"App\\": "src/" "Tvdt\\": "src/"
} }
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"App\\Tests\\": "tests/" "Tvdt\\Tests\\": "tests/"
} }
}, },
"replace": { "replace": {
@@ -95,7 +98,7 @@
"symfony/polyfill-php81": "*", "symfony/polyfill-php81": "*",
"symfony/polyfill-php82": "*", "symfony/polyfill-php82": "*",
"symfony/polyfill-php83": "*", "symfony/polyfill-php83": "*",
"symfony/polyfill-uuid": "*" "symfony/polyfill-php84": "*"
}, },
"scripts": { "scripts": {
"auto-scripts": { "auto-scripts": {
@@ -116,7 +119,7 @@
"extra": { "extra": {
"symfony": { "symfony": {
"allow-contrib": false, "allow-contrib": false,
"require": "7.2.*", "require": "7.3.*",
"docker": true "docker": true
} }
} }

2525
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,6 @@ declare(strict_types=1);
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
use Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle; use Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle;
use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle; use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle;
use EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle;
use Sentry\SentryBundle\SentryBundle; use Sentry\SentryBundle\SentryBundle;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\MakerBundle\MakerBundle; use Symfony\Bundle\MakerBundle\MakerBundle;
@@ -14,7 +13,6 @@ use Symfony\Bundle\TwigBundle\TwigBundle;
use Symfony\Bundle\WebProfilerBundle\WebProfilerBundle; use Symfony\Bundle\WebProfilerBundle\WebProfilerBundle;
use Symfony\UX\StimulusBundle\StimulusBundle; use Symfony\UX\StimulusBundle\StimulusBundle;
use Symfony\UX\Turbo\TurboBundle; use Symfony\UX\Turbo\TurboBundle;
use Symfony\UX\TwigComponent\TwigComponentBundle;
use SymfonyCasts\Bundle\VerifyEmail\SymfonyCastsVerifyEmailBundle; use SymfonyCasts\Bundle\VerifyEmail\SymfonyCastsVerifyEmailBundle;
use Symfonycasts\SassBundle\SymfonycastsSassBundle; use Symfonycasts\SassBundle\SymfonycastsSassBundle;
use Twig\Extra\TwigExtraBundle\TwigExtraBundle; use Twig\Extra\TwigExtraBundle\TwigExtraBundle;
@@ -28,8 +26,6 @@ return [
SecurityBundle::class => ['all' => true], SecurityBundle::class => ['all' => true],
WebProfilerBundle::class => ['dev' => true, 'test' => true], WebProfilerBundle::class => ['dev' => true, 'test' => true],
TwigExtraBundle::class => ['all' => true], TwigExtraBundle::class => ['all' => true],
TwigComponentBundle::class => ['all' => true],
EasyAdminBundle::class => ['all' => true],
DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
SymfonyCastsVerifyEmailBundle::class => ['all' => true], SymfonyCastsVerifyEmailBundle::class => ['all' => true],
SentryBundle::class => ['prod' => true], SentryBundle::class => ['prod' => true],

View File

@@ -1,15 +1,15 @@
framework: framework:
asset_mapper: asset_mapper:
# The paths to make available to the asset mapper. # The paths to make available to the asset mapper.
paths: paths:
- assets/ - assets/
excluded_patterns: excluded_patterns:
- '*/assets/styles/_*.scss' - '*/assets/styles/_*.scss'
- '*/assets/styles/**/_*.scss' - '*/assets/styles/**/_*.scss'
missing_import_mode: strict missing_import_mode: strict
when@prod: when@prod:
framework: framework:
asset_mapper: asset_mapper:
missing_import_mode: warn missing_import_mode: warn

View File

@@ -1,52 +1,60 @@
doctrine: doctrine:
dbal: dbal:
url: '%env(resolve:DATABASE_URL)%' url: '%env(resolve:DATABASE_URL)%'
profiling_collect_backtrace: '%kernel.debug%'
types:
# JSON types
jsonb: MartinGeorgiev\Doctrine\DBAL\Types\Jsonb
'jsonb[]': MartinGeorgiev\Doctrine\DBAL\Types\JsonbArray
tsrange: MartinGeorgiev\Doctrine\DBAL\Types\TsRange
mapping_types:
# JSON type mappings
jsonb: jsonb
'jsonb[]': 'jsonb[]'
_jsonb: 'jsonb[]'
tsrange: tsrange
# IMPORTANT: You MUST configure your server version,
# either here or in the DATABASE_URL env var (see .env file)
#server_version: '16'
profiling_collect_backtrace: '%kernel.debug%' orm:
use_savepoints: true enable_native_lazy_objects: true
orm: validate_xml_mapping: true
auto_generate_proxy_classes: true naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
enable_lazy_ghost_objects: true identity_generation_preferences:
report_fields_where_declared: true Doctrine\DBAL\Platforms\PostgreSQLPlatform: identity
validate_xml_mapping: true auto_mapping: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware mappings:
identity_generation_preferences: Tvdt:
Doctrine\DBAL\Platforms\PostgreSQLPlatform: identity type: attribute
auto_mapping: true is_bundle: false
mappings: dir: '%kernel.project_dir%/src/Entity'
App: prefix: 'Tvdt\Entity'
type: attribute alias: Tvdt
is_bundle: false controller_resolver:
dir: '%kernel.project_dir%/src/Entity' auto_mapping: false
prefix: 'App\Entity'
alias: App
when@test: when@test:
doctrine: doctrine:
dbal: dbal:
# "TEST_TOKEN" is typically set by ParaTest # "TEST_TOKEN" is typically set by ParaTest
dbname_suffix: '_test%env(default::TEST_TOKEN)%' dbname_suffix: '_test%env(default::TEST_TOKEN)%'
when@prod: when@prod:
doctrine: doctrine:
orm: orm:
auto_generate_proxy_classes: false auto_generate_proxy_classes: false
proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies'
query_cache_driver: query_cache_driver:
type: pool type: pool
pool: doctrine.system_cache_pool pool: doctrine.system_cache_pool
result_cache_driver: result_cache_driver:
type: pool type: pool
pool: doctrine.result_cache_pool pool: doctrine.result_cache_pool
framework: framework:
cache: cache:
pools: pools:
doctrine.result_cache_pool: doctrine.result_cache_pool:
adapter: cache.app adapter: cache.app
doctrine.system_cache_pool: doctrine.system_cache_pool:
adapter: cache.system adapter: cache.system

View File

@@ -1,6 +1,6 @@
doctrine_migrations: doctrine_migrations:
migrations_paths: migrations_paths:
# namespace is arbitrary but should be different from App\Migrations # namespace is arbitrary but should be different from Tvdt\Migrations
# as migrations classes should NOT be autoloaded # as migrations classes should NOT be autoloaded
'DoctrineMigrations': '%kernel.project_dir%/migrations' 'DoctrineMigrations': '%kernel.project_dir%/migrations'
enable_profiler: false enable_profiler: false

View File

@@ -1,23 +1,23 @@
# see https://symfony.com/doc/current/reference/configuration/framework.html # see https://symfony.com/doc/current/reference/configuration/framework.html
framework: framework:
secret: '%env(APP_SECRET)%' secret: '%env(APP_SECRET)%'
# Note that the session will be started ONLY if you read or write from it. # Note that the session will be started ONLY if you read or write from it.
session: true session: true
form: form:
csrf_protection: csrf_protection:
enabled: true enabled: true
#esi: true #esi: true
#fragments: true #fragments: true
when@prod: when@prod:
framework: framework:
# shortcut for private IP address ranges of your proxy # shortcut for private IP address ranges of your proxy
trusted_proxies: 'private_ranges' trusted_proxies: 'private_ranges'
# or, if your proxy instead uses the "Forwarded" header # or, if your proxy instead uses the "Forwarded" header
trusted_headers: [ 'forwarded' ] trusted_headers: [ 'forwarded' ]
when@test: when@test:
framework: framework:
test: true test: true
session: session:
storage_factory_id: session.storage.factory.mock_file storage_factory_id: session.storage.factory.mock_file

View File

@@ -1,7 +1,7 @@
framework: framework:
mailer: mailer:
dsn: '%env(MAILER_DSN)%' dsn: '%env(MAILER_DSN)%'
envelope: envelope:
sender: '%env(MAILER_SENDER)%' sender: '%env(MAILER_SENDER)%'
headers: headers:
From: 'Tijd voor de test <%env(MAILER_SENDER)%>' From: 'Tijd voor de test <%env(MAILER_SENDER)%>'

View File

@@ -0,0 +1,3 @@
framework:
property_info:
with_constructor_extractor: true

View File

@@ -1,10 +1,10 @@
framework: framework:
router: router:
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands. # Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
#default_uri: http://localhost #default_uri: http://localhost
when@prod: when@prod:
framework: framework:
router: router:
strict_requirements: null strict_requirements: null

View File

@@ -1,53 +1,53 @@
security: security:
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers: password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers: providers:
# used to reload user from session & other features (e.g. switch_user) # used to reload user from session & other features (e.g. switch_user)
app_user_provider: tvdt_user_provider:
entity: entity:
class: App\Entity\User class: Tvdt\Entity\User
property: email property: email
# used to reload user from session & other features (e.g. switch_user) # used to reload user from session & other features (e.g. switch_user)
firewalls: firewalls:
dev: dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/ pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false security: false
main: main:
lazy: true lazy: true
provider: app_user_provider provider: tvdt_user_provider
form_login: form_login:
login_path: app_login_login login_path: tvdt_login_login
check_path: app_login_login check_path: tvdt_login_login
enable_csrf: true enable_csrf: true
default_target_path: app_backoffice_index default_target_path: tvdt_backoffice_index
logout: logout:
path: app_login_logout path: tvdt_login_logout
# where to redirect after logout # where to redirect after logout
# target: app_any_route # target: tvdt_any_route
# activate different ways to authenticate # activate different ways to authenticate
# https://symfony.com/doc/current/security.html#the-firewall # https://symfony.com/doc/current/security.html#the-firewall
# https://symfony.com/doc/current/security/impersonating_user.html # https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true # switch_user: true
# Easy way to control access for large sections of your site # Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used # Note: Only the *first* access control that matches will be used
access_control: access_control:
- { path: ^/admin, roles: ROLE_ADMIN } - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER } - { path: ^/backoffice, roles: ROLE_USER }
when@test: when@test:
security: security:
password_hashers: password_hashers:
# By default, password hashers are resource intensive and take time. This is # By default, password hashers are resource intensive and take time. This is
# important to generate secure password hashes. In tests however, secure hashes # important to generate secure password hashes. In tests however, secure hashes
# are not important, waste resources and increase test times. The following # are not important, waste resources and increase test times. The following
# reduces the work factor to the lowest possible values. # reduces the work factor to the lowest possible values.
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
algorithm: auto algorithm: auto
cost: 4 # Lowest possible value for bcrypt cost: 4 # Lowest possible value for bcrypt
time_cost: 3 # Lowest possible value for argon time_cost: 3 # Lowest possible value for argon
memory_cost: 10 # Lowest possible value for argon memory_cost: 10 # Lowest possible value for argon

View File

@@ -1,16 +1,16 @@
when@prod: when@prod:
sentry: sentry:
dsn: '%env(SENTRY_DSN)%' dsn: '%env(SENTRY_DSN)%'
# Add request headers, cookies, IP address and the authenticated user # Add request headers, cookies, IP address and the authenticated user
# see https://docs.sentry.io/platforms/php/data-management/data-collected/ for more info # see https://docs.sentry.io/platforms/php/data-management/data-collected/ for more info
# send_default_pii: true # send_default_pii: true
options: options:
traces_sample_rate: 1.0 traces_sample_rate: 1.0
profiles_sample_rate: 1.0 profiles_sample_rate: 1.0
ignore_exceptions: ignore_exceptions:
- 'Symfony\Component\ErrorHandler\Error\FatalError' - 'Symfony\Component\ErrorHandler\Error\FatalError'
- 'Symfony\Component\Debug\Exception\FatalErrorException' - 'Symfony\Component\Debug\Exception\FatalErrorException'
# If you are using Monolog, you also need this additional configuration to log the errors correctly: # If you are using Monolog, you also need this additional configuration to log the errors correctly:
# https://docs.sentry.io/platforms/php/guides/symfony/#monolog-integration # https://docs.sentry.io/platforms/php/guides/symfony/#monolog-integration

View File

@@ -1,7 +1,7 @@
framework: framework:
default_locale: nl default_locale: nl
translator: translator:
default_path: '%kernel.project_dir%/translations' default_path: '%kernel.project_dir%/translations'
fallbacks: fallbacks:
- en - en
providers: providers:

View File

@@ -1,7 +1,7 @@
twig: twig:
file_name_pattern: '*.twig' file_name_pattern: '*.twig'
form_themes: [ 'bootstrap_5_layout.html.twig' ] form_themes: [ 'bootstrap_5_layout.html.twig' ]
when@test: when@test:
twig: twig:
strict_variables: true strict_variables: true

View File

@@ -1,5 +0,0 @@
twig_component:
anonymous_template_directory: 'components/'
defaults:
# Namespace & directory for components
App\Twig\Components\: 'components/'

View File

@@ -1,4 +0,0 @@
framework:
uid:
default_uuid_version: 7
time_based_uuid_version: 7

View File

@@ -1,11 +1,11 @@
framework: framework:
validation: validation:
# Enables validator auto-mapping support. # Enables validator auto-mapping support.
# For instance, basic validation constraints will be inferred from Doctrine's metadata. # For instance, basic validation constraints will be inferred from Doctrine's metadata.
#auto_mapping: #auto_mapping:
# App\Entity\: [] # Tvdt\Entity\: []
when@test: when@test:
framework: framework:
validation: validation:
not_compromised_password: false not_compromised_password: false

View File

@@ -1,17 +1,17 @@
when@dev: when@dev:
web_profiler: web_profiler:
toolbar: true toolbar: true
intercept_redirects: false intercept_redirects: false
framework: framework:
profiler: profiler:
only_exceptions: false only_exceptions: false
collect_serializer_data: true collect_serializer_data: true
when@test: when@test:
web_profiler: web_profiler:
toolbar: false toolbar: false
intercept_redirects: false intercept_redirects: false
framework: framework:
profiler: { collect: false } profiler: { collect: false }

View File

@@ -1,5 +1,5 @@
controllers: controllers:
resource: resource:
path: ../src/Controller/ path: ../src/Controller/
namespace: App\Controller namespace: Tvdt\Controller
type: attribute type: attribute

View File

@@ -1,9 +1,9 @@
controllers: controllers:
resource: resource:
path: ../../src/Controller/ path: ../../src/Controller/
namespace: App\Controller namespace: Tvdt\Controller
type: attribute type: attribute
kernel: kernel:
resource: App\Kernel resource: Tvdt\Kernel
type: attribute type: attribute

View File

@@ -1,3 +0,0 @@
easyadmin:
resource: .
type: easyadmin.routes

View File

@@ -1,4 +1,4 @@
when@dev: when@dev:
_errors: _errors:
resource: '@FrameworkBundle/Resources/config/routing/errors.xml' resource: '@FrameworkBundle/Resources/config/routing/errors.php'
prefix: /_error prefix: /_error

View File

@@ -1,3 +1,3 @@
_security_logout: _security_logout:
resource: security.route_loader.logout resource: security.route_loader.logout
type: service type: service

View File

@@ -1,8 +1,8 @@
when@dev: when@dev:
web_profiler_wdt: web_profiler_wdt:
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml' resource: '@WebProfilerBundle/Resources/config/routing/wdt.php'
prefix: /_wdt prefix: /_wdt
web_profiler_profiler: web_profiler_profiler:
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml' resource: '@WebProfilerBundle/Resources/config/routing/profiler.php'
prefix: /_profiler prefix: /_profiler

View File

@@ -6,19 +6,19 @@
parameters: parameters:
services: services:
# default configuration for services in *this* file # default configuration for services in *this* file
_defaults: _defaults:
autowire: true # Automatically injects dependencies in your services. autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services # makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name # this creates a service per class whose id is the fully-qualified class name
App\: Tvdt\:
resource: '../src/' resource: '../src/'
exclude: exclude:
- '../src/DependencyInjection/' - '../src/DependencyInjection/'
- '../src/Entity/' - '../src/Entity/'
- '../src/Kernel.php' - '../src/Kernel.php'
# add more service definitions when explicit configuration is needed # add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones # please note that last definitions always *replace* previous ones

View File

@@ -0,0 +1,84 @@
# This file is part of the Symfony package.
#
# (c) Fabien Potencier <fabien@symfony.com>
#
# For the full copyright and license information, please view
# https://symfony.com/doc/current/contributing/code/license.html
_sf_composer() {
# Use newline as only separator to allow space in completion values
local IFS=$'\n'
local sf_cmd="${COMP_WORDS[0]}"
# for an alias, get the real script behind it
sf_cmd_type=$(type -t $sf_cmd)
if [[ $sf_cmd_type == "alias" ]]; then
sf_cmd=$(alias $sf_cmd | sed -E "s/alias $sf_cmd='(.*)'/\1/")
elif [[ $sf_cmd_type == "file" ]]; then
sf_cmd=$(type -p $sf_cmd)
fi
if [[ $sf_cmd_type != "function" && ! -x $sf_cmd ]]; then
return 1
fi
local cur prev words cword
_get_comp_words_by_ref -n := cur prev words cword
local completecmd=("$sf_cmd" "_complete" "--no-interaction" "-sbash" "-c$cword" "-S2.8.11")
for w in ${words[@]}; do
w=$(printf -- '%b' "$w")
# remove quotes from typed values
quote="${w:0:1}"
if [ "$quote" == \' ]; then
w="${w%\'}"
w="${w#\'}"
elif [ "$quote" == \" ]; then
w="${w%\"}"
w="${w#\"}"
fi
# empty values are ignored
if [ ! -z "$w" ]; then
completecmd+=("-i$w")
fi
done
local sfcomplete
if sfcomplete=$(${completecmd[@]} 2>&1); then
local quote suggestions
quote=${cur:0:1}
# Use single quotes by default if suggestions contains backslash (FQCN)
if [ "$quote" == '' ] && [[ "$sfcomplete" =~ \\ ]]; then
quote=\'
fi
if [ "$quote" == \' ]; then
# single quotes: no additional escaping (does not accept ' in values)
suggestions=$(for s in $sfcomplete; do printf $'%q%q%q\n' "$quote" "$s" "$quote"; done)
elif [ "$quote" == \" ]; then
# double quotes: double escaping for \ $ ` "
suggestions=$(for s in $sfcomplete; do
s=${s//\\/\\\\}
s=${s//\$/\\\$}
s=${s//\`/\\\`}
s=${s//\"/\\\"}
printf $'%q%q%q\n' "$quote" "$s" "$quote";
done)
else
# no quotes: double escaping
suggestions=$(for s in $sfcomplete; do printf $'%q\n' $(printf '%q' "$s"); done)
fi
COMPREPLY=($(IFS=$'\n' compgen -W "$suggestions" -- $(printf -- "%q" "$cur")))
__ltrim_colon_completions "$cur"
else
if [[ "$sfcomplete" != *"Command \"_complete\" is not defined."* ]]; then
>&2 echo
>&2 echo $sfcomplete
fi
return 1
fi
}
complete -F _sf_composer composer

View File

@@ -0,0 +1,94 @@
# This file is part of the Symfony package.
#
# (c) Fabien Potencier <fabien@symfony.com>
#
# For the full copyright and license information, please view
# https://symfony.com/doc/current/contributing/code/license.html
_sf_console() {
# Use the default completion for shell redirect operators.
for w in '>' '>>' '&>' '<'; do
if [[ $w = "${COMP_WORDS[COMP_CWORD-1]}" ]]; then
compopt -o filenames
COMPREPLY=($(compgen -f -- "${COMP_WORDS[COMP_CWORD]}"))
return 0
fi
done
# Use newline as only separator to allow space in completion values
local IFS=$'\n'
local sf_cmd="${COMP_WORDS[0]}"
# for an alias, get the real script behind it
sf_cmd_type=$(type -t $sf_cmd)
if [[ $sf_cmd_type == "alias" ]]; then
sf_cmd=$(alias $sf_cmd | sed -E "s/alias $sf_cmd='(.*)'/\1/")
elif [[ $sf_cmd_type == "file" ]]; then
sf_cmd=$(type -p $sf_cmd)
fi
if [[ $sf_cmd_type != "function" && ! -x $sf_cmd ]]; then
return 1
fi
local cur prev words cword
_get_comp_words_by_ref -n := cur prev words cword
local completecmd=("$sf_cmd" "_complete" "--no-interaction" "-sbash" "-c$cword" "-a1")
for w in ${words[@]}; do
w="${w//\\\\/\\}"
# remove quotes from typed values
quote="${w:0:1}"
if [ "$quote" == \' ]; then
w="${w%\'}"
w="${w#\'}"
elif [ "$quote" == \" ]; then
w="${w%\"}"
w="${w#\"}"
fi
# empty values are ignored
if [ ! -z "$w" ]; then
completecmd+=("-i$w")
fi
done
local sfcomplete
if sfcomplete=$(${completecmd[@]} 2>&1); then
local quote suggestions
quote=${cur:0:1}
# Use single quotes by default if suggestions contains backslash (FQCN)
if [ "$quote" == '' ] && [[ "$sfcomplete" =~ \\ ]]; then
quote=\'
fi
if [ "$quote" == \' ]; then
# single quotes: no additional escaping (does not accept ' in values)
suggestions=$(for s in $sfcomplete; do printf $'%q%q%q\n' "$quote" "$s" "$quote"; done)
elif [ "$quote" == \" ]; then
# double quotes: double escaping for \ $ ` "
suggestions=$(for s in $sfcomplete; do
s=${s//\\/\\\\}
s=${s//\$/\\\$}
s=${s//\`/\\\`}
s=${s//\"/\\\"}
printf $'%q%q%q\n' "$quote" "$s" "$quote";
done)
else
# no quotes: double escaping
suggestions=$(for s in $sfcomplete; do printf $'%q\n' $(printf '%q' "$s"); done)
fi
COMPREPLY=($(IFS=$'\n' compgen -W "$suggestions" -- $(printf -- "%q" "$cur")))
__ltrim_colon_completions "$cur"
else
if [[ "$sfcomplete" != *"Command \"_complete\" is not defined."* ]]; then
>&2 echo
>&2 echo $sfcomplete
fi
return 1
fi
}
complete -F _sf_console console

View File

@@ -7,9 +7,6 @@ namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241229195702 extends AbstractMigration final class Version20241229195702 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
@@ -19,7 +16,6 @@ final class Version20241229195702 extends AbstractMigration
public function up(Schema $schema): void 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 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('CREATE INDEX IDX_DADD4A251E27F6BF ON answer (question_id)');
$this->addSql('COMMENT ON COLUMN answer.id IS \'(DC2Type:uuid)\''); $this->addSql('COMMENT ON COLUMN answer.id IS \'(DC2Type:uuid)\'');
@@ -67,7 +63,6 @@ final class Version20241229195702 extends AbstractMigration
public function down(Schema $schema): void 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('CREATE SCHEMA public');
$this->addSql('ALTER TABLE answer DROP CONSTRAINT FK_DADD4A251E27F6BF'); $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_F54D5192AA334807');

View File

@@ -7,9 +7,6 @@ namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241229201314 extends AbstractMigration final class Version20241229201314 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
@@ -19,7 +16,6 @@ final class Version20241229201314 extends AbstractMigration
public function up(Schema $schema): void 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 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_BDA4AD74EC001D1 ON season_user (season_id)');
$this->addSql('CREATE INDEX IDX_BDA4AD7A76ED395 ON season_user (user_id)'); $this->addSql('CREATE INDEX IDX_BDA4AD7A76ED395 ON season_user (user_id)');
@@ -31,7 +27,6 @@ final class Version20241229201314 extends AbstractMigration
public function down(Schema $schema): void 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('CREATE SCHEMA public');
$this->addSql('ALTER TABLE season_user DROP CONSTRAINT FK_BDA4AD74EC001D1'); $this->addSql('ALTER TABLE season_user DROP CONSTRAINT FK_BDA4AD74EC001D1');
$this->addSql('ALTER TABLE season_user DROP CONSTRAINT FK_BDA4AD7A76ED395'); $this->addSql('ALTER TABLE season_user DROP CONSTRAINT FK_BDA4AD7A76ED395');

View File

@@ -7,9 +7,6 @@ namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241229202103 extends AbstractMigration final class Version20241229202103 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
@@ -19,7 +16,6 @@ final class Version20241229202103 extends AbstractMigration
public function up(Schema $schema): void 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 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_A29DA1B891BD8781 ON correction (candidate_id)');
$this->addSql('CREATE INDEX IDX_A29DA1B8853CD175 ON correction (quiz_id)'); $this->addSql('CREATE INDEX IDX_A29DA1B8853CD175 ON correction (quiz_id)');
@@ -32,7 +28,6 @@ final class Version20241229202103 extends AbstractMigration
public function down(Schema $schema): void 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('CREATE SCHEMA public');
$this->addSql('ALTER TABLE correction DROP CONSTRAINT FK_A29DA1B891BD8781'); $this->addSql('ALTER TABLE correction DROP CONSTRAINT FK_A29DA1B891BD8781');
$this->addSql('ALTER TABLE correction DROP CONSTRAINT FK_A29DA1B8853CD175'); $this->addSql('ALTER TABLE correction DROP CONSTRAINT FK_A29DA1B8853CD175');

View File

@@ -7,9 +7,6 @@ namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241229202155 extends AbstractMigration final class Version20241229202155 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
@@ -19,13 +16,11 @@ final class Version20241229202155 extends AbstractMigration
public function up(Schema $schema): void 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)'); $this->addSql('CREATE UNIQUE INDEX UNIQ_A29DA1B891BD8781853CD175 ON correction (candidate_id, quiz_id)');
} }
public function down(Schema $schema): void 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('CREATE SCHEMA public');
$this->addSql('DROP INDEX UNIQ_A29DA1B891BD8781853CD175'); $this->addSql('DROP INDEX UNIQ_A29DA1B891BD8781853CD175');
} }

View File

@@ -7,9 +7,6 @@ namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241229204335 extends AbstractMigration final class Version20241229204335 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
@@ -19,14 +16,12 @@ final class Version20241229204335 extends AbstractMigration
public function up(Schema $schema): void 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 answer_id DROP NOT NULL');
$this->addSql('ALTER TABLE given_answer ALTER created DROP NOT NULL'); $this->addSql('ALTER TABLE given_answer ALTER created DROP NOT NULL');
} }
public function down(Schema $schema): void 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('CREATE SCHEMA public');
$this->addSql('ALTER TABLE given_answer ALTER answer_id SET NOT NULL'); $this->addSql('ALTER TABLE given_answer ALTER answer_id SET NOT NULL');
$this->addSql('ALTER TABLE given_answer ALTER created SET NOT NULL'); $this->addSql('ALTER TABLE given_answer ALTER created SET NOT NULL');

View File

@@ -7,9 +7,6 @@ namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250303221227 extends AbstractMigration final class Version20250303221227 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
@@ -19,7 +16,6 @@ final class Version20250303221227 extends AbstractMigration
public function up(Schema $schema): void 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 given_answer ALTER created SET NOT NULL');
$this->addSql('ALTER TABLE season ADD active_quiz_id UUID DEFAULT 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('COMMENT ON COLUMN season.active_quiz_id IS \'(DC2Type:uuid)\'');
@@ -29,7 +25,6 @@ final class Version20250303221227 extends AbstractMigration
public function down(Schema $schema): void 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('CREATE SCHEMA public');
$this->addSql('ALTER TABLE season DROP CONSTRAINT FK_F0E45BA96706D6B'); $this->addSql('ALTER TABLE season DROP CONSTRAINT FK_F0E45BA96706D6B');
$this->addSql('DROP INDEX IDX_F0E45BA96706D6B'); $this->addSql('DROP INDEX IDX_F0E45BA96706D6B');

View File

@@ -7,9 +7,6 @@ namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250311213417 extends AbstractMigration final class Version20250311213417 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
@@ -19,7 +16,6 @@ final class Version20250311213417 extends AbstractMigration
public function up(Schema $schema): void public function up(Schema $schema): void
{ {
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE answer ALTER id TYPE UUID'); $this->addSql('ALTER TABLE answer ALTER id TYPE UUID');
$this->addSql('ALTER TABLE answer ALTER question_id TYPE UUID'); $this->addSql('ALTER TABLE answer ALTER question_id TYPE UUID');
$this->addSql('COMMENT ON COLUMN answer.id IS \'\''); $this->addSql('COMMENT ON COLUMN answer.id IS \'\'');
@@ -69,7 +65,6 @@ final class Version20250311213417 extends AbstractMigration
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE candidate ALTER id TYPE UUID'); $this->addSql('ALTER TABLE candidate ALTER id TYPE UUID');
$this->addSql('ALTER TABLE candidate ALTER season_id TYPE UUID'); $this->addSql('ALTER TABLE candidate ALTER season_id TYPE UUID');
$this->addSql('COMMENT ON COLUMN candidate.id IS \'(DC2Type:uuid)\''); $this->addSql('COMMENT ON COLUMN candidate.id IS \'(DC2Type:uuid)\'');

View File

@@ -7,9 +7,6 @@ namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250402185128 extends AbstractMigration final class Version20250402185128 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
@@ -19,13 +16,11 @@ final class Version20250402185128 extends AbstractMigration
public function up(Schema $schema): void public function up(Schema $schema): void
{ {
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE elimination (id UUID NOT NULL, data JSON NOT NULL, PRIMARY KEY(id))'); $this->addSql('CREATE TABLE elimination (id UUID NOT NULL, data JSON NOT NULL, PRIMARY KEY(id))');
} }
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE elimination'); $this->addSql('DROP TABLE elimination');
} }
} }

View File

@@ -18,13 +18,13 @@ final class Version20250420111904 extends AbstractMigration
{ {
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE "user" ADD is_verified BOOLEAN NOT NULL ALTER TABLE "user" ADD is_verified BOOLEAN NOT NULL
SQL); SQL);
} }
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE "user" DROP is_verified ALTER TABLE "user" DROP is_verified
SQL); SQL);
} }
} }

View File

@@ -18,13 +18,13 @@ final class Version20250420125040 extends AbstractMigration
{ {
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE season DROP preregister_candidates ALTER TABLE season DROP preregister_candidates
SQL); SQL);
} }
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE season ADD preregister_candidates BOOLEAN NOT NULL DEFAULT true ALTER TABLE season ADD preregister_candidates BOOLEAN NOT NULL DEFAULT true
SQL); SQL);
} }
} }

View File

@@ -7,9 +7,6 @@ namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250427174822 extends AbstractMigration final class Version20250427174822 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
@@ -19,23 +16,21 @@ final class Version20250427174822 extends AbstractMigration
public function up(Schema $schema): void public function up(Schema $schema): void
{ {
// this up() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE answer ADD ordering SMALLINT DEFAULT 0 NOT NULL ALTER TABLE answer ADD ordering SMALLINT DEFAULT 0 NOT NULL
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE question ADD ordering SMALLINT DEFAULT 0 NOT NULL ALTER TABLE question ADD ordering SMALLINT DEFAULT 0 NOT NULL
SQL); SQL);
} }
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
// this down() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE answer DROP ordering ALTER TABLE answer DROP ordering
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE question DROP ordering ALTER TABLE question DROP ordering
SQL); SQL);
} }
} }

View File

@@ -7,9 +7,6 @@ namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250504101440 extends AbstractMigration final class Version20250504101440 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
@@ -19,23 +16,21 @@ final class Version20250504101440 extends AbstractMigration
public function up(Schema $schema): void public function up(Schema $schema): void
{ {
// this up() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
CREATE UNIQUE INDEX UNIQ_C8B28E445E237E064EC001D1 ON candidate (name, season_id) CREATE UNIQUE INDEX UNIQ_C8B28E445E237E064EC001D1 ON candidate (name, season_id)
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
CREATE UNIQUE INDEX UNIQ_A412FA925E237E064EC001D1 ON quiz (name, season_id) CREATE UNIQUE INDEX UNIQ_A412FA925E237E064EC001D1 ON quiz (name, season_id)
SQL); SQL);
} }
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
// this down() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
DROP INDEX UNIQ_A412FA925E237E064EC001D1 DROP INDEX UNIQ_A412FA925E237E064EC001D1
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
DROP INDEX UNIQ_C8B28E445E237E064EC001D1 DROP INDEX UNIQ_C8B28E445E237E064EC001D1
SQL); SQL);
} }
} }

View File

@@ -18,37 +18,37 @@ final class Version20250521192752 extends AbstractMigration
{ {
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE elimination ADD quiz_id UUID NOT NULL ALTER TABLE elimination ADD quiz_id UUID NOT NULL
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE elimination ADD CONSTRAINT FK_5947284F853CD175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE ALTER TABLE elimination ADD CONSTRAINT FK_5947284F853CD175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
CREATE INDEX IDX_5947284F853CD175 ON elimination (quiz_id) CREATE INDEX IDX_5947284F853CD175 ON elimination (quiz_id)
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE quiz ALTER dropouts SET DEFAULT 1 ALTER TABLE quiz ALTER dropouts SET DEFAULT 1
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE quiz ALTER dropouts SET NOT NULL ALTER TABLE quiz ALTER dropouts SET NOT NULL
SQL); SQL);
} }
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE quiz ALTER dropouts DROP DEFAULT ALTER TABLE quiz ALTER dropouts DROP DEFAULT
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE quiz ALTER dropouts DROP NOT NULL ALTER TABLE quiz ALTER dropouts DROP NOT NULL
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE elimination DROP CONSTRAINT FK_5947284F853CD175 ALTER TABLE elimination DROP CONSTRAINT FK_5947284F853CD175
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
DROP INDEX IDX_5947284F853CD175 DROP INDEX IDX_5947284F853CD175
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE elimination DROP quiz_id ALTER TABLE elimination DROP quiz_id
SQL); SQL);
} }
} }

View File

@@ -18,7 +18,7 @@ final class Version20250521194035 extends AbstractMigration
{ {
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE elimination ADD created TIMESTAMP(0) WITHOUT TIME ZONE ALTER TABLE elimination ADD created TIMESTAMP(0) WITHOUT TIME ZONE
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
UPDATE elimination SET created = NOW() UPDATE elimination SET created = NOW()
SQL SQL
@@ -33,6 +33,6 @@ final class Version20250521194035 extends AbstractMigration
{ {
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE elimination DROP created ALTER TABLE elimination DROP created
SQL); SQL);
} }
} }

View File

@@ -18,73 +18,73 @@ final class Version20250606192337 extends AbstractMigration
{ {
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
CREATE TABLE quiz_candidate (id UUID NOT NULL, corrections DOUBLE PRECISION NOT NULL, created TIMESTAMP(0) WITH TIME ZONE NOT NULL, quiz_id UUID NOT NULL, candidate_id UUID NOT NULL, PRIMARY KEY(id)) CREATE TABLE quiz_candidate (id UUID NOT NULL, corrections DOUBLE PRECISION NOT NULL, created TIMESTAMP(0) WITH TIME ZONE NOT NULL, quiz_id UUID NOT NULL, candidate_id UUID NOT NULL, PRIMARY KEY(id))
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
CREATE INDEX IDX_CED2FFA2853CD175 ON quiz_candidate (quiz_id) CREATE INDEX IDX_CED2FFA2853CD175 ON quiz_candidate (quiz_id)
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
CREATE INDEX IDX_CED2FFA291BD8781 ON quiz_candidate (candidate_id) CREATE INDEX IDX_CED2FFA291BD8781 ON quiz_candidate (candidate_id)
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
CREATE UNIQUE INDEX UNIQ_CED2FFA291BD8781853CD175 ON quiz_candidate (candidate_id, quiz_id) CREATE UNIQUE INDEX UNIQ_CED2FFA291BD8781853CD175 ON quiz_candidate (candidate_id, quiz_id)
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE quiz_candidate ADD CONSTRAINT FK_CED2FFA2853CD175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE ALTER TABLE quiz_candidate ADD CONSTRAINT FK_CED2FFA2853CD175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE quiz_candidate ADD CONSTRAINT FK_CED2FFA291BD8781 FOREIGN KEY (candidate_id) REFERENCES candidate (id) NOT DEFERRABLE INITIALLY IMMEDIATE ALTER TABLE quiz_candidate ADD CONSTRAINT FK_CED2FFA291BD8781 FOREIGN KEY (candidate_id) REFERENCES candidate (id) NOT DEFERRABLE INITIALLY IMMEDIATE
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE correction DROP CONSTRAINT fk_a29da1b891bd8781 ALTER TABLE correction DROP CONSTRAINT fk_a29da1b891bd8781
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE correction DROP CONSTRAINT fk_a29da1b8853cd175 ALTER TABLE correction DROP CONSTRAINT fk_a29da1b8853cd175
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
DROP TABLE correction DROP TABLE correction
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE elimination ALTER created TYPE TIMESTAMP(0) WITH TIME ZONE ALTER TABLE elimination ALTER created TYPE TIMESTAMP(0) WITH TIME ZONE
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE given_answer ALTER created TYPE TIMESTAMP(0) WITH TIME ZONE ALTER TABLE given_answer ALTER created TYPE TIMESTAMP(0) WITH TIME ZONE
SQL); SQL);
} }
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
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)) 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))
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
CREATE UNIQUE INDEX uniq_a29da1b891bd8781853cd175 ON correction (candidate_id, quiz_id) CREATE UNIQUE INDEX uniq_a29da1b891bd8781853cd175 ON correction (candidate_id, quiz_id)
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
CREATE INDEX idx_a29da1b8853cd175 ON correction (quiz_id) CREATE INDEX idx_a29da1b8853cd175 ON correction (quiz_id)
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
CREATE INDEX idx_a29da1b891bd8781 ON correction (candidate_id) CREATE INDEX idx_a29da1b891bd8781 ON correction (candidate_id)
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE correction ADD CONSTRAINT fk_a29da1b891bd8781 FOREIGN KEY (candidate_id) REFERENCES candidate (id) NOT DEFERRABLE INITIALLY IMMEDIATE ALTER TABLE correction ADD CONSTRAINT fk_a29da1b891bd8781 FOREIGN KEY (candidate_id) REFERENCES candidate (id) NOT DEFERRABLE INITIALLY IMMEDIATE
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE correction ADD CONSTRAINT fk_a29da1b8853cd175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE ALTER TABLE correction ADD CONSTRAINT fk_a29da1b8853cd175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE quiz_candidate DROP CONSTRAINT FK_CED2FFA2853CD175 ALTER TABLE quiz_candidate DROP CONSTRAINT FK_CED2FFA2853CD175
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE quiz_candidate DROP CONSTRAINT FK_CED2FFA291BD8781 ALTER TABLE quiz_candidate DROP CONSTRAINT FK_CED2FFA291BD8781
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
DROP TABLE quiz_candidate DROP TABLE quiz_candidate
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE given_answer ALTER created TYPE TIMESTAMP(0) WITHOUT TIME ZONE ALTER TABLE given_answer ALTER created TYPE TIMESTAMP(0) WITHOUT TIME ZONE
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE elimination ALTER created TYPE TIMESTAMP(0) WITHOUT TIME ZONE ALTER TABLE elimination ALTER created TYPE TIMESTAMP(0) WITHOUT TIME ZONE
SQL); SQL);
} }
} }

View File

@@ -7,9 +7,6 @@ namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250606195952 extends AbstractMigration final class Version20250606195952 extends AbstractMigration
{ {
public function getDescription(): string public function getDescription(): string
@@ -21,22 +18,22 @@ final class Version20250606195952 extends AbstractMigration
{ {
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
delete from given_answer where answer_id is null delete from given_answer where answer_id is null
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE given_answer ALTER answer_id TYPE UUID ALTER TABLE given_answer ALTER answer_id TYPE UUID
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE given_answer ALTER answer_id SET NOT NULL ALTER TABLE given_answer ALTER answer_id SET NOT NULL
SQL); SQL);
} }
public function down(Schema $schema): void public function down(Schema $schema): void
{ {
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE given_answer ALTER answer_id TYPE UUID ALTER TABLE given_answer ALTER answer_id TYPE UUID
SQL); SQL);
$this->addSql(<<<'SQL' $this->addSql(<<<'SQL'
ALTER TABLE given_answer ALTER answer_id DROP NOT NULL ALTER TABLE given_answer ALTER answer_id DROP NOT NULL
SQL); SQL);
} }
} }

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20250607154730 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
$this->addSql(<<<'SQL'
ALTER TABLE season DROP CONSTRAINT FK_F0E45BA96706D6B
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE season ADD CONSTRAINT FK_F0E45BA96706D6B FOREIGN KEY (active_quiz_id) REFERENCES quiz (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE
SQL);
}
public function down(Schema $schema): void
{
$this->addSql(<<<'SQL'
ALTER TABLE season DROP CONSTRAINT fk_f0e45ba96706d6b
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE season ADD CONSTRAINT fk_f0e45ba96706d6b FOREIGN KEY (active_quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE
SQL);
}
}

View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20250607184525 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
$this->addSql(<<<'SQL'
ALTER TABLE elimination DROP CONSTRAINT FK_5947284F853CD175
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE elimination ADD CONSTRAINT FK_5947284F853CD175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE given_answer DROP CONSTRAINT FK_9AC61A30853CD175
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE given_answer ADD CONSTRAINT FK_9AC61A30853CD175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
SQL);
}
public function down(Schema $schema): void
{
$this->addSql(<<<'SQL'
ALTER TABLE given_answer DROP CONSTRAINT fk_9ac61a30853cd175
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE given_answer ADD CONSTRAINT fk_9ac61a30853cd175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE elimination DROP CONSTRAINT fk_5947284f853cd175
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE elimination ADD CONSTRAINT fk_5947284f853cd175 FOREIGN KEY (quiz_id) REFERENCES quiz (id) NOT DEFERRABLE INITIALLY IMMEDIATE
SQL);
}
}

View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20250610210417 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
$this->addSql(<<<'SQL'
CREATE TABLE season_settings (id UUID NOT NULL, show_numbers BOOLEAN DEFAULT false NOT NULL, confirm_answers BOOLEAN DEFAULT false NOT NULL, PRIMARY KEY(id))
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE season ADD settings_id UUID DEFAULT NULL
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE season ADD CONSTRAINT FK_F0E45BA959949888 FOREIGN KEY (settings_id) REFERENCES season_settings (id) NOT DEFERRABLE INITIALLY IMMEDIATE
SQL);
$this->addSql(<<<'SQL'
CREATE UNIQUE INDEX UNIQ_F0E45BA959949888 ON season (settings_id)
SQL);
}
public function down(Schema $schema): void
{
$this->addSql(<<<'SQL'
DROP TABLE season_settings
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE season DROP CONSTRAINT FK_F0E45BA959949888
SQL);
$this->addSql(<<<'SQL'
DROP INDEX UNIQ_F0E45BA959949888
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE season DROP settings_id
SQL);
}
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20251007194241 extends AbstractMigration
{
public function getDescription(): string
{
return 'Change elimination data type to jsonb';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE elimination ALTER data TYPE JSONB');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE elimination ALTER data TYPE JSON');
}
}

View File

@@ -7,3 +7,8 @@ parameters:
- public/ - public/
- src/ - src/
- tests/ - tests/
treatPhpDocTypesAsCertain: false
symfony:
containerXmlPath: var/cache/dev/Tvdt_KernelDevDebugContainer.xml
doctrine:
objectManagerLoader: tests/object-manager.php

View File

@@ -2,7 +2,7 @@
declare(strict_types=1); declare(strict_types=1);
use App\Kernel; use Tvdt\Kernel;
require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; require_once dirname(__DIR__).'/vendor/autoload_runtime.php';

View File

@@ -3,6 +3,8 @@
declare(strict_types=1); declare(strict_types=1);
use Rector\Config\RectorConfig; use Rector\Config\RectorConfig;
use Rector\Symfony\Bridge\Symfony\Routing\SymfonyRoutesProvider;
use Rector\Symfony\Contract\Bridge\Symfony\Routing\SymfonyRoutesProviderInterface;
return RectorConfig::configure() return RectorConfig::configure()
->withPaths([ ->withPaths([
@@ -11,7 +13,9 @@ return RectorConfig::configure()
__DIR__.'/src', __DIR__.'/src',
__DIR__.'/tests', __DIR__.'/tests',
]) ])
->withSymfonyContainerXml('var/cache/dev/App_KernelDevDebugContainer.xml') ->withSymfonyContainerXml(__DIR__.'/var/cache/dev/Tvdt_KernelDevDebugContainer.xml')
->withSymfonyContainerPhp(__DIR__.'/tests/symfony-container.php')
->registerService(SymfonyRoutesProvider::class, SymfonyRoutesProviderInterface::class)
->withParallel() ->withParallel()
->withPhpSets() ->withPhpSets()
->withPreparedSets( ->withPreparedSets(

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Tvdt\Command;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Tvdt\Entity\SeasonSettings;
use Tvdt\Repository\SeasonRepository;
#[AsCommand(
name: 'tvdt:add-settings',
description: 'Add a short description for your command',
)]
readonly class AddSettingsCommand
{
public function __construct(private SeasonRepository $seasonRepository, private EntityManagerInterface $entityManager) {}
public function __invoke(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
foreach ($this->seasonRepository->findAll() as $season) {
if (null !== $season->settings) {
continue;
}
$io->text('Adding settings to season : '.$season->seasonCode);
$season->settings = new SeasonSettings();
}
$this->entityManager->flush();
return Command::SUCCESS;
}
}

View File

@@ -2,45 +2,35 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Command; namespace Tvdt\Command;
use App\Repository\SeasonRepository;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\Argument;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Style\SymfonyStyle;
use Tvdt\Repository\SeasonRepository;
use Tvdt\Repository\UserRepository;
#[AsCommand( #[AsCommand(
name: 'app:claim-season', name: 'tvdt:claim-season',
description: 'Give a user owner rights on a season', description: 'Give a user owner rights on a season',
)] )]
class ClaimSeasonCommand extends Command readonly class ClaimSeasonCommand
{ {
public function __construct( public function __construct(private UserRepository $userRepository, private SeasonRepository $seasonRepository, private EntityManagerInterface $entityManager) {}
private readonly UserRepository $userRepository,
private readonly SeasonRepository $seasonRepository,
private readonly EntityManagerInterface $entityManager)
{
parent::__construct();
}
protected function configure(): void public function __invoke(
{ #[Argument]
$this string $seasonCode,
->addArgument('email', InputArgument::REQUIRED, 'The email of the user thats claims the season') #[Argument]
->addArgument('season', InputArgument::REQUIRED, 'The season to claim') string $email,
; InputInterface $input,
} OutputInterface $output,
): int {
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$email = $input->getArgument('email');
$seasonCode = $input->getArgument('season');
try { try {
$season = $this->seasonRepository->findOneBy(['seasonCode' => $seasonCode]); $season = $this->seasonRepository->findOneBy(['seasonCode' => $seasonCode]);

View File

@@ -2,39 +2,31 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Command; namespace Tvdt\Command;
use App\Repository\UserRepository; use Symfony\Component\Console\Attribute\Argument;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Style\SymfonyStyle;
use Tvdt\Repository\UserRepository;
#[AsCommand( #[AsCommand(
name: 'app:make-admin', name: 'tvdt:make-admin',
description: 'Give a user the role admin', description: 'Give a user the role admin',
)] )]
class MakeAdminCommand extends Command readonly class MakeAdminCommand
{ {
public function __construct(private readonly UserRepository $userRepository) public function __construct(private UserRepository $userRepository) {}
{
parent::__construct();
}
protected function configure(): void public function __invoke(
{ #[Argument]
$this string $email,
->addArgument('email', InputArgument::REQUIRED, 'The email of the user to make admin') InputInterface $input,
; OutputInterface $output,
} ): int {
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$email = $input->getArgument('email');
try { try {
$this->userRepository->makeAdmin($email); $this->userRepository->makeAdmin($email);
} catch (\InvalidArgumentException) { } catch (\InvalidArgumentException) {

View File

@@ -2,14 +2,15 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Controller; namespace Tvdt\Controller;
use App\Enum\FlashType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as AbstractBaseController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as AbstractBaseController;
use Tvdt\Enum\FlashType;
abstract class AbstractController extends AbstractBaseController abstract class AbstractController extends AbstractBaseController
{ {
protected const string SEASON_CODE_REGEX = '[A-Za-z\d]{5}'; protected const string SEASON_CODE_REGEX = '[A-Za-z\d]{5}';
protected const string CANDIDATE_HASH_REGEX = '[\w\-=]+'; protected const string CANDIDATE_HASH_REGEX = '[\w\-=]+';
#[\Override] #[\Override]

View File

@@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin;
use App\Entity\Answer;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
class AnswerCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return Answer::class;
}
}

View File

@@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin;
use App\Entity\Candidate;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
class CandidateCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return Candidate::class;
}
}

View File

@@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin;
use App\Entity\QuizCandidate;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
class CorrectionCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return QuizCandidate::class;
}
}

View File

@@ -1,67 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin;
use App\Entity\Answer;
use App\Entity\Candidate;
use App\Entity\GivenAnswer;
use App\Entity\Question;
use App\Entity\Quiz;
use App\Entity\QuizCandidate;
use App\Entity\Season;
use App\Entity\User;
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminDashboard;
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;
#[AdminDashboard(routePath: '/admin', routeName: 'admin')]
class DashboardController extends AbstractDashboardController
{
#[\Override]
public function index(): Response
{
// 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');
}
#[\Override]
public function configureDashboard(): Dashboard
{
return Dashboard::new()
->setTitle('TijdVoorDeTest');
}
#[\Override]
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', QuizCandidate::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', 'fas fa-sign-out');
}
}

View File

@@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin;
use App\Entity\GivenAnswer;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
class GivenAnswerCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return GivenAnswer::class;
}
}

View File

@@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin;
use App\Entity\Question;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
class QuestionCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return Question::class;
}
}

View File

@@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin;
use App\Entity\Quiz;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
class QuizCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return Quiz::class;
}
}

View File

@@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin;
use App\Entity\Season;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
class SeasonCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return Season::class;
}
}

View File

@@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin;
use App\Entity\User;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
class UserCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return User::class;
}
}

View File

@@ -2,14 +2,8 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Controller\Backoffice; namespace Tvdt\Controller\Backoffice;
use App\Controller\AbstractController;
use App\Entity\Season;
use App\Entity\User;
use App\Form\CreateSeasonFormType;
use App\Repository\SeasonRepository;
use App\Service\QuizSpreadsheetService;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\SecurityBundle\Security; use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -18,6 +12,12 @@ use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Attribute\AsController; use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted; use Symfony\Component\Security\Http\Attribute\IsGranted;
use Tvdt\Controller\AbstractController;
use Tvdt\Entity\Season;
use Tvdt\Entity\User;
use Tvdt\Form\CreateSeasonFormType;
use Tvdt\Repository\SeasonRepository;
use Tvdt\Service\QuizSpreadsheetService;
#[AsController] #[AsController]
#[IsGranted('ROLE_USER')] #[IsGranted('ROLE_USER')]
@@ -28,7 +28,7 @@ final class BackofficeController extends AbstractController
private readonly Security $security, private readonly Security $security,
) {} ) {}
#[Route('/backoffice/', name: 'app_backoffice_index')] #[Route('/backoffice/', name: 'tvdt_backoffice_index')]
public function index(): Response public function index(): Response
{ {
$user = $this->getUser(); $user = $this->getUser();
@@ -43,7 +43,7 @@ final class BackofficeController extends AbstractController
]); ]);
} }
#[Route('/backoffice/add', name: 'app_backoffice_season_add', priority: 10)] #[Route('/backoffice/season/add', name: 'tvdt_backoffice_season_add', priority: 10)]
public function addSeason(Request $request, EntityManagerInterface $em): Response public function addSeason(Request $request, EntityManagerInterface $em): Response
{ {
$season = new Season(); $season = new Season();
@@ -61,13 +61,13 @@ final class BackofficeController extends AbstractController
$em->persist($season); $em->persist($season);
$em->flush(); $em->flush();
return $this->redirectToRoute('app_backoffice_season', ['seasonCode' => $season->getSeasonCode()]); return $this->redirectToRoute('tvdt_backoffice_season', ['seasonCode' => $season->seasonCode]);
} }
return $this->render('backoffice/season_add.html.twig', ['form' => $form]); return $this->render('backoffice/season_add.html.twig', ['form' => $form]);
} }
#[Route('/backoffice/template', name: 'app_backoffice_template', priority: 10)] #[Route('/backoffice/template', name: 'tvdt_backoffice_template', priority: 10)]
public function getTemplate(QuizSpreadsheetService $excel): Response public function getTemplate(QuizSpreadsheetService $excel): Response
{ {
$response = new StreamedResponse($excel->generateTemplate()); $response = new StreamedResponse($excel->generateTemplate());

View File

@@ -2,36 +2,37 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Controller\Backoffice; namespace Tvdt\Controller\Backoffice;
use App\Controller\AbstractController;
use App\Entity\Elimination;
use App\Entity\Quiz;
use App\Entity\Season;
use App\Factory\EliminationFactory;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Requirement\Requirement;
use Tvdt\Controller\AbstractController;
use Tvdt\Entity\Elimination;
use Tvdt\Entity\Quiz;
use Tvdt\Entity\Season;
use Tvdt\Factory\EliminationFactory;
final class PrepareEliminationController extends AbstractController final class PrepareEliminationController extends AbstractController
{ {
#[Route( #[Route(
'/backoffice/elimination/{seasonCode}/{quiz}/prepare', '/backoffice/season/{seasonCode:season}/quiz/{quiz}/elimination/prepare',
name: 'app_prepare_elimination', name: 'tvdt_prepare_elimination',
requirements: ['seasonCode' => self::SEASON_CODE_REGEX], requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID],
)] )]
public function index(Season $season, Quiz $quiz, EliminationFactory $eliminationFactory): Response public function index(Season $season, Quiz $quiz, EliminationFactory $eliminationFactory): Response
{ {
$elimination = $eliminationFactory->createEliminationFromQuiz($quiz); $elimination = $eliminationFactory->createEliminationFromQuiz($quiz);
return $this->redirectToRoute('app_prepare_elimination_view', ['elimination' => $elimination->getId()]); return $this->redirectToRoute('tvdt_prepare_elimination_view', ['elimination' => $elimination->id]);
} }
#[Route( #[Route(
'/backoffice/elimination/{elimination}', '/backoffice/elimination/{elimination}',
name: 'app_prepare_elimination_view', name: 'tvdt_prepare_elimination_view',
requirements: ['seasonCode' => self::SEASON_CODE_REGEX], requirements: ['elimination' => Requirement::UUID],
)] )]
public function viewElimination(Elimination $elimination, Request $request, EntityManagerInterface $em): Response public function viewElimination(Elimination $elimination, Request $request, EntityManagerInterface $em): Response
{ {
@@ -39,9 +40,10 @@ final class PrepareEliminationController extends AbstractController
$elimination->updateFromInputBag($request->request); $elimination->updateFromInputBag($request->request);
$em->flush(); $em->flush();
if (true === $request->request->getBoolean('start')) { if ($request->request->getBoolean('start')) {
return $this->redirectToRoute('app_elimination', ['elimination' => $elimination->getId()]); return $this->redirectToRoute('tvdt_elimination', ['elimination' => $elimination->id]);
} }
$this->addFlash('success', 'Elimination updated'); $this->addFlash('success', 'Elimination updated');
} }

View File

@@ -2,18 +2,27 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Controller\Backoffice; namespace Tvdt\Controller\Backoffice;
use App\Controller\AbstractController;
use App\Entity\Quiz;
use App\Entity\Season;
use App\Repository\CandidateRepository;
use App\Security\Voter\SeasonVoter;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController; use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Requirement\Requirement;
use Symfony\Component\Security\Http\Attribute\IsGranted; use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Contracts\Translation\TranslatorInterface;
use Tvdt\Controller\AbstractController;
use Tvdt\Entity\Candidate;
use Tvdt\Entity\Quiz;
use Tvdt\Entity\Season;
use Tvdt\Exception\ErrorClearingQuizException;
use Tvdt\Repository\CandidateRepository;
use Tvdt\Repository\QuizCandidateRepository;
use Tvdt\Repository\QuizRepository;
use Tvdt\Security\Voter\SeasonVoter;
#[AsController] #[AsController]
#[IsGranted('ROLE_USER')] #[IsGranted('ROLE_USER')]
@@ -21,12 +30,15 @@ class QuizController extends AbstractController
{ {
public function __construct( public function __construct(
private readonly CandidateRepository $candidateRepository, private readonly CandidateRepository $candidateRepository,
private readonly TranslatorInterface $translator,
) {} ) {}
#[Route('/backoffice/season/{seasonCode}/quiz/{quiz}', name: 'app_backoffice_quiz',
requirements: ['seasonCode' => self::SEASON_CODE_REGEX],
)]
#[IsGranted(SeasonVoter::EDIT, subject: 'season')] #[IsGranted(SeasonVoter::EDIT, subject: 'season')]
#[Route(
'/backoffice/season/{seasonCode:season}/quiz/{quiz}',
name: 'tvdt_backoffice_quiz',
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID],
)]
public function index(Season $season, Quiz $quiz): Response public function index(Season $season, Quiz $quiz): Response
{ {
return $this->render('backoffice/quiz.html.twig', [ return $this->render('backoffice/quiz.html.twig', [
@@ -36,19 +48,73 @@ class QuizController extends AbstractController
]); ]);
} }
#[Route('/backoffice/season/{seasonCode}/quiz/{quiz}/enable', name: 'app_backoffice_enable',
requirements: ['seasonCode' => self::SEASON_CODE_REGEX],
)]
#[IsGranted(SeasonVoter::EDIT, subject: 'season')] #[IsGranted(SeasonVoter::EDIT, subject: 'season')]
public function enableQuiz(Season $season, ?Quiz $quiz, EntityManagerInterface $em): Response #[Route(
'/backoffice/season/{seasonCode:season}/quiz/{quiz}/enable',
name: 'tvdt_backoffice_enable',
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID.'|null'],
)]
public function enableQuiz(Season $season, ?Quiz $quiz, EntityManagerInterface $em): RedirectResponse
{ {
$season->setActiveQuiz($quiz); $season->activeQuiz = $quiz;
$em->flush(); $em->flush();
if ($quiz instanceof Quiz) { if ($quiz instanceof Quiz) {
return $this->redirectToRoute('app_backoffice_quiz', ['seasonCode' => $season->getSeasonCode(), 'quiz' => $quiz->getId()]); return $this->redirectToRoute('tvdt_backoffice_quiz', ['seasonCode' => $season->seasonCode, 'quiz' => $quiz->id]);
} }
return $this->redirectToRoute('app_backoffice_season', ['seasonCode' => $season->getSeasonCode()]); return $this->redirectToRoute('tvdt_backoffice_season', ['seasonCode' => $season->seasonCode]);
}
#[IsGranted(SeasonVoter::EDIT, subject: 'quiz')]
#[Route(
'/backoffice/quiz/{quiz}/clear',
name: 'tvdt_backoffice_quiz_clear',
requirements: ['quiz' => Requirement::UUID],
)]
public function clearQuiz(Quiz $quiz, QuizRepository $quizRepository): RedirectResponse
{
try {
$quizRepository->clearQuiz($quiz);
$this->addFlash('success', $this->translator->trans('Quiz cleared'));
} catch (ErrorClearingQuizException) {
$this->addFlash('error', $this->translator->trans('Error clearing quiz'));
}
return $this->redirectToRoute('tvdt_backoffice_quiz', ['seasonCode' => $quiz->season->seasonCode, 'quiz' => $quiz->id]);
}
#[IsGranted(SeasonVoter::DELETE, subject: 'quiz')]
#[Route(
'/backoffice/quiz/{quiz}/delete',
name: 'tvdt_backoffice_quiz_delete',
requirements: ['quiz' => Requirement::UUID],
)]
public function deleteQuiz(Quiz $quiz, QuizRepository $quizRepository): RedirectResponse
{
$quizRepository->deleteQuiz($quiz);
$this->addFlash('success', $this->translator->trans('Quiz deleted'));
return $this->redirectToRoute('tvdt_backoffice_season', ['seasonCode' => $quiz->season->seasonCode]);
}
#[IsGranted(SeasonVoter::EDIT, subject: 'quiz')]
#[Route(
'/backoffice/quiz/{quiz}/candidate/{candidate}/modify_correction',
name: 'tvdt_backoffice_modify_correction',
requirements: ['quiz' => Requirement::UUID, 'candidate' => Requirement::UUID],
)]
public function modifyCorrection(Quiz $quiz, Candidate $candidate, QuizCandidateRepository $quizCandidateRepository, Request $request): RedirectResponse
{
if (!$request->isMethod('POST')) {
throw new MethodNotAllowedHttpException(['POST']);
}
$corrections = (float) $request->request->get('corrections');
$quizCandidateRepository->setCorrectionsForCandidate($quiz, $candidate, $corrections);
return $this->redirectToRoute('tvdt_backoffice_quiz', ['seasonCode' => $quiz->season->seasonCode, 'quiz' => $quiz->id]);
} }
} }

View File

@@ -2,17 +2,8 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Controller\Backoffice; namespace Tvdt\Controller\Backoffice;
use App\Controller\AbstractController;
use App\Entity\Candidate;
use App\Entity\Quiz;
use App\Entity\Season;
use App\Enum\FlashType;
use App\Form\AddCandidatesFormType;
use App\Form\UploadQuizFormType;
use App\Security\Voter\SeasonVoter;
use App\Service\QuizSpreadsheetService;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -21,34 +12,55 @@ use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted; use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use Tvdt\Controller\AbstractController;
use Tvdt\Entity\Candidate;
use Tvdt\Entity\Quiz;
use Tvdt\Entity\Season;
use Tvdt\Enum\FlashType;
use Tvdt\Form\AddCandidatesFormType;
use Tvdt\Form\SettingsForm;
use Tvdt\Form\UploadQuizFormType;
use Tvdt\Security\Voter\SeasonVoter;
use Tvdt\Service\QuizSpreadsheetService;
#[AsController] #[AsController]
#[IsGranted('ROLE_USER')] #[IsGranted('ROLE_USER')]
class SeasonController extends AbstractController class SeasonController extends AbstractController
{ {
public function __construct(private readonly TranslatorInterface $translator, private EntityManagerInterface $em, public function __construct(
private readonly TranslatorInterface $translator,
private readonly EntityManagerInterface $em,
) {} ) {}
#[IsGranted(SeasonVoter::EDIT, subject: 'season')]
#[Route( #[Route(
'/backoffice/season/{seasonCode}', '/backoffice/season/{seasonCode:season}',
name: 'app_backoffice_season', name: 'tvdt_backoffice_season',
requirements: ['seasonCode' => self::SEASON_CODE_REGEX], requirements: ['seasonCode' => self::SEASON_CODE_REGEX],
)] )]
#[IsGranted(SeasonVoter::EDIT, subject: 'season')] public function index(Season $season, Request $request): Response
public function index(Season $season): Response
{ {
$form = $this->createForm(SettingsForm::class, $season->settings);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->em->flush();
}
return $this->render('backoffice/season.html.twig', [ return $this->render('backoffice/season.html.twig', [
'season' => $season, 'season' => $season,
'form' => $form,
]); ]);
} }
#[IsGranted(SeasonVoter::EDIT, subject: 'season')]
#[Route( #[Route(
'/backoffice/season/{seasonCode}/add_candidate', '/backoffice/season/{seasonCode:season}/add-candidate',
name: 'app_backoffice_add_candidates', name: 'tvdt_backoffice_add_candidates',
requirements: ['seasonCode' => self::SEASON_CODE_REGEX], requirements: ['seasonCode' => self::SEASON_CODE_REGEX],
priority: 10, priority: 10,
)] )]
#[IsGranted(SeasonVoter::EDIT, subject: 'season')]
public function addCandidates(Season $season, Request $request): Response public function addCandidates(Season $season, Request $request): Response
{ {
$form = $this->createForm(AddCandidatesFormType::class); $form = $this->createForm(AddCandidatesFormType::class);
@@ -56,25 +68,25 @@ class SeasonController extends AbstractController
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$candidates = $form->get('candidates')->getData(); $candidates = $form->get('candidates')->getData();
foreach (explode("\r\n", (string) $candidates) as $candidate) { foreach (explode("\n", (string) $candidates) as $candidate) {
$season->addCandidate(new Candidate($candidate)); $season->addCandidate(new Candidate(mb_rtrim($candidate)));
} }
$this->em->flush(); $this->em->flush();
return $this->redirectToRoute('app_backoffice_season', ['seasonCode' => $season->getSeasonCode()]); return $this->redirectToRoute('tvdt_backoffice_season', ['seasonCode' => $season->seasonCode]);
} }
return $this->render('backoffice/season_add_candidates.html.twig', ['form' => $form]); return $this->render('backoffice/season_add_candidates.html.twig', ['form' => $form]);
} }
#[IsGranted(SeasonVoter::EDIT, subject: 'season')]
#[Route( #[Route(
'/backoffice/season/{seasonCode}/add', '/backoffice/season/{seasonCode:season}/add-quiz',
name: 'app_backoffice_quiz_add', name: 'tvdt_backoffice_quiz_add',
requirements: ['seasonCode' => self::SEASON_CODE_REGEX], requirements: ['seasonCode' => self::SEASON_CODE_REGEX],
priority: 10, priority: 10,
)] )]
#[IsGranted(SeasonVoter::EDIT, subject: 'season')]
public function addQuiz(Request $request, Season $season, QuizSpreadsheetService $quizSpreadsheet): Response public function addQuiz(Request $request, Season $season, QuizSpreadsheetService $quizSpreadsheet): Response
{ {
$quiz = new Quiz(); $quiz = new Quiz();
@@ -88,13 +100,13 @@ class SeasonController extends AbstractController
$quizSpreadsheet->xlsxToQuiz($quiz, $sheet); $quizSpreadsheet->xlsxToQuiz($quiz, $sheet);
$quiz->setSeason($season); $quiz->season = $season;
$this->em->persist($quiz); $this->em->persist($quiz);
$this->em->flush(); $this->em->flush();
$this->addFlash(FlashType::Success, $this->translator->trans('Quiz Added!')); $this->addFlash(FlashType::Success, $this->translator->trans('Quiz Added!'));
return $this->redirectToRoute('app_backoffice_season', ['seasonCode' => $season->getSeasonCode()]); return $this->redirectToRoute('tvdt_backoffice_season', ['seasonCode' => $season->seasonCode]);
} }
return $this->render('/backoffice/quiz_add.html.twig', ['form' => $form, 'season' => $season]); return $this->render('/backoffice/quiz_add.html.twig', ['form' => $form, 'season' => $season]);

View File

@@ -2,22 +2,23 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Controller; namespace Tvdt\Controller;
use App\Entity\Candidate;
use App\Entity\Elimination;
use App\Enum\FlashType;
use App\Form\EliminationEnterNameType;
use App\Helpers\Base64;
use App\Repository\CandidateRepository;
use App\Security\Voter\SeasonVoter;
use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Bridge\Doctrine\Attribute\MapEntity;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController; use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Requirement\Requirement;
use Symfony\Component\Security\Http\Attribute\IsGranted; use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use Tvdt\Entity\Candidate;
use Tvdt\Entity\Elimination;
use Tvdt\Enum\FlashType;
use Tvdt\Form\EliminationEnterNameType;
use Tvdt\Helpers\Base64;
use Tvdt\Repository\CandidateRepository;
use Tvdt\Security\Voter\SeasonVoter;
use function Symfony\Component\Translation\t; use function Symfony\Component\Translation\t;
@@ -27,8 +28,8 @@ final class EliminationController extends AbstractController
{ {
public function __construct(private readonly TranslatorInterface $translator) {} public function __construct(private readonly TranslatorInterface $translator) {}
#[Route('/elimination/{elimination}', name: 'app_elimination')]
#[IsGranted(SeasonVoter::ELIMINATION, 'elimination')] #[IsGranted(SeasonVoter::ELIMINATION, 'elimination')]
#[Route('/elimination/{elimination}', name: 'tvdt_elimination', requirements: ['elimination' => Requirement::UUID])]
public function index(#[MapEntity] Elimination $elimination, Request $request): Response public function index(#[MapEntity] Elimination $elimination, Request $request): Response
{ {
$form = $this->createForm(EliminationEnterNameType::class); $form = $this->createForm(EliminationEnterNameType::class);
@@ -38,7 +39,7 @@ final class EliminationController extends AbstractController
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$name = $form->get('name')->getData(); $name = $form->get('name')->getData();
return $this->redirectToRoute('app_elimination_candidate', ['elimination' => $elimination->getId(), 'candidateHash' => Base64::base64UrlEncode($name)]); return $this->redirectToRoute('tvdt_elimination_candidate', ['elimination' => $elimination->id, 'candidateHash' => Base64::base64UrlEncode($name)]);
} }
return $this->render('quiz/elimination/index.html.twig', [ return $this->render('quiz/elimination/index.html.twig', [
@@ -47,25 +48,25 @@ final class EliminationController extends AbstractController
]); ]);
} }
#[Route('/elimination/{elimination}/{candidateHash}', name: 'app_elimination_candidate')]
#[IsGranted(SeasonVoter::ELIMINATION, 'elimination')] #[IsGranted(SeasonVoter::ELIMINATION, 'elimination')]
#[Route('/elimination/{elimination}/{candidateHash}', name: 'tvdt_elimination_candidate', requirements: ['elimination' => Requirement::UUID, 'candidateHash' => self::CANDIDATE_HASH_REGEX])]
public function candidateScreen(Elimination $elimination, string $candidateHash, CandidateRepository $candidateRepository): Response public function candidateScreen(Elimination $elimination, string $candidateHash, CandidateRepository $candidateRepository): Response
{ {
$candidate = $candidateRepository->getCandidateByHash($elimination->getQuiz()->getSeason(), $candidateHash); $candidate = $candidateRepository->getCandidateByHash($elimination->quiz->season, $candidateHash);
if (!$candidate instanceof Candidate) { if (!$candidate instanceof Candidate) {
$this->addFlash(FlashType::Warning, $this->addFlash(FlashType::Warning,
t('Cound not find candidate with name %name%', ['%name%' => Base64::base64UrlDecode($candidateHash)])->trans($this->translator), t('Cound not find candidate with name %name%', ['%name%' => Base64::base64UrlDecode($candidateHash)])->trans($this->translator),
); );
return $this->redirectToRoute('app_elimination', ['elimination' => $elimination->getId()]); return $this->redirectToRoute('tvdt_elimination', ['elimination' => $elimination->id]);
} }
$screenColour = $elimination->getScreenColour($candidate->getName()); $screenColour = $elimination->getScreenColour($candidate->name);
if (null === $screenColour) { if (null === $screenColour) {
$this->addFlash(FlashType::Warning, $this->translator->trans('Cound not find candidate with name %name% in elimination.', ['%name%' => $candidate->getName()])); $this->addFlash(FlashType::Warning, $this->translator->trans('Cound not find candidate with name %name% in elimination.', ['%name%' => $candidate->name]));
return $this->redirectToRoute('app_elimination', ['elimination' => $elimination->getId()]); return $this->redirectToRoute('tvdt_elimination', ['elimination' => $elimination->id]);
} }
return $this->render('quiz/elimination/candidate.html.twig', [ return $this->render('quiz/elimination/candidate.html.twig', [

View File

@@ -2,20 +2,20 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Controller; namespace Tvdt\Controller;
use App\Enum\FlashType;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController; use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use Tvdt\Enum\FlashType;
#[AsController] #[AsController]
final class LoginController extends AbstractController final class LoginController extends AbstractController
{ {
#[Route(path: '/login', name: 'app_login_login')] #[Route(path: '/login', name: 'tvdt_login_login')]
public function login(AuthenticationUtils $authenticationUtils, TranslatorInterface $translator): Response public function login(AuthenticationUtils $authenticationUtils, TranslatorInterface $translator): Response
{ {
// get the login error if there is one // get the login error if there is one
@@ -34,7 +34,7 @@ final class LoginController extends AbstractController
]); ]);
} }
#[Route(path: '/logout', name: 'app_login_logout')] #[Route(path: '/logout', name: 'tvdt_login_logout')]
public function logout(): never public function logout(): never
{ {
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.'); throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');

View File

@@ -2,38 +2,37 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Controller; namespace Tvdt\Controller;
use App\Entity\Answer;
use App\Entity\Candidate;
use App\Entity\GivenAnswer;
use App\Entity\Question;
use App\Entity\Quiz;
use App\Entity\Season;
use App\Enum\FlashType;
use App\Form\EnterNameType;
use App\Form\SelectSeasonType;
use App\Helpers\Base64;
use App\Repository\AnswerRepository;
use App\Repository\CandidateRepository;
use App\Repository\GivenAnswerRepository;
use App\Repository\QuestionRepository;
use App\Repository\QuizCandidateRepository;
use App\Repository\SeasonRepository;
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController; use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use Tvdt\Entity\Answer;
use Tvdt\Entity\Candidate;
use Tvdt\Entity\GivenAnswer;
use Tvdt\Entity\Question;
use Tvdt\Entity\Quiz;
use Tvdt\Entity\Season;
use Tvdt\Enum\FlashType;
use Tvdt\Form\EnterNameType;
use Tvdt\Form\SelectSeasonType;
use Tvdt\Helpers\Base64;
use Tvdt\Repository\AnswerRepository;
use Tvdt\Repository\CandidateRepository;
use Tvdt\Repository\GivenAnswerRepository;
use Tvdt\Repository\QuestionRepository;
use Tvdt\Repository\QuizCandidateRepository;
use Tvdt\Repository\SeasonRepository;
#[AsController] #[AsController]
final class QuizController extends AbstractController final class QuizController extends AbstractController
{ {
public function __construct(private readonly TranslatorInterface $translator) {} public function __construct(private readonly TranslatorInterface $translator) {}
#[Route(path: '/', name: 'app_quiz_select_season', methods: ['GET', 'POST'])] #[Route(path: '/', name: 'tvdt_quiz_select_season', methods: ['GET', 'POST'])]
public function selectSeason(Request $request, SeasonRepository $seasonRepository): Response public function selectSeason(Request $request, SeasonRepository $seasonRepository): Response
{ {
$form = $this->createForm(SelectSeasonType::class); $form = $this->createForm(SelectSeasonType::class);
@@ -45,19 +44,18 @@ final class QuizController extends AbstractController
if ([] === $seasonRepository->findBy(['seasonCode' => $seasonCode])) { if ([] === $seasonRepository->findBy(['seasonCode' => $seasonCode])) {
$this->addFlash(FlashType::Warning, $this->translator->trans('Invalid season code')); $this->addFlash(FlashType::Warning, $this->translator->trans('Invalid season code'));
return $this->redirectToRoute('app_quiz_select_season'); return $this->redirectToRoute('tvdt_quiz_select_season');
} }
return $this->redirectToRoute('app_quiz_enter_name', ['seasonCode' => $seasonCode]); return $this->redirectToRoute('tvdt_quiz_enter_name', ['seasonCode' => $seasonCode]);
} }
return $this->render('quiz/select_season.html.twig', ['form' => $form]); return $this->render('quiz/select_season.html.twig', ['form' => $form]);
} }
#[Route(path: '/{seasonCode}', name: 'app_quiz_enter_name', requirements: ['seasonCode' => self::SEASON_CODE_REGEX])] #[Route(path: '/{seasonCode:season}', name: 'tvdt_quiz_enter_name', requirements: ['seasonCode' => self::SEASON_CODE_REGEX])]
public function enterName( public function enterName(
Request $request, Request $request,
#[MapEntity(mapping: ['seasonCode' => 'seasonCode'])]
Season $season, Season $season,
): Response { ): Response {
$form = $this->createForm(EnterNameType::class); $form = $this->createForm(EnterNameType::class);
@@ -67,19 +65,18 @@ final class QuizController extends AbstractController
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$name = $form->get('name')->getData(); $name = $form->get('name')->getData();
return $this->redirectToRoute('app_quiz_quiz_page', ['seasonCode' => $season->getSeasonCode(), 'nameHash' => Base64::base64UrlEncode($name)]); return $this->redirectToRoute('tvdt_quiz_quiz_page', ['seasonCode' => $season->seasonCode, 'nameHash' => Base64::base64UrlEncode($name)]);
} }
return $this->render('quiz/enter_name.twig', ['season' => $season, 'form' => $form]); return $this->render('quiz/enter_name.twig', ['season' => $season, 'form' => $form]);
} }
#[Route( #[Route(
path: '/{seasonCode}/{nameHash}', path: '/{seasonCode:season}/{nameHash}',
name: 'app_quiz_quiz_page', name: 'tvdt_quiz_quiz_page',
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'nameHash' => self::CANDIDATE_HASH_REGEX], requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'nameHash' => self::CANDIDATE_HASH_REGEX],
)] )]
public function quizPage( public function quizPage(
#[MapEntity(mapping: ['seasonCode' => 'seasonCode'])]
Season $season, Season $season,
string $nameHash, string $nameHash,
Request $request, Request $request,
@@ -94,28 +91,28 @@ final class QuizController extends AbstractController
if (!$candidate instanceof Candidate) { if (!$candidate instanceof Candidate) {
$this->addFlash(FlashType::Danger, $this->translator->trans('Candidate not found')); $this->addFlash(FlashType::Danger, $this->translator->trans('Candidate not found'));
return $this->redirectToRoute('app_quiz_enter_name', ['seasonCode' => $season->getSeasonCode()]); return $this->redirectToRoute('tvdt_quiz_enter_name', ['seasonCode' => $season->seasonCode]);
} }
$quiz = $season->getActiveQuiz(); $quiz = $season->activeQuiz;
if (!$quiz instanceof Quiz) { if (!$quiz instanceof Quiz) {
$this->addFlash(FlashType::Warning, $this->translator->trans('There is no active quiz')); $this->addFlash(FlashType::Warning, $this->translator->trans('There is no active quiz'));
return $this->redirectToRoute('app_quiz_enter_name', ['seasonCode' => $season->getSeasonCode()]); return $this->redirectToRoute('tvdt_quiz_enter_name', ['seasonCode' => $season->seasonCode]);
} }
if ('POST' === $request->getMethod()) { if ('POST' === $request->getMethod()) {
$answer = $answerRepository->findOneBy(['id' => $request->request->get('answer')]); $answer = $answerRepository->findOneBy(['id' => $request->request->get('answer')]);
if (!$answer instanceof Answer) { if (!$answer instanceof Answer) {
throw new BadRequestException('Invalid Answer ID'); throw new BadRequestHttpException('Invalid Answer ID');
} }
$givenAnswer = new GivenAnswer($candidate, $answer->getQuestion()->getQuiz(), $answer); $givenAnswer = new GivenAnswer($candidate, $answer->question->quiz, $answer);
$givenAnswerRepository->save($givenAnswer); $givenAnswerRepository->save($givenAnswer);
return $this->redirectToRoute('app_quiz_quiz_page', ['seasonCode' => $season->getSeasonCode(), 'nameHash' => $nameHash]); return $this->redirectToRoute('tvdt_quiz_quiz_page', ['seasonCode' => $season->seasonCode, 'nameHash' => $nameHash]);
} }
$question = $questionRepository->findNextQuestionForCandidate($candidate); $question = $questionRepository->findNextQuestionForCandidate($candidate);
@@ -123,11 +120,11 @@ final class QuizController extends AbstractController
if (!$question instanceof Question) { if (!$question instanceof Question) {
$this->addFlash(FlashType::Success, $this->translator->trans('Quiz completed')); $this->addFlash(FlashType::Success, $this->translator->trans('Quiz completed'));
return $this->redirectToRoute('app_quiz_enter_name', ['seasonCode' => $season->getSeasonCode()]); return $this->redirectToRoute('tvdt_quiz_enter_name', ['seasonCode' => $season->seasonCode]);
} }
$quizCandidateRepository->createIfNotExist($quiz, $candidate); $quizCandidateRepository->createIfNotExist($quiz, $candidate);
return $this->render('quiz/question.twig', ['candidate' => $candidate, 'question' => $question]); return $this->render('quiz/question.twig', ['candidate' => $candidate, 'question' => $question, 'season' => $season]);
} }
} }

View File

@@ -2,33 +2,36 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Controller; namespace Tvdt\Controller;
use App\Entity\User;
use App\Form\RegistrationFormType;
use App\Repository\UserRepository;
use App\Security\EmailVerifier;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bridge\Twig\Mime\TemplatedEmail; use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\SecurityBundle\Security; use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use SymfonyCasts\Bundle\VerifyEmail\Exception\VerifyEmailExceptionInterface; use SymfonyCasts\Bundle\VerifyEmail\Exception\VerifyEmailExceptionInterface;
use Tvdt\Entity\User;
use Tvdt\Form\RegistrationFormType;
use Tvdt\Repository\UserRepository;
use Tvdt\Security\EmailVerifier;
final class RegistrationController extends AbstractController final class RegistrationController extends AbstractController
{ {
public function __construct(private readonly EmailVerifier $emailVerifier, private readonly TranslatorInterface $translator) {} public function __construct(private readonly EmailVerifier $emailVerifier, private readonly TranslatorInterface $translator) {}
#[Route('/register', name: 'app_register')] #[Route('/register', name: 'tvdt_register')]
public function register( public function register(
Request $request, Request $request,
UserPasswordHasherInterface $userPasswordHasher, UserPasswordHasherInterface $userPasswordHasher,
Security $security, Security $security,
EntityManagerInterface $entityManager, EntityManagerInterface $entityManager,
LoggerInterface $logger,
): Response { ): Response {
$user = new User(); $user = new User();
$form = $this->createForm(RegistrationFormType::class, $user); $form = $this->createForm(RegistrationFormType::class, $user);
@@ -38,18 +41,22 @@ final class RegistrationController extends AbstractController
/** @var string $plainPassword */ /** @var string $plainPassword */
$plainPassword = $form->get('plainPassword')->getData(); $plainPassword = $form->get('plainPassword')->getData();
$user->setPassword($userPasswordHasher->hashPassword($user, $plainPassword)); $user->password = $userPasswordHasher->hashPassword($user, $plainPassword);
$entityManager->persist($user); $entityManager->persist($user);
$entityManager->flush(); $entityManager->flush();
// generate a signed url and email it to the user try {
$this->emailVerifier->sendEmailConfirmation('app_verify_email', $user, // generate a signed url and email it to the user
(new TemplatedEmail()) $this->emailVerifier->sendEmailConfirmation('tvdt_verify_email', $user,
->to((string) $user->getEmail()) new TemplatedEmail()
->subject($this->translator->trans('Please Confirm your Email')) ->to($user->email)
->htmlTemplate('backoffice/registration/confirmation_email.html.twig'), ->subject($this->translator->trans('Please Confirm your Email'))
); ->htmlTemplate('backoffice/registration/confirmation_email.html.twig'),
);
} catch (TransportExceptionInterface $e) {
$logger->error($e->getMessage());
}
$response = $security->login($user, 'form_login', 'main'); $response = $security->login($user, 'form_login', 'main');
\assert($response instanceof Response); \assert($response instanceof Response);
@@ -62,19 +69,19 @@ final class RegistrationController extends AbstractController
]); ]);
} }
#[Route('/verify/email', name: 'app_verify_email')] #[Route('/verify/email', name: 'tvdt_verify_email')]
public function verifyUserEmail(Request $request, TranslatorInterface $translator, UserRepository $userRepository): Response public function verifyUserEmail(Request $request, TranslatorInterface $translator, UserRepository $userRepository): Response
{ {
$id = $request->query->get('id'); $id = $request->query->get('id');
if (null === $id) { if (null === $id) {
return $this->redirectToRoute('app_register'); return $this->redirectToRoute('tvdt_register');
} }
$user = $userRepository->find($id); $user = $userRepository->find($id);
if (null === $user) { if (null === $user) {
return $this->redirectToRoute('app_register'); return $this->redirectToRoute('tvdt_register');
} }
// validate email confirmation link, sets User::isVerified=true and persists // validate email confirmation link, sets User::isVerified=true and persists
@@ -83,11 +90,11 @@ final class RegistrationController extends AbstractController
} catch (VerifyEmailExceptionInterface $verifyEmailException) { } catch (VerifyEmailExceptionInterface $verifyEmailException) {
$this->addFlash('verify_email_error', $translator->trans($verifyEmailException->getReason(), [], 'VerifyEmailBundle')); $this->addFlash('verify_email_error', $translator->trans($verifyEmailException->getReason(), [], 'VerifyEmailBundle'));
return $this->redirectToRoute('app_register'); return $this->redirectToRoute('tvdt_register');
} }
$this->addFlash('success', $this->translator->trans('Your email address has been verified.')); $this->addFlash('success', $this->translator->trans('Your email address has been verified.'));
return $this->redirectToRoute('app_backoffice_index'); return $this->redirectToRoute('tvdt_backoffice_index');
} }
} }

View File

@@ -2,15 +2,15 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\DataFixtures; namespace Tvdt\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\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Tvdt\Entity\Answer;
use Tvdt\Entity\Candidate;
use Tvdt\Entity\Question;
use Tvdt\Entity\Quiz;
use Tvdt\Entity\Season;
class KrtekFixtures extends Fixture class KrtekFixtures extends Fixture
{ {
@@ -19,8 +19,9 @@ class KrtekFixtures extends Fixture
$season = new Season(); $season = new Season();
$manager->persist($season); $manager->persist($season);
$season->setName('Krtek Weekend') $season->name = 'Krtek Weekend';
->setSeasonCode('krtek') $season->seasonCode = 'krtek';
$season
->addCandidate(new Candidate('Claudia')) ->addCandidate(new Candidate('Claudia'))
->addCandidate(new Candidate('Eelco')) ->addCandidate(new Candidate('Eelco'))
->addCandidate(new Candidate('Elise')) ->addCandidate(new Candidate('Elise'))
@@ -35,338 +36,341 @@ class KrtekFixtures extends Fixture
->addCandidate(new Candidate('Robbert')) ->addCandidate(new Candidate('Robbert'))
->addCandidate(new Candidate('Tom')); ->addCandidate(new Candidate('Tom'));
$quiz1 = $this->createQuiz1($season); $quiz1 = $this->createQuiz1($season);
$season->addQuiz($quiz1) $season->addQuiz($quiz1);
->setActiveQuiz($quiz1) $season->activeQuiz = $quiz1;
->addQuiz($this->createQuiz2($season)); $season->addQuiz($this->createQuiz2($season));
$manager->flush(); $manager->flush();
} }
private function createQuiz1(Season $season): Quiz private function createQuiz1(Season $season): Quiz
{ {
return (new Quiz()) $quiz = new Quiz();
->setName('Quiz 1') $quiz->name = 'Quiz 1';
->setSeason($season) $quiz->season = $season;
->addQuestion((new Question()) $q = new Question();
->setQuestion('Is de Krtek een man of een vrouw?') $q->question = 'Is de Krtek een man of een vrouw?';
->addAnswer(new Answer('Vrouw', true)) $q->addAnswer(new Answer('Vrouw', true))
->addAnswer(new Answer('Man')) ->addAnswer(new Answer('Man'));
->setOrdering(1), $q->ordering = 1;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Hoeveel broers heeft de Krtek?') $q->question = 'Hoeveel broers heeft de Krtek?';
->addAnswer(new Answer('Geen', true)) $q->addAnswer(new Answer('Geen', true))
->addAnswer(new Answer('1')) ->addAnswer(new Answer('1'))
->addAnswer(new Answer('2')) ->addAnswer(new Answer('2'));
->setOrdering(2), $q->ordering = 2;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Wat is de lievelingsfeestdag van de Krtek?') $q->question = 'Wat is de lievelingsfeestdag van de Krtek?';
->addAnswer(new Answer('Geen')) $q->addAnswer(new Answer('Geen'))
->addAnswer(new Answer('Diens eigen verjaardag')) ->addAnswer(new Answer('Diens eigen verjaardag'))
->addAnswer(new Answer('Koningsdag')) ->addAnswer(new Answer('Koningsdag'))
->addAnswer(new Answer('Kerst', true)) ->addAnswer(new Answer('Kerst', true))
->addAnswer(new Answer('Oud en Nieuw')) ->addAnswer(new Answer('Oud en Nieuw'));
->setOrdering(3), $q->ordering = 3;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Hoe kwam de Krtek naar Kersteren vandaag?') $q->question = 'Hoe kwam de Krtek naar Kersteren vandaag?';
->addAnswer(new Answer('Met het OV', true)) $q->addAnswer(new Answer('Met het OV', true))
->addAnswer(new Answer('Met de auto')) ->addAnswer(new Answer('Met de auto'));
->setOrdering(4), $q->ordering = 4;
) $quiz->addQuestion($q);
->addQuestion((new Question())
->setQuestion('Met wie keek de Krtek 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))
->setOrdering(5),
)
->addQuestion((new Question()) $q = new Question();
->setQuestion('Welk advies zou de Krtek zichzelf als kind geven?') $q->question = 'Met wie keek de Krtek video bij binnenkomst?';
->addAnswer(new Answer('Geef je vader een knuffel.')) $q->addAnswer(new Answer('Claudia'))
->addAnswer(new Answer('Trek je wat minder aan van anderen.')) ->addAnswer(new Answer('Eelco'))
->addAnswer(new Answer('Luister meer naar je eigen gevoel in plaats van naar wat anderen vinden.')) ->addAnswer(new Answer('Elise'))
->addAnswer(new Answer('Stel niet alles tot het laatste moment uit.')) ->addAnswer(new Answer('Gert-Jan'))
->addAnswer(new Answer('Altijd doorgaan.')) ->addAnswer(new Answer('Iris'))
->addAnswer(new Answer('Probeer ook eens buiten de lijntjes te kleuren', true)) ->addAnswer(new Answer('Jari'))
->addAnswer(new Answer('Ga als je groot bent op groepsreis! ')) ->addAnswer(new Answer('Lara'))
->addAnswer(new Answer('Trek minder aan van de mening van anderen, het is oké om anders te zijn.')) ->addAnswer(new Answer('Lotte'))
->setOrdering(6), ->addAnswer(new Answer('Myrthe'))
) ->addAnswer(new Answer('Philine'))
->addAnswer(new Answer('Remy'))
->addAnswer(new Answer('Robbert'))
->addAnswer(new Answer('Tom', true));
$q->ordering = 5;
$quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Wat voor soort schoenen droeg de Krtek bij het diner?') $q->question = 'Welk advies zou de Krtek zichzelf als kind geven?';
->addAnswer(new Answer('Sneakers')) $q->addAnswer(new Answer('Geef je vader een knuffel.'))
->addAnswer(new Answer('Wandel-/bergschoenen', true)) ->addAnswer(new Answer('Trek je wat minder aan van anderen.'))
->addAnswer(new Answer('Lederen schoenen')) ->addAnswer(new Answer('Luister meer naar je eigen gevoel in plaats van naar wat anderen vinden.'))
->addAnswer(new Answer('Pantoffels')) ->addAnswer(new Answer('Stel niet alles tot het laatste moment uit.'))
->addAnswer(new Answer('Hakken')) ->addAnswer(new Answer('Altijd doorgaan.'))
->addAnswer(new Answer('Geen schoenen, alleen sokken')) ->addAnswer(new Answer('Probeer ook eens buiten de lijntjes te kleuren', true))
->setOrdering(7), ->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.'));
$q->ordering = 6;
$quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Met welk vervoersmiddel reist de Krtek het liefste?') $q->question = 'Wat voor soort schoenen droeg de Krtek bij het diner?';
->addAnswer(new Answer('Fiets', true)) $q->addAnswer(new Answer('Sneakers'))
->addAnswer(new Answer('Auto')) ->addAnswer(new Answer('Wandel-/bergschoenen', true))
->addAnswer(new Answer('Trein')) ->addAnswer(new Answer('Lederen schoenen'))
->setOrdering(8), ->addAnswer(new Answer('Pantoffels'))
) ->addAnswer(new Answer('Hakken'))
->addAnswer(new Answer('Geen schoenen, alleen sokken'));
$q->ordering = 7;
$quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Heeft de Krtek een eigen auto?') $q->question = 'Met welk vervoersmiddel reist de Krtek het liefste?';
->addAnswer(new Answer('Ja')) $q->addAnswer(new Answer('Fiets', true))
->addAnswer(new Answer('Nee', true)) ->addAnswer(new Answer('Auto'))
->setOrdering(9), ->addAnswer(new Answer('Trein'));
) $q->ordering = 8;
$quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Van wie is de quote die de Krtek gepakt heeft') $q->question = 'Heeft de Krtek een eigen auto?';
->addAnswer(new Answer('Karen')) $q->addAnswer(new Answer('Ja'))
->addAnswer(new Answer('Gilles de Coster')) ->addAnswer(new Answer('Nee', true));
->addAnswer(new Answer('Kees Tol')) $q->ordering = 9;
->addAnswer(new Answer('Harry en John')) $quiz->addQuestion($q);
->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))
->setOrdering(10),
)
->addQuestion((new Question()) $q = new Question();
->setQuestion('Zou de Krtek molboekjes, jokers, vrijstellingen of topitos uit iemands rugzak stelen om te kunnen winnen?') $q->question = 'Van wie is de quote die de Krtek gepakt heeft';
->addAnswer(new Answer('Ja')) $q->addAnswer(new Answer('Karen'))
->addAnswer(new Answer('Nee', true)) ->addAnswer(new Answer('Gilles de Coster'))
->setOrdering(11), ->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));
$q->ordering = 10;
$quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('In wat voor bed slaapt de Krtek dit weekend?') $q->question = 'Zou de Krtek molboekjes, jokers, vrijstellingen of topitos uit iemands rugzak stelen om te kunnen winnen?';
->addAnswer(new Answer('Éénpersoons, losstaand bed')) $q->addAnswer(new Answer('Ja'))
->addAnswer(new Answer('Éénpersoonsbed, tegen een ander bed aan', true)) ->addAnswer(new Answer('Nee', true));
->addAnswer(new Answer('Tweepersoons bed')) $q->ordering = 11;
->setOrdering(12), $quiz->addQuestion($q);
)
->addQuestion((new Question()) $q = new Question();
->setQuestion('Hoeveel jaar heeft de Krtek gedaan over de middelbare school?') $q->question = 'In wat voor bed slaapt de Krtek dit weekend?';
->addAnswer(new Answer('5')) $q->addAnswer(new Answer('Éénpersoons, losstaand bed'))
->addAnswer(new Answer('6', true)) ->addAnswer(new Answer('Éénpersoonsbed, tegen een ander bed aan', true))
->addAnswer(new Answer('7')) ->addAnswer(new Answer('Tweepersoons bed'));
->addAnswer(new Answer('8')) $q->ordering = 12;
->setOrdering(13), $quiz->addQuestion($q);
)
->addQuestion((new Question()) $q = new Question();
->setQuestion('Waar zat de Krtek aan tafel bij het diner?') $q->question = 'Hoeveel jaar heeft de Krtek gedaan over de middelbare school?';
->addAnswer(new Answer('Met de rug naar de accommodatie')) $q->addAnswer(new Answer('5'))
->addAnswer(new Answer('Met de rug naar de buitenmuur', true)) ->addAnswer(new Answer('6', true))
->setOrdering(14), ->addAnswer(new Answer('7'))
) ->addAnswer(new Answer('8'));
$q->ordering = 13;
$quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Wie is de Krtek?') $q->question = 'Waar zat de Krtek aan tafel bij het diner?';
->addAnswer(new Answer('Claudia', true)) $q->addAnswer(new Answer('Met de rug naar de accommodatie'))
->addAnswer(new Answer('Eelco')) ->addAnswer(new Answer('Met de rug naar de buitenmuur', true));
->addAnswer(new Answer('Elise')) $q->ordering = 14;
->addAnswer(new Answer('Gert-Jan')) $quiz->addQuestion($q);
->addAnswer(new Answer('Iris'))
->addAnswer(new Answer('Jari')) $q = new Question();
->addAnswer(new Answer('Lara')) $q->question = 'Wie is de Krtek?';
->addAnswer(new Answer('Lotte')) $q->addAnswer(new Answer('Claudia', true))
->addAnswer(new Answer('Myrthe')) ->addAnswer(new Answer('Eelco'))
->addAnswer(new Answer('Philine')) ->addAnswer(new Answer('Elise'))
->addAnswer(new Answer('Remy')) ->addAnswer(new Answer('Gert-Jan'))
->addAnswer(new Answer('Robbert')) ->addAnswer(new Answer('Iris'))
->addAnswer(new Answer('Tom')) ->addAnswer(new Answer('Jari'))
->setOrdering(15), ->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'));
$q->ordering = 15;
$quiz->addQuestion($q);
return $quiz;
} }
private function createQuiz2(Season $season): Quiz private function createQuiz2(Season $season): Quiz
{ {
return (new Quiz()) $quiz = new Quiz();
->setName('Quiz 2') $quiz->name = 'Quiz 2';
->setSeason($season) $quiz->season = $season;
->addQuestion((new Question()) $q = new Question();
->setQuestion('Is de Krtek een man of een vrouw?') $q->question = 'Is de Krtek een man of een vrouw?';
->addAnswer(new Answer('Man')) $q->addAnswer(new Answer('Man'))
->addAnswer(new Answer('Vrouw', true)) ->addAnswer(new Answer('Vrouw', true));
->setOrdering(1), $q->ordering = 1;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Heeft de Krtek dieetwensen of allergieën?') $q->question = 'Heeft de Krtek dieetwensen of allergieën?';
->addAnswer(new Answer('nee')) $q->addAnswer(new Answer('nee'))
->addAnswer(new Answer('De Krtek is vegetariër', true)) ->addAnswer(new Answer('De Krtek is vegetariër', true))
->addAnswer(new Answer('De Krtek is flexitariër')) ->addAnswer(new Answer('De Krtek is flexitariër'))
->addAnswer(new Answer('De Krtek heeft een allergie')) ->addAnswer(new Answer('De Krtek heeft een allergie'))
->addAnswer(new Answer('De Krtek heeft een intolerantie')) ->addAnswer(new Answer('De Krtek heeft een intolerantie'))
->addAnswer(new Answer('De Krtek eet geen rundvlees')) ->addAnswer(new Answer('De Krtek eet geen rundvlees'))
->addAnswer(new Answer('De Krtek eet geen waterdieren')) ->addAnswer(new Answer('De Krtek eet geen waterdieren'));
->setOrdering(2), $q->ordering = 2;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Hoe heet het huisdier/de huisdieren van de Krtek?') $q->question = 'Hoe heet het huisdier/de huisdieren van de Krtek?';
->addAnswer(new Answer('Amy, Karel en Floyd')) $q->addAnswer(new Answer('Amy, Karel en Floyd'))
->addAnswer(new Answer('Flip en Majoor')) ->addAnswer(new Answer('Flip en Majoor'))
->addAnswer(new Answer('Benji')) ->addAnswer(new Answer('Benji'))
->addAnswer(new Answer('Sini')) ->addAnswer(new Answer('Sini'))
->addAnswer(new Answer('Tom')) ->addAnswer(new Answer('Tom'))
->addAnswer(new Answer('De huisdieren van de Krtek hebben geen naam')) ->addAnswer(new Answer('De huisdieren van de Krtek hebben geen naam'))
->addAnswer(new Answer('De Krtek heeft geen huisdieren', true)) ->addAnswer(new Answer('De Krtek heeft geen huisdieren', true));
->setOrdering(3), $q->ordering = 3;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Wat dronk de Krtek deze ochtend bij het ontbijt?') $q->question = 'Wat dronk de Krtek deze ochtend bij het ontbijt?';
->addAnswer(new Answer('Koffie')) $q->addAnswer(new Answer('Koffie'))
->addAnswer(new Answer('Thee')) ->addAnswer(new Answer('Thee'))
->addAnswer(new Answer('Water', true)) ->addAnswer(new Answer('Water', true))
->addAnswer(new Answer('Melk')) ->addAnswer(new Answer('Melk'))
->addAnswer(new Answer('Sap')) ->addAnswer(new Answer('Sap'))
->addAnswer(new Answer('Niks')) ->addAnswer(new Answer('Niks'));
->setOrdering(4), $q->ordering = 4;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Waar ging de eerste vakantie die de Krtek zich nog herinnert heen?') $q->question = 'Waar ging de eerste vakantie die de Krtek zich nog herinnert heen?';
->addAnswer(new Answer('Denemarken')) $q->addAnswer(new Answer('Denemarken'))
->addAnswer(new Answer('Drenthe')) ->addAnswer(new Answer('Drenthe'))
->addAnswer(new Answer('Mallorca')) ->addAnswer(new Answer('Mallorca'))
->addAnswer(new Answer('Marokko')) ->addAnswer(new Answer('Marokko'))
->addAnswer(new Answer('Oostenrijk')) ->addAnswer(new Answer('Oostenrijk'))
->addAnswer(new Answer('Turkije')) ->addAnswer(new Answer('Turkije'))
->addAnswer(new Answer('Zweden', true)) ->addAnswer(new Answer('Zweden', true));
->setOrdering(5), $q->ordering = 5;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Met welk groepje ging de Krtek als eerste het Douanespel in?') $q->question = 'Met welk groepje ging de Krtek als eerste het Douanespel in?';
->addAnswer(new Answer('Het eerste groepje', true)) $q->addAnswer(new Answer('Het eerste groepje', true))
->addAnswer(new Answer('Het tweede groepje')) ->addAnswer(new Answer('Het tweede groepje'))
->addAnswer(new Answer('Het derde groepje')) ->addAnswer(new Answer('Het derde groepje'))
->addAnswer(new Answer('Het vierde groepje')) ->addAnswer(new Answer('Het vierde groepje'))
->addAnswer(new Answer('Het vijfde groepje')) ->addAnswer(new Answer('Het vijfde groepje'));
->setOrdering(6), $q->ordering = 6;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Gelooft de Krtek ergens in?') $q->question = 'Gelooft de Krtek ergens in?';
->addAnswer(new Answer('Nee')) $q->addAnswer(new Answer('Nee'))
->addAnswer(new Answer('Het universum', true)) ->addAnswer(new Answer('Het universum', true))
->addAnswer(new Answer('Toeval')) ->addAnswer(new Answer('Toeval'))
->addAnswer(new Answer('De Krtek is hindoeïstisch')) ->addAnswer(new Answer('De Krtek is hindoeïstisch'));
->setOrdering(7), $q->ordering = 7;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('At de Krtek op vrijdagavond heksenkaas tijdens het diner?') $q->question = 'At de Krtek op vrijdagavond heksenkaas tijdens het diner?';
->addAnswer(new Answer('Ja', true)) $q->addAnswer(new Answer('Ja', true))
->addAnswer(new Answer('Nee')) ->addAnswer(new Answer('Nee'));
->setOrdering(8), $q->ordering = 8;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Hoe laat ging de Krtek gisteravond naar bed?') $q->question = 'Hoe laat ging de Krtek gisteravond naar bed?';
->addAnswer(new Answer('Tussen 0:00 en 0:59 uur')) $q->addAnswer(new Answer('Tussen 0:00 en 0:59 uur'))
->addAnswer(new Answer('Tussen 1:00 en 1:59 uur', true)) ->addAnswer(new Answer('Tussen 1:00 en 1:59 uur', true))
->addAnswer(new Answer('Tussen 2:00 en 2:59 uur')) ->addAnswer(new Answer('Tussen 2:00 en 2:59 uur'))
->addAnswer(new Answer('Na 3:00')) ->addAnswer(new Answer('Na 3:00'));
->setOrdering(9), $q->ordering = 9;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Hoeveel batterijen heeft de Krtek naar het bord gebracht bij het douanespel?') $q->question = 'Hoeveel batterijen heeft de Krtek naar het bord gebracht bij het douanespel?';
->addAnswer(new Answer('1')) $q->addAnswer(new Answer('1'))
->addAnswer(new Answer('2')) ->addAnswer(new Answer('2'))
->addAnswer(new Answer('3')) ->addAnswer(new Answer('3'))
->addAnswer(new Answer('geen', true)) ->addAnswer(new Answer('geen', true));
->setOrdering(10), $q->ordering = 10;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Wat keek de Krtek als kind graag op TV?') $q->question = 'Wat keek de Krtek als kind graag op TV?';
->addAnswer(new Answer('Digimon', true)) $q->addAnswer(new Answer('Digimon', true))
->addAnswer(new Answer('Floris')) ->addAnswer(new Answer('Floris'))
->addAnswer(new Answer('Het huis Anubis')) ->addAnswer(new Answer('Het huis Anubis'))
->addAnswer(new Answer('Sesamstraat')) ->addAnswer(new Answer('Sesamstraat'))
->addAnswer(new Answer('Spongebob Squarepants')) ->addAnswer(new Answer('Spongebob Squarepants'))
->addAnswer(new Answer('Teletubbies')) ->addAnswer(new Answer('Teletubbies'));
->setOrdering(11), $q->ordering = 11;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Waarin zat op de heenreis de bagage van de Krtek (voornamelijk)?') $q->question = 'Waarin zat op de heenreis de bagage van de Krtek (voornamelijk)?';
->addAnswer(new Answer('In koffer(s)', true)) $q->addAnswer(new Answer('In koffer(s)', true))
->addAnswer(new Answer('In losse tas(sen)')) ->addAnswer(new Answer('In losse tas(sen)'))
->addAnswer(new Answer('In een rugzak')) ->addAnswer(new Answer('In een rugzak'));
->setOrdering(12), $q->ordering = 12;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Van welk geluid gaan de haren van de Krtek overeind staan?') $q->question = 'Van welk geluid gaan de haren van de Krtek overeind staan?';
->addAnswer(new Answer('Een vork die door een metalen pan krast ')) $q->addAnswer(new Answer('Een vork die door een metalen pan krast '))
->addAnswer(new Answer('Smakkende mensen')) ->addAnswer(new Answer('Smakkende mensen'))
->addAnswer(new Answer('Een vork die over een bord schraapt')) ->addAnswer(new Answer('Een vork die over een bord schraapt'))
->addAnswer(new Answer('Schuren met schuurpapier')) ->addAnswer(new Answer('Schuren met schuurpapier'))
->addAnswer(new Answer('Nagels op een krijtbord')) ->addAnswer(new Answer('Nagels op een krijtbord'))
->addAnswer(new Answer('Servies dat tegen elkaar klettert')) ->addAnswer(new Answer('Servies dat tegen elkaar klettert'))
->addAnswer(new Answer('Het geroekoe van een duif', true)) ->addAnswer(new Answer('Het geroekoe van een duif', true))
->addAnswer(new Answer('Piepschuim')) ->addAnswer(new Answer('Piepschuim'));
->setOrdering(13), $q->ordering = 13;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Wilde de Krtek penningmeester worden?') $q->question = 'Wilde de Krtek penningmeester worden?';
->addAnswer(new Answer('Ja')) $q->addAnswer(new Answer('Ja'))
->addAnswer(new Answer('Nee', true)) ->addAnswer(new Answer('Nee', true));
->setOrdering(14), $q->ordering = 14;
) $quiz->addQuestion($q);
->addQuestion((new Question()) $q = new Question();
->setQuestion('Wie is de Krtek?') $q->question = 'Wie is de Krtek?';
->addAnswer(new Answer('Claudia', true)) $q->addAnswer(new Answer('Claudia', true))
->addAnswer(new Answer('Eelco')) ->addAnswer(new Answer('Eelco'))
->addAnswer(new Answer('Elise')) ->addAnswer(new Answer('Elise'))
->addAnswer(new Answer('Gert-Jan')) ->addAnswer(new Answer('Gert-Jan'))
->addAnswer(new Answer('Iris')) ->addAnswer(new Answer('Iris'))
->addAnswer(new Answer('Jari')) ->addAnswer(new Answer('Jari'))
->addAnswer(new Answer('Lara')) ->addAnswer(new Answer('Lara'))
->addAnswer(new Answer('Lotte')) ->addAnswer(new Answer('Lotte'))
->addAnswer(new Answer('Myrthe')) ->addAnswer(new Answer('Myrthe'))
->addAnswer(new Answer('Philine')) ->addAnswer(new Answer('Philine'))
->addAnswer(new Answer('Remy')) ->addAnswer(new Answer('Remy'))
->addAnswer(new Answer('Robbert')) ->addAnswer(new Answer('Robbert'))
->addAnswer(new Answer('Tom')) ->addAnswer(new Answer('Tom'));
->setOrdering(15), $q->ordering = 15;
) $quiz->addQuestion($q);
;
return $quiz;
} }
} }

19
src/Dto/Result.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Tvdt\Dto;
use Symfony\Component\Uid\Uuid;
final readonly class Result
{
public function __construct(
public Uuid $id,
public string $name,
public int $correct,
public float $corrections,
public \DateInterval $time,
public float $score,
) {}
}

View File

@@ -2,129 +2,59 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Entity; namespace Tvdt\Entity;
use App\Repository\AnswerRepository;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use Symfony\Bridge\Doctrine\Types\UuidType; use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid; use Symfony\Component\Uid\Uuid;
use Tvdt\Repository\AnswerRepository;
#[ORM\Entity(repositoryClass: AnswerRepository::class)] #[ORM\Entity(repositoryClass: AnswerRepository::class)]
class Answer final class Answer
{ {
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME)] #[ORM\Column(type: UuidType::NAME)]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
#[ORM\GeneratedValue(strategy: 'CUSTOM')] #[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: UuidGenerator::class)] #[ORM\Id]
private Uuid $id; public private(set) Uuid $id;
#[ORM\Column(type: Types::SMALLINT, options: ['default' => 0])] #[ORM\Column(type: Types::SMALLINT, options: ['default' => 0])]
private int $ordering = 0; public int $ordering = 0;
#[ORM\ManyToOne(inversedBy: 'answers')]
#[ORM\JoinColumn(nullable: false)] #[ORM\JoinColumn(nullable: false)]
private Question $question; #[ORM\ManyToOne(inversedBy: 'answers')]
public Question $question;
/** @var Collection<int, Candidate> */ /** @var Collection<int, Candidate> */
#[ORM\ManyToMany(targetEntity: Candidate::class, inversedBy: 'answersOnCandidate')] #[ORM\ManyToMany(targetEntity: Candidate::class, inversedBy: 'answersOnCandidate')]
private Collection $candidates; public private(set) Collection $candidates;
/** @var Collection<int, GivenAnswer> */ /** @var Collection<int, GivenAnswer> */
#[ORM\OneToMany(targetEntity: GivenAnswer::class, mappedBy: 'answer', orphanRemoval: true)] #[ORM\OneToMany(targetEntity: GivenAnswer::class, mappedBy: 'answer', orphanRemoval: true)]
private Collection $givenAnswers; public private(set) Collection $givenAnswers;
public function __construct( public function __construct(
#[ORM\Column(length: 255)] #[ORM\Column(length: 255)]
private string $text, public string $text,
#[ORM\Column] #[ORM\Column]
private bool $isRightAnswer = false, public bool $isRightAnswer = false,
) { ) {
$this->candidates = new ArrayCollection(); $this->candidates = new ArrayCollection();
$this->givenAnswers = new ArrayCollection(); $this->givenAnswers = new ArrayCollection();
} }
public function getId(): Uuid public function addCandidate(Candidate $candidate): void
{
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)) { if (!$this->candidates->contains($candidate)) {
$this->candidates->add($candidate); $this->candidates->add($candidate);
} }
return $this;
} }
public function removeCandidate(Candidate $candidate): static public function removeCandidate(Candidate $candidate): void
{ {
$this->candidates->removeElement($candidate); $this->candidates->removeElement($candidate);
return $this;
}
/** @return Collection<int, GivenAnswer> */
public function getGivenAnswers(): Collection
{
return $this->givenAnswers;
}
public function getOrdering(): int
{
return $this->ordering;
}
public function setOrdering(int $ordering): self
{
$this->ordering = $ordering;
return $this;
} }
} }

View File

@@ -2,120 +2,67 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Entity; namespace Tvdt\Entity;
use App\Helpers\Base64;
use App\Repository\CandidateRepository;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use Symfony\Bridge\Doctrine\Types\UuidType; use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid; use Symfony\Component\Uid\Uuid;
use Tvdt\Helpers\Base64;
use Tvdt\Repository\CandidateRepository;
#[ORM\Entity(repositoryClass: CandidateRepository::class)] #[ORM\Entity(repositoryClass: CandidateRepository::class)]
#[ORM\UniqueConstraint(fields: ['name', 'season'])] #[ORM\UniqueConstraint(fields: ['name', 'season'])]
class Candidate final class Candidate
{ {
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME, unique: true)] #[ORM\Column(type: UuidType::NAME, unique: true)]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
#[ORM\GeneratedValue(strategy: 'CUSTOM')] #[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: UuidGenerator::class)] #[ORM\Id]
private Uuid $id; public private(set) Uuid $id;
#[ORM\ManyToOne(inversedBy: 'candidates')]
#[ORM\JoinColumn(nullable: false)] #[ORM\JoinColumn(nullable: false)]
private Season $season; #[ORM\ManyToOne(inversedBy: 'candidates')]
public Season $season;
/** @var Collection<int, Answer> */ /** @var Collection<int, Answer> */
#[ORM\ManyToMany(targetEntity: Answer::class, mappedBy: 'candidates')] #[ORM\ManyToMany(targetEntity: Answer::class, mappedBy: 'candidates')]
private Collection $answersOnCandidate; public private(set) Collection $answersOnCandidate;
/** @var Collection<int, GivenAnswer> */ /** @var Collection<int, GivenAnswer> */
#[ORM\OneToMany(targetEntity: GivenAnswer::class, mappedBy: 'candidate', orphanRemoval: true)] #[ORM\OneToMany(targetEntity: GivenAnswer::class, mappedBy: 'candidate', orphanRemoval: true)]
private Collection $givenAnswers; public private(set) Collection $givenAnswers;
/** @var Collection<int, QuizCandidate> */ /** @var Collection<int, QuizCandidate> */
#[ORM\OneToMany(targetEntity: QuizCandidate::class, mappedBy: 'candidate', orphanRemoval: true)] #[ORM\OneToMany(targetEntity: QuizCandidate::class, mappedBy: 'candidate', orphanRemoval: true)]
private Collection $quizData; public private(set) Collection $quizData;
public string $nameHash {
get => Base64::base64UrlEncode($this->name);
}
public function __construct( public function __construct(
#[ORM\Column(length: 16)] #[ORM\Column(length: 16)]
private string $name, public string $name,
) { ) {
$this->answersOnCandidate = new ArrayCollection(); $this->answersOnCandidate = new ArrayCollection();
$this->givenAnswers = new ArrayCollection(); $this->givenAnswers = new ArrayCollection();
$this->quizData = new ArrayCollection(); $this->quizData = new ArrayCollection();
} }
public function getId(): Uuid public function addAnswersOnCandidate(Answer $answersOnCandidate): void
{
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)) { if (!$this->answersOnCandidate->contains($answersOnCandidate)) {
$this->answersOnCandidate->add($answersOnCandidate); $this->answersOnCandidate->add($answersOnCandidate);
$answersOnCandidate->addCandidate($this); $answersOnCandidate->addCandidate($this);
} }
return $this;
} }
public function removeAnswersOnCandidate(Answer $answersOnCandidate): static public function removeAnswersOnCandidate(Answer $answersOnCandidate): void
{ {
if ($this->answersOnCandidate->removeElement($answersOnCandidate)) { if ($this->answersOnCandidate->removeElement($answersOnCandidate)) {
$answersOnCandidate->removeCandidate($this); $answersOnCandidate->removeCandidate($this);
} }
return $this;
}
/** @return Collection<int, GivenAnswer> */
public function getGivenAnswers(): Collection
{
return $this->givenAnswers;
}
/** @return Collection<int, QuizCandidate> */
public function getQuizData(): Collection
{
return $this->quizData;
}
public function getNameHash(): string
{
return Base64::base64UrlEncode($this->name);
} }
} }

View File

@@ -2,71 +2,47 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Entity; namespace Tvdt\Entity;
use App\Repository\EliminationRepository;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Safe\DateTimeImmutable; use Safe\DateTimeImmutable;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use Symfony\Bridge\Doctrine\Types\UuidType; use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\HttpFoundation\InputBag; use Symfony\Component\HttpFoundation\InputBag;
use Symfony\Component\Uid\Uuid; use Symfony\Component\Uid\Uuid;
use Tvdt\Repository\EliminationRepository;
#[ORM\Entity(repositoryClass: EliminationRepository::class)] #[ORM\Entity(repositoryClass: EliminationRepository::class)]
#[ORM\HasLifecycleCallbacks] #[ORM\HasLifecycleCallbacks]
class Elimination final class Elimination
{ {
public const string SCREEN_GREEN = 'green'; public const string SCREEN_GREEN = 'green';
public const string SCREEN_RED = 'red'; public const string SCREEN_RED = 'red';
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME, unique: true)] #[ORM\Column(type: UuidType::NAME, unique: true)]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
#[ORM\GeneratedValue(strategy: 'CUSTOM')] #[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: UuidGenerator::class)] #[ORM\Id]
private Uuid $id; public private(set) Uuid $id;
/** @var array<string, mixed> */ /** @var array<string, mixed> */
#[ORM\Column(type: Types::JSON)] #[ORM\Column(type: Types::JSONB)]
private array $data = []; public array $data = [];
#[ORM\Column(type: Types::DATETIMETZ_IMMUTABLE, nullable: false)] #[ORM\Column(type: Types::DATETIMETZ_IMMUTABLE, nullable: false)]
private \DateTimeImmutable $created; public private(set) \DateTimeImmutable $created;
public function __construct( public function __construct(
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
#[ORM\ManyToOne(inversedBy: 'eliminations')] #[ORM\ManyToOne(inversedBy: 'eliminations')]
#[ORM\JoinColumn(nullable: false)] public Quiz $quiz,
private Quiz $quiz,
) {} ) {}
public function getId(): Uuid
{
return $this->id;
}
/** @return array<string, mixed> */
public function getData(): array
{
return $this->data;
}
/** @param array<string, mixed> $data */
public function setData(array $data): self
{
$this->data = $data;
return $this;
}
public function getQuiz(): Quiz
{
return $this->quiz;
}
/** @param InputBag<bool|float|int|string> $inputBag */ /** @param InputBag<bool|float|int|string> $inputBag */
public function updateFromInputBag(InputBag $inputBag): self public function updateFromInputBag(InputBag $inputBag): self
{ {
foreach ($this->data as $name => $screenColour) { foreach (array_keys($this->data) as $name) {
$newColour = $inputBag->get('colour-'.mb_strtolower($name)); $newColour = $inputBag->get('colour-'.mb_strtolower($name));
if (\is_string($newColour)) { if (\is_string($newColour)) {
$this->data[$name] = $inputBag->get('colour-'.mb_strtolower($name)); $this->data[$name] = $inputBag->get('colour-'.mb_strtolower($name));
@@ -86,9 +62,4 @@ class Elimination
{ {
$this->created = new DateTimeImmutable(); $this->created = new DateTimeImmutable();
} }
public function getCreated(): \DateTimeInterface
{
return $this->created;
}
} }

View File

@@ -2,68 +2,42 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Entity; namespace Tvdt\Entity;
use App\Repository\GivenAnswerRepository;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Safe\DateTimeImmutable; use Safe\DateTimeImmutable;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use Symfony\Bridge\Doctrine\Types\UuidType; use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid; use Symfony\Component\Uid\Uuid;
use Tvdt\Repository\GivenAnswerRepository;
#[ORM\Entity(repositoryClass: GivenAnswerRepository::class)] #[ORM\Entity(repositoryClass: GivenAnswerRepository::class)]
#[ORM\HasLifecycleCallbacks] #[ORM\HasLifecycleCallbacks]
class GivenAnswer final class GivenAnswer
{ {
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME, unique: true)] #[ORM\Column(type: UuidType::NAME, unique: true)]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
#[ORM\GeneratedValue(strategy: 'CUSTOM')] #[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: UuidGenerator::class)] #[ORM\Id]
private Uuid $id; public private(set) Uuid $id;
#[ORM\Column(type: Types::DATETIMETZ_IMMUTABLE, nullable: false)] #[ORM\Column(type: Types::DATETIMETZ_IMMUTABLE, nullable: false)]
private \DateTimeImmutable $created; public private(set) \DateTimeImmutable $created;
public function __construct( public function __construct(
#[ORM\ManyToOne(inversedBy: 'givenAnswers')]
#[ORM\JoinColumn(nullable: false)] #[ORM\JoinColumn(nullable: false)]
private Candidate $candidate, #[ORM\ManyToOne(inversedBy: 'givenAnswers')]
private(set) Candidate $candidate,
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
#[ORM\ManyToOne] #[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)] private(set) Quiz $quiz,
private Quiz $quiz,
#[ORM\JoinColumn(nullable: false)]
#[ORM\ManyToOne(inversedBy: 'givenAnswers')] #[ORM\ManyToOne(inversedBy: 'givenAnswers')]
#[ORM\JoinColumn(nullable: false)] private(set) Answer $answer,
private Answer $answer,
) {} ) {}
public function getId(): Uuid
{
return $this->id;
}
public function getCandidate(): Candidate
{
return $this->candidate;
}
public function getQuiz(): Quiz
{
return $this->quiz;
}
public function getAnswer(): Answer
{
return $this->answer;
}
public function getCreated(): \DateTimeImmutable
{
return $this->created;
}
#[ORM\PrePersist] #[ORM\PrePersist]
public function setCreatedAtValue(): void public function setCreatedAtValue(): void
{ {

View File

@@ -2,101 +2,53 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Entity; namespace Tvdt\Entity;
use App\Repository\QuestionRepository;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use Symfony\Bridge\Doctrine\Types\UuidType; use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid; use Symfony\Component\Uid\Uuid;
use Tvdt\Repository\QuestionRepository;
#[ORM\Entity(repositoryClass: QuestionRepository::class)] #[ORM\Entity(repositoryClass: QuestionRepository::class)]
class Question final class Question
{ {
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME)] #[ORM\Column(type: UuidType::NAME)]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
#[ORM\GeneratedValue(strategy: 'CUSTOM')] #[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: UuidGenerator::class)] #[ORM\Id]
private Uuid $id; public private(set) Uuid $id;
#[ORM\Column(type: Types::SMALLINT, options: ['default' => 0])] #[ORM\Column(type: Types::SMALLINT, options: ['default' => 0])]
private int $ordering; public int $ordering;
#[ORM\Column(type: Types::STRING, length: 255)] #[ORM\Column(type: Types::STRING, length: 255)]
private string $question; public string $question;
#[ORM\ManyToOne(inversedBy: 'questions')]
#[ORM\JoinColumn(nullable: false)] #[ORM\JoinColumn(nullable: false)]
private Quiz $quiz; #[ORM\ManyToOne(inversedBy: 'questions')]
public Quiz $quiz;
#[ORM\Column] #[ORM\Column]
private bool $enabled = true; public bool $enabled = true;
/** @var Collection<int, Answer> */ /** @var Collection<int, Answer> */
#[ORM\OneToMany(targetEntity: Answer::class, mappedBy: 'question', cascade: ['persist'], orphanRemoval: true)] #[ORM\OneToMany(targetEntity: Answer::class, mappedBy: 'question', cascade: ['persist'], orphanRemoval: true)]
#[ORM\OrderBy(['ordering' => 'ASC'])] #[ORM\OrderBy(['ordering' => 'ASC'])]
private Collection $answers; public private(set) Collection $answers;
public function __construct() public function __construct()
{ {
$this->answers = new ArrayCollection(); $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 public function addAnswer(Answer $answer): static
{ {
if (!$this->answers->contains($answer)) { if (!$this->answers->contains($answer)) {
$this->answers->add($answer); $this->answers->add($answer);
$answer->setQuestion($this); $answer->question = $this;
} }
return $this; return $this;
@@ -108,7 +60,7 @@ class Question
return 'This question has no answers'; return 'This question has no answers';
} }
$correctAnswers = $this->answers->filter(static fn (Answer $answer): bool => $answer->isRightAnswer())->count(); $correctAnswers = $this->answers->filter(static fn (Answer $answer): bool => $answer->isRightAnswer)->count();
if (0 === $correctAnswers) { if (0 === $correctAnswers) {
return 'This question has no correct answers'; return 'This question has no correct answers';
@@ -120,16 +72,4 @@ class Question
return null; return null;
} }
public function getOrdering(): int
{
return $this->ordering;
}
public function setOrdering(int $ordering): static
{
$this->ordering = $ordering;
return $this;
}
} }

View File

@@ -2,49 +2,48 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Entity; namespace Tvdt\Entity;
use App\Repository\QuizRepository;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use Symfony\Bridge\Doctrine\Types\UuidType; use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid; use Symfony\Component\Uid\Uuid;
use Tvdt\Repository\QuizRepository;
#[ORM\Entity(repositoryClass: QuizRepository::class)] #[ORM\Entity(repositoryClass: QuizRepository::class)]
#[ORM\UniqueConstraint(fields: ['name', 'season'])] #[ORM\UniqueConstraint(fields: ['name', 'season'])]
class Quiz final class Quiz
{ {
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME)] #[ORM\Column(type: UuidType::NAME)]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
#[ORM\GeneratedValue(strategy: 'CUSTOM')] #[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: UuidGenerator::class)] #[ORM\Id]
private Uuid $id; public private(set) Uuid $id;
#[ORM\Column(length: 64)] #[ORM\Column(length: 64)]
private string $name; public string $name;
#[ORM\ManyToOne(inversedBy: 'quizzes')]
#[ORM\JoinColumn(nullable: false)] #[ORM\JoinColumn(nullable: false)]
private Season $season; #[ORM\ManyToOne(inversedBy: 'quizzes')]
public Season $season;
/** @var Collection<int, Question> */ /** @var Collection<int, Question> */
#[ORM\OneToMany(targetEntity: Question::class, mappedBy: 'quiz', cascade: ['persist'], orphanRemoval: true)] #[ORM\OneToMany(targetEntity: Question::class, mappedBy: 'quiz', cascade: ['persist'], orphanRemoval: true)]
#[ORM\OrderBy(['ordering' => 'ASC'])] #[ORM\OrderBy(['ordering' => 'ASC'])]
private Collection $questions; public private(set) Collection $questions;
/** @var Collection<int, QuizCandidate> */ /** @var Collection<int, QuizCandidate> */
#[ORM\OneToMany(targetEntity: QuizCandidate::class, mappedBy: 'quiz', orphanRemoval: true)] #[ORM\OneToMany(targetEntity: QuizCandidate::class, mappedBy: 'quiz', orphanRemoval: true)]
private Collection $candidateData; public private(set) Collection $candidateData;
#[ORM\Column(nullable: false, options: ['default' => 1])] #[ORM\Column(nullable: false, options: ['default' => 1])]
private int $dropouts = 1; public int $dropouts = 1;
/** @var Collection<int, Elimination> */ /** @var Collection<int, Elimination> */
#[ORM\OneToMany(targetEntity: Elimination::class, mappedBy: 'quiz', cascade: ['persist'], orphanRemoval: true)] #[ORM\OneToMany(targetEntity: Elimination::class, mappedBy: 'quiz', cascade: ['persist'], orphanRemoval: true)]
#[ORM\OrderBy(['created' => 'DESC'])] #[ORM\OrderBy(['created' => 'DESC'])]
private Collection $eliminations; public private(set) Collection $eliminations;
public function __construct() public function __construct()
{ {
@@ -53,75 +52,16 @@ class Quiz
$this->eliminations = new ArrayCollection(); $this->eliminations = 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 public function addQuestion(Question $question): static
{ {
if (!$this->questions->contains($question)) { if (!$this->questions->contains($question)) {
$this->questions->add($question); $this->questions->add($question);
$question->setQuiz($this); $question->quiz = $this;
} }
return $this; return $this;
} }
/** @return Collection<int, QuizCandidate> */
public function getCandidateData(): Collection
{
return $this->candidateData;
}
public function getDropouts(): int
{
return $this->dropouts;
}
public function setDropouts(int $dropouts): static
{
$this->dropouts = $dropouts;
return $this;
}
/** @return Collection<int, Elimination> */
public function getEliminations(): Collection
{
return $this->eliminations;
}
public function addElimination(Elimination $elimination): self public function addElimination(Elimination $elimination): self
{ {
$this->eliminations->add($elimination); $this->eliminations->add($elimination);

View File

@@ -2,75 +2,42 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Entity; namespace Tvdt\Entity;
use App\Repository\QuizCandidateRepository;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Safe\DateTimeImmutable; use Safe\DateTimeImmutable;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use Symfony\Bridge\Doctrine\Types\UuidType; use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid; use Symfony\Component\Uid\Uuid;
use Tvdt\Repository\QuizCandidateRepository;
#[ORM\Entity(repositoryClass: QuizCandidateRepository::class)] #[ORM\Entity(repositoryClass: QuizCandidateRepository::class)]
#[ORM\UniqueConstraint(columns: ['candidate_id', 'quiz_id'])]
#[ORM\HasLifecycleCallbacks] #[ORM\HasLifecycleCallbacks]
class QuizCandidate #[ORM\UniqueConstraint(columns: ['candidate_id', 'quiz_id'])]
final class QuizCandidate
{ {
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME, unique: true)] #[ORM\Column(type: UuidType::NAME, unique: true)]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
#[ORM\GeneratedValue(strategy: 'CUSTOM')] #[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: UuidGenerator::class)] #[ORM\Id]
private Uuid $id; public private(set) Uuid $id;
#[ORM\Column] #[ORM\Column]
private float $corrections = 0; public float $corrections = 0;
#[ORM\Column(type: Types::DATETIMETZ_IMMUTABLE)] #[ORM\Column(type: Types::DATETIMETZ_IMMUTABLE)]
private \DateTimeImmutable $created; public private(set) \DateTimeImmutable $created;
public function __construct( public function __construct(
#[ORM\JoinColumn(nullable: false)]
#[ORM\ManyToOne(inversedBy: 'candidateData')] #[ORM\ManyToOne(inversedBy: 'candidateData')]
#[ORM\JoinColumn(nullable: false)] public Quiz $quiz,
private Quiz $quiz,
#[ORM\JoinColumn(nullable: false)]
#[ORM\ManyToOne(inversedBy: 'quizData')] #[ORM\ManyToOne(inversedBy: 'quizData')]
#[ORM\JoinColumn(nullable: false)] public Candidate $candidate,
private Candidate $candidate,
) {} ) {}
public function getId(): Uuid
{
return $this->id;
}
public function getCandidate(): Candidate
{
return $this->candidate;
}
public function getQuiz(): Quiz
{
return $this->quiz;
}
public function getCorrections(): ?float
{
return $this->corrections;
}
public function setCorrections(float $corrections): static
{
$this->corrections = $corrections;
return $this;
}
public function getCreated(): \DateTimeImmutable
{
return $this->created;
}
#[ORM\PrePersist] #[ORM\PrePersist]
public function setCreatedAtValue(): void public function setCreatedAtValue(): void
{ {

View File

@@ -2,123 +2,81 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Entity; namespace Tvdt\Entity;
use App\Repository\SeasonRepository;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use Symfony\Bridge\Doctrine\Types\UuidType; use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid; use Symfony\Component\Uid\Uuid;
use Tvdt\Repository\SeasonRepository;
#[ORM\Entity(repositoryClass: SeasonRepository::class)] #[ORM\Entity(repositoryClass: SeasonRepository::class)]
class Season final class Season
{ {
private const string SEASON_CODE_CHARACTERS = 'bcdfghjklmnpqrstvwxz'; private const string SEASON_CODE_CHARACTERS = 'bcdfghjklmnpqrstvwxz';
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME)] #[ORM\Column(type: UuidType::NAME)]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
#[ORM\GeneratedValue(strategy: 'CUSTOM')] #[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: UuidGenerator::class)] #[ORM\Id]
private Uuid $id; public private(set) Uuid $id;
#[ORM\Column(length: 64)] #[ORM\Column(length: 64)]
private string $name; public string $name;
#[ORM\Column(length: 5)] #[ORM\Column(length: 5)]
private string $seasonCode; public string $seasonCode;
/** @var Collection<int, Quiz> */ /** @var Collection<int, Quiz> */
#[ORM\OneToMany(targetEntity: Quiz::class, mappedBy: 'season', cascade: ['persist'], orphanRemoval: true)] #[ORM\OneToMany(targetEntity: Quiz::class, mappedBy: 'season', cascade: ['persist'], orphanRemoval: true)]
private Collection $quizzes; public private(set) Collection $quizzes;
/** @var Collection<int, Candidate> */ /** @var Collection<int, Candidate> */
#[ORM\OneToMany(targetEntity: Candidate::class, mappedBy: 'season', cascade: ['persist'], orphanRemoval: true)] #[ORM\OneToMany(targetEntity: Candidate::class, mappedBy: 'season', cascade: ['persist'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
private Collection $candidates; public private(set) Collection $candidates;
/** @var Collection<int, User> */ /** @var Collection<int, User> */
#[ORM\ManyToMany(targetEntity: User::class, inversedBy: 'seasons')] #[ORM\ManyToMany(targetEntity: User::class, inversedBy: 'seasons')]
private Collection $owners; public private(set) Collection $owners;
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
#[ORM\ManyToOne] #[ORM\ManyToOne]
private ?Quiz $ActiveQuiz = null; public ?Quiz $activeQuiz = null;
#[ORM\JoinColumn(nullable: true)]
#[ORM\OneToOne(cascade: ['persist', 'remove'])]
public ?SeasonSettings $settings = null;
public function __construct() public function __construct()
{ {
$this->settings = new SeasonSettings();
$this->quizzes = new ArrayCollection(); $this->quizzes = new ArrayCollection();
$this->candidates = new ArrayCollection(); $this->candidates = new ArrayCollection();
$this->owners = 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;
}
/** @return Collection<int, Quiz> */
public function getQuizzes(): Collection
{
return $this->quizzes;
}
public function addQuiz(Quiz $quiz): static public function addQuiz(Quiz $quiz): static
{ {
if (!$this->quizzes->contains($quiz)) { if (!$this->quizzes->contains($quiz)) {
$this->quizzes->add($quiz); $this->quizzes->add($quiz);
$quiz->setSeason($this); $quiz->season = $this;
} }
return $this; return $this;
} }
/** @return Collection<int, Candidate> */
public function getCandidates(): Collection
{
return $this->candidates;
}
public function addCandidate(Candidate $candidate): static public function addCandidate(Candidate $candidate): static
{ {
if (!$this->candidates->contains($candidate)) { if (!$this->candidates->contains($candidate)) {
$this->candidates->add($candidate); $this->candidates->add($candidate);
$candidate->setSeason($this); $candidate->season = $this;
} }
return $this; return $this;
} }
/** @return Collection<int, User> */
public function getOwners(): Collection
{
return $this->owners;
}
public function addOwner(User $owner): static public function addOwner(User $owner): static
{ {
if (!$this->owners->contains($owner)) { if (!$this->owners->contains($owner)) {
@@ -135,18 +93,6 @@ class Season
return $this; return $this;
} }
public function getActiveQuiz(): ?Quiz
{
return $this->ActiveQuiz;
}
public function setActiveQuiz(?Quiz $ActiveQuiz): static
{
$this->ActiveQuiz = $ActiveQuiz;
return $this;
}
public function isOwner(User $user): bool public function isOwner(User $user): bool
{ {
return $this->owners->contains($user); return $this->owners->contains($user);

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Tvdt\Entity;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid;
use Tvdt\Repository\SeasonSettingsRepository;
#[ORM\Entity(repositoryClass: SeasonSettingsRepository::class)]
final class SeasonSettings
{
#[ORM\Column(type: UuidType::NAME)]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\Id]
public private(set) Uuid $id;
#[ORM\Column(type: Types::BOOLEAN, options: ['default' => false])]
public bool $showNumbers = false;
#[ORM\Column(type: Types::BOOLEAN, options: ['default' => false])]
public bool $confirmAnswers = false;
}

View File

@@ -2,72 +2,58 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Entity; namespace Tvdt\Entity;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use Symfony\Bridge\Doctrine\Types\UuidType; use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Uid\Uuid; use Symfony\Component\Uid\Uuid;
use Tvdt\Repository\UserRepository;
#[ORM\Entity(repositoryClass: UserRepository::class)] #[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\Table(name: '`user`')] #[ORM\Table(name: '`user`')]
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_EMAIL', fields: ['email'])] #[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_EMAIL', fields: ['email'])]
#[UniqueEntity(fields: ['email'], message: 'There is already an account with this email')] #[UniqueEntity(fields: ['email'], message: 'There is already an account with this email')]
class User implements UserInterface, PasswordAuthenticatedUserInterface final class User implements UserInterface, PasswordAuthenticatedUserInterface
{ {
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME, unique: true)] #[ORM\Column(type: UuidType::NAME, unique: true)]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
#[ORM\GeneratedValue(strategy: 'CUSTOM')] #[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: UuidGenerator::class)] #[ORM\Id]
private Uuid $id; public private(set) Uuid $id;
#[ORM\Column(length: 180)] #[ORM\Column(length: 180)]
private string $email; public string $email;
/** @var list<string> The user roles */ /** @var list<string> The user roles */
#[ORM\Column(type: Types::JSON)] #[ORM\Column(type: Types::JSON)]
private array $roles = []; public array $roles = [];
/** @var string The hashed password */ /** @var string The hashed password */
#[ORM\Column] #[ORM\Column]
private string $password; public string $password;
/** @var Collection<int, Season> */ /** @var Collection<int, Season> */
#[ORM\ManyToMany(targetEntity: Season::class, mappedBy: 'owners')] #[ORM\ManyToMany(targetEntity: Season::class, mappedBy: 'owners')]
private Collection $seasons; public private(set) Collection $seasons;
#[ORM\Column] #[ORM\Column]
private bool $isVerified = false; public bool $isVerified = false;
public bool $isAdmin {
get => \in_array('ROLE_ADMIN', $this->getRoles(), true);
}
public function __construct() public function __construct()
{ {
$this->seasons = new ArrayCollection(); $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. * A visual identifier that represents this user.
* *
@@ -97,27 +83,12 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
return array_unique($roles); return array_unique($roles);
} }
/** @param list<string> $roles */
public function setRoles(array $roles): static
{
$this->roles = $roles;
return $this;
}
/** @see PasswordAuthenticatedUserInterface */ /** @see PasswordAuthenticatedUserInterface */
public function getPassword(): ?string public function getPassword(): ?string
{ {
return $this->password; return $this->password;
} }
public function setPassword(string $password): static
{
$this->password = $password;
return $this;
}
/** @see UserInterface */ /** @see UserInterface */
public function eraseCredentials(): void public function eraseCredentials(): void
{ {
@@ -125,12 +96,6 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
// $this->plainPassword = null; // $this->plainPassword = null;
} }
/** @return Collection<int, Season> */
public function getSeasons(): Collection
{
return $this->seasons;
}
public function addSeason(Season $season): static public function addSeason(Season $season): static
{ {
if (!$this->seasons->contains($season)) { if (!$this->seasons->contains($season)) {
@@ -149,21 +114,4 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
return $this; return $this;
} }
public function isVerified(): bool
{
return $this->isVerified;
}
public function setIsVerified(bool $isVerified): static
{
$this->isVerified = $isVerified;
return $this;
}
public function isAdmin(): bool
{
return \in_array('ROLE_ADMIN', $this->getRoles(), true);
}
} }

Some files were not shown because too many files have changed in this diff Show More