├── .env
├── .env.integration
├── .env.test
├── .github
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── workflows
│ ├── quality.yaml
│ └── test.yaml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── assets
├── images
│ └── .gitkeep
├── js
│ └── app.js
└── scss
│ ├── abstracts
│ ├── .gitkeep
│ ├── _alerts.scss
│ ├── _animations.scss
│ ├── _breakpoints.scss
│ ├── _buttons.scss
│ ├── _colors.scss
│ ├── _fields.scss
│ ├── _flash_bag.scss
│ ├── _flex.scss
│ ├── _grid.scss
│ ├── _icons.scss
│ ├── _layout.scss
│ ├── _list_groups.scss
│ ├── _responsive.scss
│ ├── _round.scss
│ ├── _shadows.scss
│ └── _texts.scss
│ ├── base
│ ├── .gitkeep
│ ├── _container.scss
│ ├── _flex.scss
│ ├── _grid.scss
│ ├── _shadow.scss
│ └── _text.scss
│ ├── components
│ ├── .gitkeep
│ ├── _alert.scss
│ ├── _button.scss
│ ├── _card.scss
│ ├── _field.scss
│ ├── _flash_bag.scss
│ └── _list_group.scss
│ ├── layout
│ ├── .gitkeep
│ ├── _footer.scss
│ ├── _global.scss
│ ├── _header.scss
│ ├── _main.scss
│ └── _main_nav.scss
│ ├── main.scss
│ ├── pages
│ └── .gitkeep
│ ├── themes
│ └── .gitkeep
│ └── vendors
│ ├── .gitkeep
│ ├── _font_awesome.scss
│ ├── _normalize.scss
│ └── _roboto.scss
├── bin
├── console
└── phpunit
├── composer.json
├── composer.lock
├── config
├── bootstrap.php
├── bundles.php
├── packages
│ ├── assets.yaml
│ ├── cache.yaml
│ ├── dev
│ │ └── web_profiler.yaml
│ ├── doctrine.yaml
│ ├── doctrine_migrations.yaml
│ ├── enqueue.yaml
│ ├── framework.yaml
│ ├── integration
│ │ ├── dama_doctrine_test_bundle.yaml
│ │ ├── framework.yaml
│ │ ├── twig.yaml
│ │ ├── validator.yaml
│ │ └── webpack_encore.yaml
│ ├── messenger.yaml
│ ├── prod
│ │ ├── doctrine.yaml
│ │ ├── messenger.yaml
│ │ ├── routing.yaml
│ │ └── webpack_encore.yaml
│ ├── ramsey_uuid_doctrine.yaml
│ ├── routing.yaml
│ ├── security.yaml
│ ├── sensio_framework_extra.yaml
│ ├── test
│ │ ├── dama_doctrine_test_bundle.yaml
│ │ ├── enqueue.yaml
│ │ ├── framework.yaml
│ │ ├── twig.yaml
│ │ ├── validator.yaml
│ │ ├── web_profiler.yaml
│ │ └── webpack_encore.yaml
│ ├── twig.yaml
│ ├── validator.yaml
│ └── webpack_encore.yaml
├── routes.yaml
├── routes
│ ├── annotations.yaml
│ └── dev
│ │ ├── framework.yaml
│ │ └── web_profiler.yaml
├── services.yaml
└── services_integration.yaml
├── docs
├── front.md
├── img
│ ├── alert_danger.png
│ ├── alert_dark.png
│ ├── alert_icons.png
│ ├── alert_info.png
│ ├── alert_light.png
│ ├── alert_primary.png
│ ├── alert_secondary.png
│ ├── alert_success.png
│ ├── alert_warning.png
│ ├── authentication.png
│ ├── button_danger.png
│ ├── button_dark.png
│ ├── button_icon.png
│ ├── button_info.png
│ ├── button_light.png
│ ├── button_outline.png
│ ├── button_primary.png
│ ├── button_secondary.png
│ ├── button_sizes.png
│ ├── button_success.png
│ ├── button_warning.png
│ ├── card.png
│ ├── card_danger.png
│ ├── card_dark.png
│ ├── card_info.png
│ ├── card_light.png
│ ├── card_primary.png
│ ├── card_secondary.png
│ ├── card_success.png
│ ├── card_warning.png
│ ├── clean_architecture.jpg
│ ├── dashboard.png
│ ├── grid.png
│ ├── grid_lg.png
│ ├── grid_md.png
│ ├── grid_sm.png
│ ├── grid_xl.png
│ ├── grid_xs.png
│ ├── manage_questions.png
│ ├── manage_users.png
│ ├── packages.png
│ ├── ranking.png
│ ├── registration.png
│ ├── reply_quiz.png
│ ├── request.png
│ ├── sign_out.png
│ ├── sitemap.png
│ ├── update_password.png
│ └── update_profile.png
├── index.md
├── tdd.md
└── uml
│ ├── authentication.puml
│ ├── dashboard.puml
│ ├── manage_questions.puml
│ ├── manage_users.puml
│ ├── packages.puml
│ ├── ranking.puml
│ ├── registration.puml
│ ├── reply_quiz.puml
│ ├── request.puml
│ ├── sign_out.puml
│ ├── sitemap.puml
│ ├── update_password.puml
│ └── update_profile.puml
├── domain
├── src
│ ├── Quiz
│ │ ├── Entity
│ │ │ ├── Answer.php
│ │ │ └── Question.php
│ │ ├── Gateway
│ │ │ └── QuestionGateway.php
│ │ ├── Presenter
│ │ │ ├── CreatePresenterInterface.php
│ │ │ └── UpdatePresenterInterface.php
│ │ ├── Request
│ │ │ ├── CreateRequest.php
│ │ │ └── UpdateRequest.php
│ │ ├── Response
│ │ │ ├── CreateResponse.php
│ │ │ └── UpdateResponse.php
│ │ └── UseCase
│ │ │ ├── Create.php
│ │ │ └── Update.php
│ ├── Security
│ │ ├── Assert
│ │ │ └── Assertion.php
│ │ ├── Entity
│ │ │ └── Participant.php
│ │ ├── Exception
│ │ │ ├── NonUniqueEmailException.php
│ │ │ ├── NonUniquePseudoException.php
│ │ │ ├── ParticipantNotFoundException.php
│ │ │ └── PasswordRecoveryInvalidTokenException.php
│ │ ├── Gateway
│ │ │ └── ParticipantGateway.php
│ │ ├── Presenter
│ │ │ ├── AskPasswordResetPresenterInterface.php
│ │ │ ├── LoginPresenterInterface.php
│ │ │ ├── RecoverPasswordPresenterInterface.php
│ │ │ └── RegistrationPresenterInterface.php
│ │ ├── Provider
│ │ │ └── MailProviderInterface.php
│ │ ├── Request
│ │ │ ├── AskPasswordResetRequest.php
│ │ │ ├── LoginRequest.php
│ │ │ ├── RecoverPasswordRequest.php
│ │ │ └── RegistrationRequest.php
│ │ ├── Response
│ │ │ ├── AskPasswordResetResponse.php
│ │ │ ├── LoginResponse.php
│ │ │ ├── RecoverPasswordResponse.php
│ │ │ └── RegistrationResponse.php
│ │ └── UseCase
│ │ │ ├── AskPasswordReset.php
│ │ │ ├── Login.php
│ │ │ ├── RecoverPassword.php
│ │ │ └── Registration.php
│ └── System
│ │ ├── Entity
│ │ └── Log.php
│ │ ├── Gateway
│ │ └── LogGateway.php
│ │ ├── Presenter
│ │ └── TrackPresenterInterface.php
│ │ ├── Request
│ │ └── TrackRequest.php
│ │ ├── Response
│ │ └── TrackResponse.php
│ │ └── UseCase
│ │ └── Track.php
└── tests
│ ├── Fixtures
│ └── Adapter
│ │ ├── LogRepository.php
│ │ ├── ParticipantRepository.php
│ │ └── QuestionRepository.php
│ ├── Quiz
│ ├── CreateTest.php
│ └── UpdateTest.php
│ ├── Security
│ ├── AskPasswordResetTest.php
│ ├── LoginTest.php
│ ├── RecoverPasswordTest.php
│ └── RegistrationTest.php
│ ├── System
│ └── TrackTest.php
│ └── bootstrap.php
├── package-lock.json
├── package.json
├── phpcs.xml.dist
├── phpunit.xml.dist
├── public
└── index.php
├── src
├── Infrastructure
│ ├── Adapter
│ │ ├── Provider
│ │ │ └── MailProvider.php
│ │ └── Repository
│ │ │ ├── LogRepository.php
│ │ │ ├── ParticipantRepository.php
│ │ │ └── QuestionRepository.php
│ ├── Doctrine
│ │ ├── DataFixtures
│ │ │ ├── QuestionFixtures.php
│ │ │ └── UserFixtures.php
│ │ ├── Entity
│ │ │ ├── .gitignore
│ │ │ ├── DoctrineAnswer.php
│ │ │ ├── DoctrineLog.php
│ │ │ ├── DoctrineParticipant.php
│ │ │ └── DoctrineQuestion.php
│ │ └── Migrations
│ │ │ └── .gitignore
│ ├── EventSubscriber
│ │ └── KernelSubscriber.php
│ ├── Maker
│ │ └── MakeUseCase.php
│ ├── ParamConverter
│ │ └── QuestionConverter.php
│ ├── Resources
│ │ └── skeleton
│ │ │ ├── presenter.tpl.php
│ │ │ ├── request.tpl.php
│ │ │ ├── response.tpl.php
│ │ │ ├── test.tpl.php
│ │ │ └── use_case.tpl.php
│ ├── Security
│ │ ├── Guard
│ │ │ └── WebAuthenticator.php
│ │ ├── Provider
│ │ │ └── UserProvider.php
│ │ └── User.php
│ ├── Test
│ │ ├── Adapter
│ │ │ ├── MailProvider.php
│ │ │ └── Repository
│ │ │ │ ├── LogRepository.php
│ │ │ │ ├── ParticipantRepository.php
│ │ │ │ └── QuestionRepository.php
│ │ └── IntegrationTestCase.php
│ └── Validator
│ │ ├── NonUniqueEmail.php
│ │ ├── NonUniqueEmailValidator.php
│ │ ├── NonUniquePseudo.php
│ │ └── NonUniquePseudoValidator.php
├── Kernel.php
└── UserInterface
│ ├── Controller
│ ├── .gitkeep
│ ├── LoginController.php
│ ├── LogoutController.php
│ ├── Question
│ │ ├── CreateController.php
│ │ └── UpdateController.php
│ ├── RegistrationController.php
│ └── Security
│ │ ├── AskPasswordResetController.php
│ │ └── RecoverPasswordController.php
│ ├── DataTransferObject
│ ├── Answer.php
│ ├── Question.php
│ ├── RecoverPasswordData.php
│ ├── Registration.php
│ └── ResetPasswordData.php
│ ├── Form
│ ├── AnswerType.php
│ ├── QuestionType.php
│ ├── RecoverPasswordType.php
│ ├── RegistrationType.php
│ └── ResetPasswordType.php
│ ├── MessageHandler
│ └── TrackHandler.php
│ ├── Presenter
│ ├── .gitkeep
│ ├── Question
│ │ ├── CreatePresenter.php
│ │ └── UpdatePresenter.php
│ ├── RegistrationPresenter.php
│ ├── Security
│ │ ├── AskPasswordResetPresenter.php
│ │ └── RecoverPasswordPresenter.php
│ └── TrackPresenter.php
│ └── ViewModel
│ ├── .gitkeep
│ ├── LoginViewModel.php
│ ├── RegistrationViewModel.php
│ └── Security
│ └── AskPasswordResetViewModel.php
├── symfony.lock
├── templates
├── base.html.twig
├── components
│ ├── flash_messages.html.twig
│ └── form.html.twig
├── emails
│ └── password_reset_request.html.twig
├── home.html.twig
├── login.html.twig
├── question
│ ├── _form.html.twig
│ ├── _prototype.html.twig
│ ├── create.html.twig
│ └── update.html.twig
├── registration.html.twig
└── security
│ ├── change_password.html.twig
│ └── reset_password.html.twig
├── tests
├── EndToEndTests
│ ├── ParticipantTest.php
│ └── VisitorTest.php
├── IntegrationTests
│ ├── AskPasswordResetTest.php
│ ├── CreateQuestionTest.php
│ ├── LoginTest.php
│ ├── RecoverPasswordTest.php
│ ├── RegistrationTest.php
│ └── UpdateQuestionTest.php
├── SystemTests
│ ├── AskPasswordResetTest.php
│ ├── CreateQuestionTest.php
│ ├── LoginTest.php
│ ├── RecoverPasswordTest.php
│ ├── RegistrationTest.php
│ └── UpdateQuestionTest.php
└── bootstrap.php
├── tsconfig.json
└── webpack.config.js
/.env:
--------------------------------------------------------------------------------
1 | # In all environments, the following files are loaded if they exist,
2 | # the latter taking precedence over the former:
3 | #
4 | # * .env contains default values for the environment variables needed by the app
5 | # * .env.local uncommitted file with local overrides
6 | # * .env.$APP_ENV committed environment-specific defaults
7 | # * .env.$APP_ENV.local uncommitted environment-specific overrides
8 | #
9 | # Real environment variables win over .env files.
10 | #
11 | # DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
12 | #
13 | # Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
14 | # https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
15 |
16 | ###> symfony/framework-bundle ###
17 | APP_ENV=dev
18 | APP_SECRET=7ea6cff520c89e6e0653a832b6382ade
19 | #TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
20 | #TRUSTED_HOSTS='^(localhost|example\.com)$'
21 | ###< symfony/framework-bundle ###
22 |
23 | ###> doctrine/doctrine-bundle ###
24 | # Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
25 | # For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
26 | # For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11&charset=utf8"
27 | # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
28 | DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7
29 | ###< doctrine/doctrine-bundle ###
30 |
31 | ###> symfony/messenger ###
32 | # Choose one of the transports below
33 | # MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages
34 | # MESSENGER_TRANSPORT_DSN=doctrine://default
35 | # MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages
36 | ###< symfony/messenger ###
37 |
38 | ###> enqueue/enqueue-bundle ###
39 | ENQUEUE_DSN=null://
40 | ###< enqueue/enqueue-bundle ###
41 |
--------------------------------------------------------------------------------
/.env.integration:
--------------------------------------------------------------------------------
1 | # define your env variables for the test env here
2 | KERNEL_CLASS='App\Kernel'
3 | APP_SECRET='$ecretf0rt3st'
4 | SYMFONY_DEPRECATIONS_HELPER=999999
5 | PANTHER_APP_ENV=panther
6 |
--------------------------------------------------------------------------------
/.env.test:
--------------------------------------------------------------------------------
1 | # define your env variables for the test env here
2 | KERNEL_CLASS='App\Kernel'
3 | APP_SECRET='$ecretf0rt3st'
4 | SYMFONY_DEPRECATIONS_HELPER=999999
5 | PANTHER_APP_ENV=panther
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/workflows/quality.yaml:
--------------------------------------------------------------------------------
1 | name: Code quality
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | phpcs:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v2
12 |
13 | - name: Validate composer.json and composer.lock
14 | run: composer validate
15 |
16 | - name: Install dependencies
17 | run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader
18 |
19 | - name: Detecting PHP Code Standards Violations
20 | run: vendor/bin/phpcs --standard=PSR12 tests src domain
21 |
--------------------------------------------------------------------------------
/.github/workflows/test.yaml:
--------------------------------------------------------------------------------
1 | name: Testing project
2 | on: [push, pull_request]
3 | jobs:
4 | symfony:
5 | name: Symfony (PHP ${{ matrix.php-versions }} on ${{ matrix.operating-system }})
6 | runs-on: ${{ matrix.operating-system }}
7 | strategy:
8 | fail-fast: false
9 | matrix:
10 | operating-system: [ubuntu-latest]
11 | php-versions: ['7.4']
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v2
15 | - name: Setup PHP, with composer and extensions
16 | uses: shivammathur/setup-php@v2 #https://github.com/shivammathur/setup-php
17 | with:
18 | php-version: ${{ matrix.php-versions }}
19 | extensions: mbstring, xml, ctype, iconv, intl
20 | coverage: xdebug #optional
21 | - name: Get composer cache directory
22 | id: composer-cache
23 | run: echo "::set-output name=dir::$(composer config cache-files-dir)"
24 | - name: Cache composer dependencies
25 | uses: actions/cache@v1
26 | with:
27 | path: ${{ steps.composer-cache.outputs.dir }}
28 | # Use composer.json for key, if composer.lock is not committed.
29 | # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
30 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
31 | restore-keys: ${{ runner.os }}-composer-
32 | - name: Install Composer dependencies
33 | run: |
34 | composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader
35 | - name: Run Tests
36 | run: php bin/phpunit --testsuite unit,integration --coverage-text
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ###> symfony/framework-bundle ###
2 | /.env.local
3 | /.env.local.php
4 | /.env.*.local
5 | /config/secrets/prod/prod.decrypt.private.php
6 | /public/bundles/
7 | /var/
8 | /vendor/
9 | ###< symfony/framework-bundle ###
10 |
11 | ###> symfony/phpunit-bridge ###
12 | .phpunit
13 | .phpunit.result.cache
14 | /phpunit.xml
15 | ###< symfony/phpunit-bridge ###
16 |
17 | ###> phpunit/phpunit ###
18 | /phpunit.xml
19 | .phpunit.result.cache
20 | ###< phpunit/phpunit ###
21 | /.idea
22 | /build
23 | .php_cs.cache
24 | *.sh
25 | debug.log
26 | ###> squizlabs/php_codesniffer ###
27 | /.phpcs-cache
28 | /phpcs.xml
29 | ###< squizlabs/php_codesniffer ###
30 |
31 | ###> symfony/webpack-encore-bundle ###
32 | /node_modules/
33 | /public/build/
34 | npm-debug.log
35 | yarn-error.log
36 | ###< symfony/webpack-encore-bundle ###
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Thomas Boileau
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | unit-tests:
2 | bin/phpunit --testsuite unit
3 |
4 | integration-tests:
5 | bin/phpunit --testsuite integration
6 |
7 | system-tests:
8 | composer database-test
9 | bin/phpunit --testsuite system
10 |
11 | e2e-tests:
12 | composer database-panther
13 | bin/phpunit --testsuite end_to_end
14 |
15 | .PHONY: tests
16 | tests:
17 | composer database
18 | bin/phpunit --testsuite unit,integration,system,end_to_end
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | CODE CHALLENGE
2 | ==============
3 |
4 | Rejoignez-nous sur :
5 | * [Twitch](https://www.twitch.tv/toham)
6 | * [Youtube](https://www.youtube.com/c/ThomasBoileau)
7 | * [Discord](https://discord.gg/AMd6d4a)
8 |
9 | # Sommaire
10 | * [Concept](#concept)
11 | * [Participer](#comment-participer-au-développement-)
12 | * [Projet](#projet)
13 | * [Contribution](CONTRIBUTING.md)
14 | * [Documentation](docs/index.md)
15 |
16 | # Concept
17 | **Code challenge** est une application web, qui a pour objectif de proposer aux utilisateurs inscrits de s'entraîner sur **PHP** et **Symfony**.
18 |
19 | L'objectif est de proposer un projet communautaire, ou chacun sera libre de participer à la conception de l'application.
20 |
21 | L'intérêt est de monter en compétence sur **PHP** et **Symfony** ensemble, autour d'un projet fédérateur. Ce n'es pas tout, le but à long terme est de passer la certification **Symfony**.
22 | Quoi de plus vertueux que de monter en compétence en concevant ensemble une application pour s'entraîner sur **Symfony**, pour enfin décrocher la certification.
23 |
24 | Le projet vous tente ? Rejoignez-nous sur Twitch [www.twitch.tv/toham](https://www.twitch.tv/toham) !
25 |
26 | # Comment participer au développement ?
27 | Tout d'abord, vous n'avez aucune obligation, cependant si vous vous engagez pour implémenter une fonctionnalité, on compte sur vous !
28 |
29 | *Je veux participer au projet, comment je fais ?* C'est très simple, rejoignez nous sur [discord](https://discord.gg/AMd6d4a), et contactez-moi en m'envoyant un message avec votre identifiant **Github** pour que je puisse vous ajouter en tant que collaborateur.
30 |
31 | # Projet
32 | Comme je l'ai expliqué plus haut, le but de se projet est de monter en compétence,pas seulement sur **PHP** et **Symfony**, mais aussi sur l'architecture logiciel comme la **Clean architecture**, en suivant les bonnes pratiques.
33 |
34 | Plus concrètement, nous allons mettre en place :
35 | * Clean architecture
36 | * **T**est **D**riven **D**evelopment
37 |
38 | Je vous invite à lire le fichier de [contribution](CONTRIBUTION.md);
39 |
40 | Pour en savoir plus, jetez un oeil à la [documentation](docs/index.md)
41 |
42 | # Crédits
43 |
44 | * [Thomas Boileau](https://github.com/TBoileau)
45 |
--------------------------------------------------------------------------------
/assets/images/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/assets/images/.gitkeep
--------------------------------------------------------------------------------
/assets/scss/abstracts/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/assets/scss/abstracts/.gitkeep
--------------------------------------------------------------------------------
/assets/scss/abstracts/_alerts.scss:
--------------------------------------------------------------------------------
1 | $alert-colors: (
2 | primary: (
3 | color: #004085,
4 | background: #cce5ff,
5 | border: #b8daff
6 | ),
7 | secondary: (
8 | color: #383d41,
9 | background: #e2e3e5,
10 | border: #d6d8db
11 | ),
12 | success: (
13 | color: #155724,
14 | background: #d4edda,
15 | border: #c3e6cb
16 | ),
17 | danger: (
18 | color: #721c24,
19 | background: #f8d7da,
20 | border: #f5c6cb
21 | ),
22 | warning: (
23 | color: #856404,
24 | background: #fff3cd,
25 | border: #ffeeba
26 | ),
27 | info: (
28 | color: #0c5460,
29 | background: #d1ecf1,
30 | border: #bee5eb
31 | ),
32 | light: (
33 | color: #818182,
34 | background: #fefefe,
35 | border: #fdfdfe
36 | ),
37 | dark: (
38 | color: #1b1e21,
39 | background: #d6d8d9,
40 | border: #c6c8ca
41 | )
42 | );
43 | $alert-icon-left: .75rem;
44 | $alert-icon-top: calc(.75rem + 1px);
45 | $alert-padding-with-icon: .75rem 1.25rem .75rem 2.5rem;
46 | $alert-margin-bottom: 1rem;
47 | $alert-padding: .75rem 1.25rem;
48 | $alert-radius: .25rem;
49 |
--------------------------------------------------------------------------------
/assets/scss/abstracts/_animations.scss:
--------------------------------------------------------------------------------
1 | @keyframes fold {
2 | from {
3 | top: 0;
4 | opacity: 1;
5 | }
6 | to {
7 | top: -150px;
8 | opacity: 0;
9 | }
10 | }
11 |
12 | @mixin animation($type, $duration, $count) {
13 | animation-duration: $duration;
14 | animation-name: $type;
15 | animation-iteration-count: $count;
16 | animation-fill-mode: forwards;
17 | }
18 |
--------------------------------------------------------------------------------
/assets/scss/abstracts/_breakpoints.scss:
--------------------------------------------------------------------------------
1 | $breakpoints: (
2 | xs: (max-width: 575.98px),
3 | sm: (min-width: 576px, max-width: 767.98px),
4 | md: (min-width: 768px, max-width: 991.98px),
5 | lg: (min-width: 992px, max-width: 1199.98px),
6 | xl: (min-width: 1200px)
7 | );
8 |
9 | $container: (
10 | xs: 95%,
11 | sm: 540px,
12 | md: 720px,
13 | lg: 960px,
14 | xl: 1140px
15 | )
16 |
--------------------------------------------------------------------------------
/assets/scss/abstracts/_buttons.scss:
--------------------------------------------------------------------------------
1 | $button-sizes: (small, medium, large );
2 | $button-paddings: (
3 | small: .25rem .5rem,
4 | medium: .5rem .75rem,
5 | large: .75rem 1rem,
6 | );
7 | $button-padding-icons: (
8 | small: .25rem .5rem .25rem 1.75rem,
9 | medium: .5rem .75rem .5rem 2rem,
10 | large: .75rem 1rem .75rem 2.5rem
11 | );
12 | $button-top-icons: (
13 | small: calc(.085rem + 1px),
14 | medium: calc(.45rem + 1px),
15 | large: calc(.75rem + 1px)
16 | );
17 | $button-left-icons: (
18 | small: .5rem,
19 | medium: .5rem,
20 | large: .75rem
21 | );
22 | $button-font-sizes: (
23 | small: .75rem,
24 | medium: 1rem,
25 | large: 1.25rem,
26 | );
27 | $button-radius: (
28 | small: .2rem,
29 | medium: .25rem,
30 | large: .3rem,
31 | );
--------------------------------------------------------------------------------
/assets/scss/abstracts/_colors.scss:
--------------------------------------------------------------------------------
1 | $primary: #007bff;
2 | $secondary: #6c757d;
3 | $success: #28a745;
4 | $danger: #dc3545;
5 | $warning: #ffc107;
6 | $info: #17a2b8;
7 | $light: #f8f9fa;
8 | $dark: #343a40;
9 | $muted: #6c757d;
10 | $white: #fff;
11 | $grey: #ced4da;
12 | $black: #000;
13 |
14 | $contrasts: (
15 | primary: $white,
16 | secondary: $white,
17 | success: $white,
18 | danger: $white,
19 | warning: $dark,
20 | info: $white,
21 | light: $dark,
22 | dark: $white,
23 | muted: $dark,
24 | white: $dark,
25 | grey: $white,
26 | black: $white,
27 | );
28 |
29 | $colors: (
30 | primary: $primary,
31 | secondary: $secondary,
32 | success: $success,
33 | danger: $danger,
34 | warning: $warning,
35 | info: $info,
36 | light: $light,
37 | dark: $dark,
38 | muted: $muted,
39 | white: $white,
40 | grey: $grey,
41 | black: $black,
42 | )
43 |
--------------------------------------------------------------------------------
/assets/scss/abstracts/_fields.scss:
--------------------------------------------------------------------------------
1 | $field-margin-bottom: 1rem;
2 | $field-help-error-margin: .25rem 0 0;
3 | $widget-padding-with-icon: .375rem 2.5rem;
4 | $widget-choice-padding: .5rem 0;
5 | $widget-padding: .375rem 2.5rem .375rem .75rem;
6 | $widget-border: 1px solid $grey;
7 | $widget-height: calc(2rem + 2px);
8 | $widget-textarea-min-height: calc(4rem + 2px);
9 | $label-height: 1rem;
10 | $label-margin-bottom: .375rem;
11 | $widget-icon-top: calc(1.875rem + 1px);
12 | $widget-select-icon-right: 1.25rem;
13 | $widget-input-icon-right: .75rem;
14 | $widget-icon-left: .75rem;
15 | $widget-icon-color: rgba($black, .375);
16 |
--------------------------------------------------------------------------------
/assets/scss/abstracts/_flash_bag.scss:
--------------------------------------------------------------------------------
1 | $flash-bag-icons: (
2 | success: (
3 | color: $success,
4 | content: "\f058"
5 | ),
6 | danger: (
7 | color: $danger,
8 | content: "\f057"
9 | ),
10 | info: (
11 | color: $info,
12 | content: "\f05a"
13 | ),
14 | warning: (
15 | color: $warning,
16 | content: "\f06a"
17 | )
18 | );
19 |
--------------------------------------------------------------------------------
/assets/scss/abstracts/_flex.scss:
--------------------------------------------------------------------------------
1 | $flex-directions: (row, column);
2 | $flex-justify: (
3 | start: flex-start,
4 | end: flex-end,
5 | center: center,
6 | between: space-between,
7 | around: space-around,
8 | evenly: space-evenly
9 | );
10 | $flex-aligns: (
11 | start: flex-start,
12 | end: flex-end,
13 | center: center,
14 | stretch: stretch,
15 | baseline: baseline
16 | );
17 |
--------------------------------------------------------------------------------
/assets/scss/abstracts/_grid.scss:
--------------------------------------------------------------------------------
1 | @mixin grid($columns) {
2 | flex: 0 0 percentage($columns/12);
3 | max-width: percentage($columns/12);
4 | }
5 |
6 | @mixin responsive-grid($columns, $size) {
7 | @include respond-from($size) {
8 | flex: 0 0 percentage($columns/12);
9 | max-width: percentage($columns/12);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/assets/scss/abstracts/_layout.scss:
--------------------------------------------------------------------------------
1 | $header-padding: 1rem 0;
2 | $header-background: $light;
3 | $header-border-bottom: 1px solid rgba(0,0,0,.125);
4 | $footer-padding: 1rem 0;
5 | $footer-background: $light;
6 | $footer-border-top: 1px solid rgba(0,0,0,.125);
7 | $main-padding: 2rem 0;
8 | $main-background: $white;
9 | $main-scrollbar-color: $grey;
10 | $main-scrollbar-background: $light;
11 | $main-scrollbar-width: .5rem;
12 |
--------------------------------------------------------------------------------
/assets/scss/abstracts/_list_groups.scss:
--------------------------------------------------------------------------------
1 | $list-group-item-padding: .5rem 0;
2 |
--------------------------------------------------------------------------------
/assets/scss/abstracts/_responsive.scss:
--------------------------------------------------------------------------------
1 | @mixin respond-to($size) {
2 | @if map-has_key(map-get($breakpoints, $size), max-width) {
3 | @media (max-width: #{map-get(map-get($breakpoints, $size), max-width)}) {
4 | @content;
5 | }
6 | }
7 | }
8 |
9 | @mixin respond-from($size) {
10 | @if map-has_key(map-get($breakpoints, $size), min-width) {
11 | @media (min-width: #{map-get(map-get($breakpoints, $size), min-width)}) {
12 | @content;
13 | }
14 | }
15 | }
16 |
17 | @mixin respond-range($from, $to) {
18 | @if map-has_key(map-get($breakpoints, $size), min-width) and map-has_key(map-get($breakpoints, $size), max-width) {
19 | @media (min-width: #{map-get(map-get($breakpoints, $from), min-width)}) and (max-width: #{map-get(map-get($breakpoints, $to), max-width)}) {
20 | @content;
21 | }
22 | }
23 | }
24 |
25 | @mixin respond-on($size) {
26 | @if map-has_key($breakpoints, $size) {
27 | @if map-has_key(map-get($breakpoints, $size), min-width) and map-has_key(map-get($breakpoints, $size), max-width) {
28 | @media (min-width: #{map-get(map-get($breakpoints, $size), min-width)}) and (max-width: #{map-get(map-get($breakpoints, $size), max-width)}) {
29 | @content;
30 | }
31 | } @else if map-has_key(map-get($breakpoints, $size), min-width) {
32 | @media (min-width: #{map-get(map-get($breakpoints, $size), min-width)}) {
33 | @content;
34 | }
35 | } @else {
36 | @media (max-width: #{map-get(map-get($breakpoints, $size), max-width)}) {
37 | @content;
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/assets/scss/abstracts/_round.scss:
--------------------------------------------------------------------------------
1 | $round-size: .25rem;
2 |
--------------------------------------------------------------------------------
/assets/scss/abstracts/_shadows.scss:
--------------------------------------------------------------------------------
1 | $shadows: (
2 | bottom: (
3 | small: 0 .125rem .25rem,
4 | medium: 0 .5rem 1rem,
5 | large: 0 1rem 3rem
6 | ),
7 | center: (
8 | small: 0 0 .25rem,
9 | medium: 0 0 1rem,
10 | large: 0 0 3rem
11 | ),
12 | top: (
13 | small: 0 -.125rem .25rem,
14 | medium: 0 -.5rem 1rem,
15 | large: 0 -1rem 3rem
16 | )
17 | );
18 |
19 | @mixin shadow($size, $color: $black, $direction: bottom) {
20 | box-shadow: map-get(map-get($shadows, $direction), $size) rgba($color, .1);
21 | }
22 |
--------------------------------------------------------------------------------
/assets/scss/abstracts/_texts.scss:
--------------------------------------------------------------------------------
1 | $base-font-size: 16px;
2 |
3 | $text-small: .8rem;
4 | $text-normal: 1rem;
5 |
6 | $heading-1: 2.5rem;
7 | $heading-2: 2rem;
8 | $heading-3: 1.75rem;
9 | $heading-4: 1.5rem;
10 | $heading-5: 1.25rem;
11 | $heading-6: 1rem;
12 |
13 | $headings: (
14 | 1: $heading-1,
15 | 2: $heading-2,
16 | 3: $heading-3,
17 | 4: $heading-4,
18 | 5: $heading-5,
19 | 6: $heading-6
20 | );
21 |
--------------------------------------------------------------------------------
/assets/scss/base/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/assets/scss/base/.gitkeep
--------------------------------------------------------------------------------
/assets/scss/base/_container.scss:
--------------------------------------------------------------------------------
1 | @use "sass:list";
2 |
3 | .Container {
4 | width: 100%;
5 | padding-right: 15px;
6 | padding-left: 15px;
7 | margin-right: auto;
8 | margin-left: auto;
9 | box-sizing: border-box;
10 |
11 | @each $size, $width in $container {
12 | @include respond-from($size) {
13 | max-width: $width;
14 | }
15 | &.Container--#{$size} {
16 | @each $subsize, $subwidth in $container {
17 | @if index(map-keys($container), $subsize) >= index(map-keys($container), $size) {
18 | @include respond-from($subsize) {
19 | max-width: $subwidth;
20 | }
21 | } @else {
22 | max-width: 100%;
23 | }
24 | }
25 | }
26 | }
27 |
28 | &.Container--fluid {
29 | max-width: 100%;
30 | }
31 | }
--------------------------------------------------------------------------------
/assets/scss/base/_flex.scss:
--------------------------------------------------------------------------------
1 | .Flex {
2 | display: flex;
3 |
4 | @each $direction in $flex-directions {
5 | &.Flex--#{$direction} {
6 | flex-direction: $direction;
7 | }
8 |
9 | @each $size in map-keys($breakpoints) {
10 | &.Flex--#{$size}-#{$direction} {
11 | @include respond-from($size) {
12 | flex-direction: $direction;
13 | }
14 | }
15 | }
16 | }
17 | }
18 |
19 | @each $name, $justify in $flex-justify {
20 | &.Justify--#{$name} {
21 | justify-content: $justify;
22 | }
23 | @each $size in map-keys($breakpoints) {
24 | &.Justify--#{$size}-#{$name} {
25 | @include respond-from($size) {
26 | justify-content: $justify;
27 | }
28 | }
29 | }
30 | }
31 |
32 | @each $name, $align in $flex-aligns {
33 | &.Align--#{$name} {
34 | align-items: $align;
35 | }
36 | @each $size in map-keys($breakpoints) {
37 | &.Align--#{$size}-#{$name} {
38 | @include respond-from($size) {
39 | align-items: $align;
40 | }
41 | }
42 | }
43 | }
44 |
45 | @each $type in (grow, shrink) {
46 | @each $i in (0, 1) {
47 | .Flex--#{$type}-#{$i} {
48 | flex-#{$type}: $i;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/assets/scss/base/_grid.scss:
--------------------------------------------------------------------------------
1 | .Row {
2 | display: flex;
3 | flex-direction: row;
4 | flex-wrap: wrap;
5 | min-width: 100%;
6 | margin-right: -15px;
7 | margin-left: -15px;
8 |
9 | .Column {
10 | position: relative;
11 | width: 100%;
12 | padding-right: 15px;
13 | padding-left: 15px;
14 | box-sizing: border-box;
15 | @for $columns from 1 through 12 {
16 | &.Column--#{$columns} {
17 | @include grid($columns);
18 | }
19 | }
20 |
21 | @each $size, $breakpoint in $breakpoints {
22 | @for $columns from 1 through 12 {
23 | &.Column--#{$size}-#{$columns} {
24 | @include responsive-grid($columns, $size);
25 | }
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/assets/scss/base/_shadow.scss:
--------------------------------------------------------------------------------
1 | .Shadow {
2 | @each $size, $shadow in $shadows {
3 | &.Shadow--#{$size} {
4 | @include shadow($size);
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/assets/scss/base/_text.scss:
--------------------------------------------------------------------------------
1 | .Text {
2 | &.Text--small {
3 | font-size: $text-small;
4 | }
5 |
6 | &.Text--normal {
7 | font-size: $text-normal;
8 | }
9 |
10 | @each $name, $color in $colors {
11 | &.Text--#{$name} {
12 | color: $color;
13 | }
14 | }
15 | }
16 |
17 | .Heading {
18 | @each $size, $heading in $headings {
19 | &.Heading--#{$size} {
20 | font-size: $heading;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/assets/scss/components/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/assets/scss/components/.gitkeep
--------------------------------------------------------------------------------
/assets/scss/components/_alert.scss:
--------------------------------------------------------------------------------
1 | .Alert {
2 | margin-bottom: $alert-margin-bottom;
3 | padding: $alert-padding;
4 | border: 1px solid transparent;
5 | border-radius: $alert-radius;
6 | min-height: 1rem;
7 |
8 | @each $name, $color in $alert-colors {
9 | &.Alert--#{$name} {
10 | color: map-get($color, color);
11 | background-color: map-get($color, background);
12 | border-color: map-get($color, border);
13 | }
14 | }
15 |
16 | &.Alert--icon {
17 | padding: $alert-padding-with-icon;
18 | position: relative;
19 |
20 | &:before {
21 | position: absolute;
22 | top: $alert-icon-top;
23 | left: $alert-icon-left;
24 | width: 1rem;
25 | height: 1rem;
26 | line-height: 1rem;
27 | text-align: center;
28 | vertical-align: middle;
29 | }
30 |
31 | @each $type in map_keys($icon-types) {
32 | @each $icon in map_keys($icons) {
33 | &.Alert--icon-#{$type}-#{$icon}:before {
34 | @include icon($type, $icon);
35 | }
36 | }
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/assets/scss/components/_card.scss:
--------------------------------------------------------------------------------
1 | .Card {
2 | @include shadow(medium);
3 | display: flex;
4 | flex-direction: column;
5 | border-radius: $round-size;
6 | border: 1px solid rgba(0,0,0,.125);
7 |
8 | @each $name, $color in $colors {
9 | &.Card--#{$name} {
10 | background: $color;
11 | color: map-get($contrasts, $name);
12 | }
13 | }
14 |
15 | > .Card__Header, > .Card__Footer {
16 | background-color: rgba(0,0,0,.03);
17 | }
18 |
19 | > .Card__Body {
20 | padding: 1.25rem;
21 | }
22 |
23 | > .Card__Header {
24 | border-bottom: 1px solid rgba(0,0,0,.125);
25 | padding: .75rem 1.25rem;
26 | }
27 |
28 | > .Card__Header {
29 | border-radius: calc($round-size - 1px) calc($round-size - 1px) 0 0;
30 | }
31 |
32 | > .Card__Footer {
33 | border-top: 1px solid rgba(0,0,0,.125);
34 | padding: .75rem 1.25rem;
35 | border-radius: 0 0 calc($round-size - 1px) calc($round-size - 1px);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/assets/scss/components/_flash_bag.scss:
--------------------------------------------------------------------------------
1 | .FlashBag {
2 | position: fixed;
3 | bottom: 30px;
4 | right: 30px;
5 | width: 250px;
6 | z-index: 10000;
7 | display: flex;
8 | flex-direction: column;
9 |
10 | .FlashBag__Message {
11 | padding: 1rem 1rem 1rem calc(2rem + 30px);
12 | position: relative;
13 | margin-bottom: 1rem;
14 | min-height: 30px;
15 | font-size: $text-small;
16 | background: $white;
17 | @include shadow(small);
18 |
19 | &:last-child {
20 | margin-bottom: 0 !important;
21 | }
22 |
23 | &:before {
24 | font-family: "Font Awesome 5 Free";
25 | font-weight: bold;
26 | font-size: 30px;
27 | position: absolute;
28 | top: 1rem;
29 | left: 1rem;
30 | }
31 |
32 | @each $type, $icon in $flash-bag-icons {
33 | &.FlashBag__Message--#{$type}:before {
34 | color: map-get($icon, color);
35 | content: map-get($icon, content);
36 | }
37 | }
38 |
39 | &.FlashBag__Message--hide {
40 | @include animation(fold, 1s, 1);
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/assets/scss/components/_list_group.scss:
--------------------------------------------------------------------------------
1 | .List-Group {
2 | margin: 0;
3 | padding: 0;
4 | .List-Group__Item {
5 | padding: $list-group-item-padding;
6 | border-bottom: 1px solid $grey;
7 |
8 | &:last-child {
9 | border-bottom: none;
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/assets/scss/layout/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/assets/scss/layout/.gitkeep
--------------------------------------------------------------------------------
/assets/scss/layout/_footer.scss:
--------------------------------------------------------------------------------
1 | .Footer {
2 | padding: $footer-padding;
3 | background: $footer-background;
4 | border-top: $footer-border-top;
5 | @include shadow(small, $black, top);
6 | z-index: 10;
7 | position: relative;
8 | }
9 |
--------------------------------------------------------------------------------
/assets/scss/layout/_global.scss:
--------------------------------------------------------------------------------
1 | *, ::after, ::before {
2 | box-sizing: content-box;
3 | }
4 |
5 | body {
6 | font-size: $base-font-size;
7 | font-family: 'Roboto', sans-serif;
8 | display: flex;
9 | flex-direction: column;
10 | height: 100vh;
11 | color: $dark;
12 | }
13 |
14 | header {
15 |
16 | }
17 |
18 | main {
19 | flex-grow: 1;
20 | }
21 |
22 | footer {
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/assets/scss/layout/_header.scss:
--------------------------------------------------------------------------------
1 | .Header {
2 | padding: $header-padding;
3 | background: $header-background;
4 | border-bottom: $header-border-bottom;
5 | @include shadow(small);
6 | z-index: 10;
7 | position: relative;
8 |
9 | nav {
10 | display: flex;
11 | justify-content: space-between;
12 | align-items: center;
13 |
14 | .Header__Title {
15 | text-decoration: none;
16 | color: inherit;
17 | margin: 0;
18 | font-weight: 400;
19 | font-size: $heading-2;
20 | text-transform: uppercase;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/assets/scss/layout/_main.scss:
--------------------------------------------------------------------------------
1 | .Main {
2 | padding: $main-padding;
3 | background: $main-background;
4 | overflow-y: auto;
5 |
6 | &::-webkit-scrollbar{
7 | width: $main-scrollbar-width;
8 | }
9 | &::-webkit-scrollbar-track{
10 | background: $main-scrollbar-background;
11 | border-radius: 0px
12 | }
13 | &::-webkit-scrollbar-thumb{
14 | background: $main-scrollbar-color;
15 | border-radius: 0px;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/assets/scss/layout/_main_nav.scss:
--------------------------------------------------------------------------------
1 | .Main-Nav {
2 | flex-grow: 1;
3 | display: flex;
4 | justify-content: flex-end;
5 | align-items: center;
6 | list-style: none;
7 | margin: 0;
8 | padding: 0;
9 |
10 | .Main-Nav__Item {
11 | margin: 0 1rem;
12 |
13 | &:first-child {
14 | margin-left: 0;
15 | }
16 |
17 | &:last-child {
18 | margin-right: 0;
19 | }
20 |
21 | > a {
22 | text-decoration: none;
23 | color: inherit;
24 |
25 | &:hover {
26 | font-weight: 500;
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/assets/scss/main.scss:
--------------------------------------------------------------------------------
1 | @import
2 | "vendors/normalize",
3 | "vendors/roboto",
4 | "vendors/font_awesome";
5 |
6 | @import
7 | "abstracts/animations",
8 | "abstracts/colors",
9 | "abstracts/buttons",
10 | "abstracts/shadows",
11 | "abstracts/flex",
12 | "abstracts/layout",
13 | "abstracts/alerts",
14 | "abstracts/list_groups",
15 | "abstracts/breakpoints",
16 | "abstracts/responsive",
17 | "abstracts/flash_bag",
18 | "abstracts/grid",
19 | "abstracts/round",
20 | "abstracts/fields",
21 | "abstracts/texts",
22 | "abstracts/icons";
23 |
24 | @import
25 | "base/shadow",
26 | "base/grid",
27 | "base/flex",
28 | "base/container",
29 | "base/text";
30 |
31 | @import
32 | "layout/header",
33 | "layout/main",
34 | "layout/main_nav",
35 | "layout/footer",
36 | "layout/global";
37 |
38 | @import
39 | "components/card",
40 | "components/field",
41 | "components/alert",
42 | "components/list_group",
43 | "components/button",
44 | "components/flash_bag";
45 |
--------------------------------------------------------------------------------
/assets/scss/pages/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/assets/scss/pages/.gitkeep
--------------------------------------------------------------------------------
/assets/scss/themes/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/assets/scss/themes/.gitkeep
--------------------------------------------------------------------------------
/assets/scss/vendors/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/assets/scss/vendors/.gitkeep
--------------------------------------------------------------------------------
/assets/scss/vendors/_font_awesome.scss:
--------------------------------------------------------------------------------
1 | @import "~@fortawesome/fontawesome-free/scss/fontawesome";
2 | @import "~@fortawesome/fontawesome-free/scss/solid";
3 | @import "~@fortawesome/fontawesome-free/scss/regular";
4 | @import "~@fortawesome/fontawesome-free/scss/brands";
--------------------------------------------------------------------------------
/assets/scss/vendors/_normalize.scss:
--------------------------------------------------------------------------------
1 | @import "~normalize.css/normalize.css";
--------------------------------------------------------------------------------
/assets/scss/vendors/_roboto.scss:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap');
2 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | getParameterOption(['--env', '-e'], null, true)) {
23 | putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env);
24 | }
25 |
26 | if ($input->hasParameterOption('--no-debug', true)) {
27 | putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0');
28 | }
29 |
30 | require dirname(__DIR__).'/config/bootstrap.php';
31 |
32 | if ($_SERVER['APP_DEBUG']) {
33 | umask(0000);
34 |
35 | if (class_exists(Debug::class)) {
36 | Debug::enable();
37 | }
38 | }
39 |
40 | $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
41 | $application = new Application($kernel);
42 | $application->run($input);
43 |
--------------------------------------------------------------------------------
/bin/phpunit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | =1.2)
9 | if (is_array($env = @include dirname(__DIR__).'/.env.local.php') && (!isset($env['APP_ENV']) || ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? $env['APP_ENV']) === $env['APP_ENV'])) {
10 | foreach ($env as $k => $v) {
11 | $_ENV[$k] = $_ENV[$k] ?? (isset($_SERVER[$k]) && 0 !== strpos($k, 'HTTP_') ? $_SERVER[$k] : $v);
12 | }
13 | } elseif (!class_exists(Dotenv::class)) {
14 | throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.');
15 | } else {
16 | // load all the .env files
17 | (new Dotenv(false))->loadEnv(dirname(__DIR__).'/.env');
18 | }
19 |
20 | $_SERVER += $_ENV;
21 | $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev';
22 | $_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV'];
23 | $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0';
24 |
--------------------------------------------------------------------------------
/config/bundles.php:
--------------------------------------------------------------------------------
1 | ['all' => true],
5 | Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
6 | Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
7 | Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
8 | Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
9 | Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
10 | Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true, 'panther' => true],
11 | DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true, 'integration' => true],
12 | Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true, 'integration' => true],
13 | Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
14 | Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
15 | Enqueue\Bundle\EnqueueBundle::class => ['all' => true],
16 | Enqueue\MessengerAdapter\Bundle\EnqueueAdapterBundle::class => ['all' => true],
17 | Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
18 | ];
19 |
--------------------------------------------------------------------------------
/config/packages/assets.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | assets:
3 | json_manifest_path: '%kernel.project_dir%/public/build/manifest.json'
4 |
--------------------------------------------------------------------------------
/config/packages/cache.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | cache:
3 | # Unique name of your app: used to compute stable namespaces for cache keys.
4 | #prefix_seed: your_vendor_name/app_name
5 |
6 | # The "app" cache stores to the filesystem by default.
7 | # The data in this cache should persist between deploys.
8 | # Other options include:
9 |
10 | # Redis
11 | #app: cache.adapter.redis
12 | #default_redis_provider: redis://localhost
13 |
14 | # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
15 | #app: cache.adapter.apcu
16 |
17 | # Namespaced pools use the above "app" backend by default
18 | #pools:
19 | #my.dedicated.cache: null
20 |
--------------------------------------------------------------------------------
/config/packages/dev/web_profiler.yaml:
--------------------------------------------------------------------------------
1 | web_profiler:
2 | toolbar: true
3 | intercept_redirects: false
4 |
5 | framework:
6 | profiler: { only_exceptions: false }
7 |
--------------------------------------------------------------------------------
/config/packages/doctrine.yaml:
--------------------------------------------------------------------------------
1 | doctrine:
2 | dbal:
3 | url: '%env(resolve:DATABASE_URL)%'
4 |
5 | # IMPORTANT: You MUST configure your server version,
6 | # either here or in the DATABASE_URL env var (see .env file)
7 | #server_version: '5.7'
8 | orm:
9 | auto_generate_proxy_classes: true
10 | naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
11 | auto_mapping: true
12 | mappings:
13 | App:
14 | is_bundle: false
15 | type: annotation
16 | dir: '%kernel.project_dir%/src/Infrastructure/Doctrine/Entity'
17 | prefix: 'App\Infrastructure\Doctrine\Entity'
18 | alias: App
19 |
--------------------------------------------------------------------------------
/config/packages/doctrine_migrations.yaml:
--------------------------------------------------------------------------------
1 | doctrine_migrations:
2 | dir_name: '%kernel.project_dir%/src/Infrastructure/Doctrine/Migrations'
3 | # namespace is arbitrary but should be different from App\Migrations
4 | # as migrations classes should NOT be autoloaded
5 | namespace: DoctrineMigrations
6 |
--------------------------------------------------------------------------------
/config/packages/enqueue.yaml:
--------------------------------------------------------------------------------
1 | enqueue:
2 | default:
3 | transport: '%env(resolve:ENQUEUE_DSN)%'
4 | client: null
5 |
--------------------------------------------------------------------------------
/config/packages/framework.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | secret: '%env(APP_SECRET)%'
3 | #csrf_protection: true
4 | #http_method_override: true
5 |
6 | # Enables session support. Note that the session will ONLY be started if you read or write from it.
7 | # Remove or comment this section to explicitly disable session support.
8 | session:
9 | handler_id: null
10 | cookie_secure: auto
11 | cookie_samesite: lax
12 | serializer:
13 | enabled: true
14 | #esi: true
15 | #fragments: true
16 | php_errors:
17 | log: true
18 |
--------------------------------------------------------------------------------
/config/packages/integration/dama_doctrine_test_bundle.yaml:
--------------------------------------------------------------------------------
1 | dama_doctrine_test:
2 | enable_static_connection: true
3 | enable_static_meta_data_cache: true
4 | enable_static_query_cache: true
5 |
--------------------------------------------------------------------------------
/config/packages/integration/framework.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | test: true
3 | session:
4 | storage_id: session.storage.mock_file
5 |
--------------------------------------------------------------------------------
/config/packages/integration/twig.yaml:
--------------------------------------------------------------------------------
1 | twig:
2 | strict_variables: true
3 |
--------------------------------------------------------------------------------
/config/packages/integration/validator.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | validation:
3 | not_compromised_password: false
4 |
--------------------------------------------------------------------------------
/config/packages/integration/webpack_encore.yaml:
--------------------------------------------------------------------------------
1 | webpack_encore:
2 | strict_mode: false
3 |
--------------------------------------------------------------------------------
/config/packages/messenger.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | messenger:
3 | # Uncomment this (and the failed transport below) to send failed messages to this transport for later handling.
4 | failure_transport: failed
5 |
6 | transports:
7 | # https://symfony.com/doc/current/messenger.html#transport-configuration
8 | mailer:
9 | dsn: 'doctrine://default?queue_name=mailer'
10 | retry_strategy:
11 | max_retries: 10
12 | # milliseconds delay
13 | delay: 1000
14 | # causes the delay to be higher before each retry
15 | # e.g. 1 second delay, 2 seconds, 4 seconds
16 | multiplier: 5
17 | max_delay: 0
18 | # async: '%env(MESSENGER_TRANSPORT_DSN)%'
19 | failed: 'doctrine://default?queue_name=failed'
20 | # sync: 'sync://'
21 |
22 | routing:
23 | # Route your messages to the transports
24 | 'Symfony\Component\Mailer\Messenger\SendEmailMessage': mailer
25 |
--------------------------------------------------------------------------------
/config/packages/prod/doctrine.yaml:
--------------------------------------------------------------------------------
1 | doctrine:
2 | orm:
3 | auto_generate_proxy_classes: false
4 | metadata_cache_driver:
5 | type: pool
6 | pool: doctrine.system_cache_pool
7 | query_cache_driver:
8 | type: pool
9 | pool: doctrine.system_cache_pool
10 | result_cache_driver:
11 | type: pool
12 | pool: doctrine.result_cache_pool
13 |
14 | framework:
15 | cache:
16 | pools:
17 | doctrine.result_cache_pool:
18 | adapter: cache.app
19 | doctrine.system_cache_pool:
20 | adapter: cache.system
21 |
--------------------------------------------------------------------------------
/config/packages/prod/messenger.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | messenger:
3 | transports:
4 | sqs: 'enqueue://default?topic[name]=code-challenge&queue[name]=code-challenge&receiveTimeout=3'
5 | routing:
6 | TBoileau\CodeChallenge\Domain\System\Request\TrackRequest: sqs
7 |
--------------------------------------------------------------------------------
/config/packages/prod/routing.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | router:
3 | strict_requirements: null
4 |
--------------------------------------------------------------------------------
/config/packages/prod/webpack_encore.yaml:
--------------------------------------------------------------------------------
1 | #webpack_encore:
2 | # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
3 | # Available in version 1.2
4 | #cache: true
5 |
--------------------------------------------------------------------------------
/config/packages/ramsey_uuid_doctrine.yaml:
--------------------------------------------------------------------------------
1 | doctrine:
2 | dbal:
3 | types:
4 | uuid: 'Ramsey\Uuid\Doctrine\UuidType'
5 |
--------------------------------------------------------------------------------
/config/packages/routing.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | router:
3 | utf8: true
4 |
--------------------------------------------------------------------------------
/config/packages/security.yaml:
--------------------------------------------------------------------------------
1 | security:
2 | encoders:
3 | App\Infrastructure\Security\User:
4 | algorithm: argon2i
5 | providers:
6 | user_provider:
7 | id: App\Infrastructure\Security\Provider\UserProvider
8 | firewalls:
9 | dev:
10 | pattern: ^/(_(profiler|wdt)|css|images|js)/
11 | security: false
12 | main:
13 | anonymous: lazy
14 | provider: user_provider
15 | pattern: ^/
16 | logout:
17 | path: logout
18 | guard:
19 | authenticators:
20 | - App\Infrastructure\Security\Guard\WebAuthenticator
21 | access_control:
22 |
--------------------------------------------------------------------------------
/config/packages/sensio_framework_extra.yaml:
--------------------------------------------------------------------------------
1 | sensio_framework_extra:
2 | router:
3 | annotations: false
4 |
--------------------------------------------------------------------------------
/config/packages/test/dama_doctrine_test_bundle.yaml:
--------------------------------------------------------------------------------
1 | dama_doctrine_test:
2 | enable_static_connection: true
3 | enable_static_meta_data_cache: true
4 | enable_static_query_cache: true
5 |
--------------------------------------------------------------------------------
/config/packages/test/enqueue.yaml:
--------------------------------------------------------------------------------
1 | enqueue:
2 | default:
3 | transport: 'null:'
4 | client:
5 | traceable_producer: true
6 |
--------------------------------------------------------------------------------
/config/packages/test/framework.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | test: true
3 | session:
4 | storage_id: session.storage.mock_file
5 |
--------------------------------------------------------------------------------
/config/packages/test/twig.yaml:
--------------------------------------------------------------------------------
1 | twig:
2 | strict_variables: true
3 |
--------------------------------------------------------------------------------
/config/packages/test/validator.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | validation:
3 | not_compromised_password: false
4 |
--------------------------------------------------------------------------------
/config/packages/test/web_profiler.yaml:
--------------------------------------------------------------------------------
1 | web_profiler:
2 | toolbar: false
3 | intercept_redirects: false
4 |
5 | framework:
6 | profiler: { collect: false }
7 |
--------------------------------------------------------------------------------
/config/packages/test/webpack_encore.yaml:
--------------------------------------------------------------------------------
1 | webpack_encore:
2 | strict_mode: false
3 |
--------------------------------------------------------------------------------
/config/packages/twig.yaml:
--------------------------------------------------------------------------------
1 | twig:
2 | default_path: '%kernel.project_dir%/templates'
3 | form_themes:
4 | - components/form.html.twig
5 |
--------------------------------------------------------------------------------
/config/packages/validator.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | validation:
3 | email_validation_mode: html5
4 |
5 | # Enables validator auto-mapping support.
6 | # For instance, basic validation constraints will be inferred from Doctrine's metadata.
7 | #auto_mapping:
8 | # App\Entity\: []
9 |
--------------------------------------------------------------------------------
/config/packages/webpack_encore.yaml:
--------------------------------------------------------------------------------
1 | webpack_encore:
2 | # The path where Encore is building the assets - i.e. Encore.setOutputPath()
3 | output_path: '%kernel.project_dir%/public/build'
4 | # If multiple builds are defined (as shown below), you can disable the default build:
5 | # output_path: false
6 |
7 | # if using Encore.enableIntegrityHashes() and need the crossorigin attribute (default: false, or use 'anonymous' or 'use-credentials')
8 | # crossorigin: 'anonymous'
9 |
10 | # preload all rendered script and link tags automatically via the http2 Link header
11 | # preload: true
12 |
13 | # Throw an exception if the entrypoints.json file is missing or an entry is missing from the data
14 | # strict_mode: false
15 |
16 | # if you have multiple builds:
17 | # builds:
18 | # pass "frontend" as the 3rg arg to the Twig functions
19 | # {{ encore_entry_script_tags('entry1', null, 'frontend') }}
20 |
21 | # frontend: '%kernel.project_dir%/public/frontend/build'
22 |
23 | # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
24 | # Put in config/packages/prod/webpack_encore.yaml
25 | # cache: true
26 |
--------------------------------------------------------------------------------
/config/routes.yaml:
--------------------------------------------------------------------------------
1 | question_create:
2 | path: /questions/create
3 | methods: GET|POST
4 | controller: App\UserInterface\Controller\Question\CreateController
5 |
6 | question_update:
7 | path: /questions/{id}/update
8 | methods: GET|POST
9 | controller: App\UserInterface\Controller\Question\UpdateController
10 |
11 | registration:
12 | path: /registration
13 | methods: GET|POST
14 | controller: App\UserInterface\Controller\RegistrationController
15 |
16 | ask_password_reset:
17 | path: /reset-password
18 | methods: GET|POST
19 | controller: App\UserInterface\Controller\Security\AskPasswordResetController
20 |
21 | recover_password:
22 | path: /handle-reset-password/{email}/{token}
23 | methods: GET|POST
24 | controller: App\UserInterface\Controller\Security\RecoverPasswordController
25 |
26 | login:
27 | path: /login
28 | methods: GET|POST
29 | controller: App\UserInterface\Controller\LoginController
30 |
31 | logout:
32 | path: /logout
33 | methods: GET
34 | controller: App\UserInterface\Controller\LogoutController
35 |
36 | home:
37 | path: /
38 | methods: GET
39 | controller: Symfony\Bundle\FrameworkBundle\Controller\TemplateController
40 | defaults:
41 | template: home.html.twig
42 |
--------------------------------------------------------------------------------
/config/routes/annotations.yaml:
--------------------------------------------------------------------------------
1 | controllers:
2 | resource: ../../src/UserInterface/Controller/
3 | type: annotation
4 |
5 | kernel:
6 | resource: ../../src/Kernel.php
7 | type: annotation
8 |
--------------------------------------------------------------------------------
/config/routes/dev/framework.yaml:
--------------------------------------------------------------------------------
1 | _errors:
2 | resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
3 | prefix: /_error
4 |
--------------------------------------------------------------------------------
/config/routes/dev/web_profiler.yaml:
--------------------------------------------------------------------------------
1 | web_profiler_wdt:
2 | resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
3 | prefix: /_wdt
4 |
5 | web_profiler_profiler:
6 | resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
7 | prefix: /_profiler
8 |
--------------------------------------------------------------------------------
/config/services.yaml:
--------------------------------------------------------------------------------
1 | # This file is the entry point to configure your own services.
2 | # Files in the packages/ subdirectory configure your dependencies.
3 |
4 | # Put parameters here that don't need to change on each machine where the app is deployed
5 | # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
6 | parameters:
7 | app.from_email: 'no-reply@email.com'
8 | app.display_name: 'Code Challenge'
9 |
10 | services:
11 | # default configuration for services in *this* file
12 | _defaults:
13 | autowire: true # Automatically injects dependencies in your services.
14 | autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
15 | bind:
16 | $fromEmail: '%app.from_email%'
17 | $displayName: '%app.display_name%'
18 |
19 | # makes classes in src/ available to be used as services
20 | # this creates a service per class whose id is the fully-qualified class name
21 |
22 | App\Infrastructure\:
23 | resource: '../src/Infrastructure'
24 | exclude: '../src/Infrastructure/{Test}'
25 |
26 | App\UserInterface\:
27 | resource: '../src/UserInterface'
28 |
29 | TBoileau\CodeChallenge\Domain\:
30 | resource: '../domain/src'
31 |
32 | # controllers are imported separately to make sure services can be injected
33 | # as action arguments even if you don't extend any base controller class
34 | App\UserInterface\Controller\:
35 | resource: '../src/UserInterface/Controller'
36 | tags: ['controller.service_arguments']
37 |
--------------------------------------------------------------------------------
/config/services_integration.yaml:
--------------------------------------------------------------------------------
1 | # This file is the entry point to configure your own services.
2 | # Files in the packages/ subdirectory configure your dependencies.
3 |
4 | # Put parameters here that don't need to change on each machine where the app is deployed
5 | # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
6 | parameters:
7 | app.from_email: 'no-reply@email.com'
8 | app.display_name: 'Code Challenge'
9 |
10 | services:
11 | # default configuration for services in *this* file
12 | _defaults:
13 | autowire: true # Automatically injects dependencies in your services.
14 | autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
15 | bind:
16 | $fromEmail: '%app.from_email%'
17 | $displayName: '%app.display_name%'
18 |
19 | # makes classes in src/ available to be used as services
20 | # this creates a service per class whose id is the fully-qualified class name
21 |
22 | App\Infrastructure\:
23 | resource: '../src/Infrastructure'
24 | exclude: '../src/Infrastructure/Adapter'
25 |
26 | App\UserInterface\:
27 | resource: '../src/UserInterface'
28 |
29 | TBoileau\CodeChallenge\Domain\:
30 | resource: '../domain/src'
31 |
32 | # controllers are imported separately to make sure services can be injected
33 | # as action arguments even if you don't extend any base controller class
34 | App\UserInterface\Controller\:
35 | resource: '../src/UserInterface/Controller'
36 | tags: ['controller.service_arguments']
37 |
--------------------------------------------------------------------------------
/docs/img/alert_danger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/alert_danger.png
--------------------------------------------------------------------------------
/docs/img/alert_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/alert_dark.png
--------------------------------------------------------------------------------
/docs/img/alert_icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/alert_icons.png
--------------------------------------------------------------------------------
/docs/img/alert_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/alert_info.png
--------------------------------------------------------------------------------
/docs/img/alert_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/alert_light.png
--------------------------------------------------------------------------------
/docs/img/alert_primary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/alert_primary.png
--------------------------------------------------------------------------------
/docs/img/alert_secondary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/alert_secondary.png
--------------------------------------------------------------------------------
/docs/img/alert_success.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/alert_success.png
--------------------------------------------------------------------------------
/docs/img/alert_warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/alert_warning.png
--------------------------------------------------------------------------------
/docs/img/authentication.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/authentication.png
--------------------------------------------------------------------------------
/docs/img/button_danger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/button_danger.png
--------------------------------------------------------------------------------
/docs/img/button_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/button_dark.png
--------------------------------------------------------------------------------
/docs/img/button_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/button_icon.png
--------------------------------------------------------------------------------
/docs/img/button_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/button_info.png
--------------------------------------------------------------------------------
/docs/img/button_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/button_light.png
--------------------------------------------------------------------------------
/docs/img/button_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/button_outline.png
--------------------------------------------------------------------------------
/docs/img/button_primary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/button_primary.png
--------------------------------------------------------------------------------
/docs/img/button_secondary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/button_secondary.png
--------------------------------------------------------------------------------
/docs/img/button_sizes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/button_sizes.png
--------------------------------------------------------------------------------
/docs/img/button_success.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/button_success.png
--------------------------------------------------------------------------------
/docs/img/button_warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/button_warning.png
--------------------------------------------------------------------------------
/docs/img/card.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/card.png
--------------------------------------------------------------------------------
/docs/img/card_danger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/card_danger.png
--------------------------------------------------------------------------------
/docs/img/card_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/card_dark.png
--------------------------------------------------------------------------------
/docs/img/card_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/card_info.png
--------------------------------------------------------------------------------
/docs/img/card_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/card_light.png
--------------------------------------------------------------------------------
/docs/img/card_primary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/card_primary.png
--------------------------------------------------------------------------------
/docs/img/card_secondary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/card_secondary.png
--------------------------------------------------------------------------------
/docs/img/card_success.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/card_success.png
--------------------------------------------------------------------------------
/docs/img/card_warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/card_warning.png
--------------------------------------------------------------------------------
/docs/img/clean_architecture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/clean_architecture.jpg
--------------------------------------------------------------------------------
/docs/img/dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/dashboard.png
--------------------------------------------------------------------------------
/docs/img/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/grid.png
--------------------------------------------------------------------------------
/docs/img/grid_lg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/grid_lg.png
--------------------------------------------------------------------------------
/docs/img/grid_md.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/grid_md.png
--------------------------------------------------------------------------------
/docs/img/grid_sm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/grid_sm.png
--------------------------------------------------------------------------------
/docs/img/grid_xl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/grid_xl.png
--------------------------------------------------------------------------------
/docs/img/grid_xs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/grid_xs.png
--------------------------------------------------------------------------------
/docs/img/manage_questions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/manage_questions.png
--------------------------------------------------------------------------------
/docs/img/manage_users.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/manage_users.png
--------------------------------------------------------------------------------
/docs/img/packages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/packages.png
--------------------------------------------------------------------------------
/docs/img/ranking.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/ranking.png
--------------------------------------------------------------------------------
/docs/img/registration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/registration.png
--------------------------------------------------------------------------------
/docs/img/reply_quiz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/reply_quiz.png
--------------------------------------------------------------------------------
/docs/img/request.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/request.png
--------------------------------------------------------------------------------
/docs/img/sign_out.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/sign_out.png
--------------------------------------------------------------------------------
/docs/img/sitemap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/sitemap.png
--------------------------------------------------------------------------------
/docs/img/update_password.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/update_password.png
--------------------------------------------------------------------------------
/docs/img/update_profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TBoileau/code-challenge/d9f33144f1c1d85093e004d048de58a1d3637e16/docs/img/update_profile.png
--------------------------------------------------------------------------------
/docs/uml/authentication.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | left to right direction
3 | skinparam packageStyle rectangle
4 | actor Visiteur
5 | database Database
6 | database Mailer
7 | rectangle Authentification {
8 | Visiteur -- (Se connecter)
9 | (Se connecter) .> (Inscription) : include
10 | (Se connecter) -- Database
11 | (Oubi de mot de passe) .> (Se connecter) : extends
12 | (Oubi de mot de passe) -- Mailer
13 | }
14 | @enduml
--------------------------------------------------------------------------------
/docs/uml/dashboard.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | left to right direction
3 | skinparam packageStyle rectangle
4 | actor Utilisateur
5 | database Database
6 | rectangle Dashboard {
7 | Utilisateur -- (Visualiser ses statistiques)
8 | Utilisateur -- (Activités récentes)
9 | Utilisateur -- (Rang)
10 | (Visualiser ses statistiques) -- Database
11 | (Activités récentes) -- Database
12 | (Rang) -- Database
13 | }
14 | @enduml
--------------------------------------------------------------------------------
/docs/uml/manage_questions.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | left to right direction
3 | skinparam packageStyle rectangle
4 | actor Contributeur
5 | actor Administrateur
6 | database Database
7 | rectangle "Gestion des questions" {
8 | Contributeur -- (Lister ses\npropres question)
9 | Contributeur -- (Ajouter une\nquestion)
10 | Contributeur -- (Modifier une\nquestion)
11 | Contributeur -- (Supprimer une\nquestion)
12 | (Ajouter une\nquestion) -- Database
13 | (Lister ses\npropres question) -- Database
14 | (Modifier une\nquestion) -- Database
15 | (Supprimer une\nquestion) -- Database
16 | (Valider une\nquestion) -- Database
17 | Administrateur -- (Valider une\nquestion)
18 | }
19 | Administrateur -> Contributeur
20 | @enduml
--------------------------------------------------------------------------------
/docs/uml/manage_users.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | left to right direction
3 | skinparam packageStyle rectangle
4 | actor Administrateur
5 | database Database
6 | rectangle "Gestion des utilisateurs" {
7 | Administrateur -- (Lister les\nutilisateurs)
8 | Administrateur -- (Ajouter un\nutilisateur)
9 | Administrateur -- (Modifier un\nutilisateur)
10 | Administrateur -- (Supprimer un\nutilisateur)
11 | (Lister les\nutilisateurs) -- Database
12 | (Ajouter un\nutilisateur) -- Database
13 | (Modifier un\nutilisateur) -- Database
14 | (Supprimer un\nutilisateur) -- Database
15 | }
16 | @enduml
--------------------------------------------------------------------------------
/docs/uml/packages.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | left to right direction
3 | skinparam packageStyle rectangle
4 | actor Visiteur
5 | actor Utilisateur
6 | actor Contributeur
7 | actor Administrateur
8 | database Database
9 | database Mailer
10 | database Stockage
11 | rectangle "Code Challenge" {
12 | Visiteur -- [Se connecter]
13 | Visiteur -- [S'inscrire]
14 | [Se connecter] -- Database
15 | [Se connecter] -- Mailer
16 | [S'inscrire] -- Database
17 | Utilisateur -- [Se déconnecter]
18 | Utilisateur -- [Répondre à\nun quiz]
19 | Utilisateur -- [Dashboard]
20 | Utilisateur -- [Modifier son\nmot de passe]
21 | Utilisateur -- [Modifier son\nprofil]
22 | Utilisateur -- [Classement]
23 | [Répondre à\nun quiz] -- Database
24 | [Dashboard] -- Database
25 | [Modifier son\nmot de passe] -- Database
26 | [Modifier son\nprofil] -- Database
27 | [Modifier son\nprofil] -- Stockage
28 | [Classement] -- Database
29 | Contributeur -- [Gestion des\nquestions]
30 | [Gestion des\nquestions] -- Database
31 | Administrateur -- [Gestion des\nutilisateurs]
32 | Administrateur -- [Gestion des\nquestions]
33 | [Gestion des\nutilisateurs] -- Database
34 | [Gestion des\nquestions] -- Database
35 | }
36 | Contributeur -> Utilisateur
37 | Administrateur -> Contributeur
38 | @enduml
--------------------------------------------------------------------------------
/docs/uml/ranking.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | left to right direction
3 | skinparam packageStyle rectangle
4 | actor Utilisateur
5 | database Database
6 | rectangle "Classement" {
7 | Utilisateur -- (Visualiser la liste des\nutilisateurs classés par point)
8 | (Rang) .> (Visualiser la liste des\nutilisateurs classés par point) : include
9 | (Visualiser la liste des\nutilisateurs classés par point) -- Database
10 | }
11 | @enduml
--------------------------------------------------------------------------------
/docs/uml/registration.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | left to right direction
3 | skinparam packageStyle rectangle
4 | actor Visiteur
5 | database Database
6 | rectangle Inscription {
7 | Visiteur -- (S'inscrire)
8 | (S'inscrire) .> (Accepter les CGU) : include
9 | (S'inscrire) -- Database
10 | }
11 | @enduml
--------------------------------------------------------------------------------
/docs/uml/reply_quiz.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | left to right direction
3 | skinparam packageStyle rectangle
4 | actor Utilisateur
5 | database Database
6 | rectangle "Répondre à un quiz" {
7 | Utilisateur -- (Répondre aux\nquestions)
8 | (Répondre aux\nquestions) .> (Visualiser les\nrésultats) : include
9 | (Répondre aux\nquestions) -- Database
10 | (Répondre aux\nquestions) .> (Mise à jour\ndes résultats) : include
11 | (Répondre aux\nquestions) .> (Mise à jour\ndu classement) : include
12 | }
13 | @enduml
--------------------------------------------------------------------------------
/docs/uml/request.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | box "Symfony" #LightGrey
3 | participant HTTPFoundation
4 | participant Kernel
5 | participant Router
6 | end box
7 | box "UserInterface" #LightBlue
8 | participant BlogController
9 | participant ReadPostPresenter
10 | participant PostViewModel
11 | end box
12 | box "Domain" #LightRed
13 | participant ReadPost
14 | participant ReadPostRequest
15 | participant ReadPostPresenterInterface
16 | participant ReadPostResponse
17 | participant Post
18 | participant PostGateway
19 | end box
20 | box "Infrastructure" #LightGreen
21 | participant PostRepository
22 | end box
23 |
24 | HTTPFoundation -> Kernel : Envoie de la requête
25 | Kernel ->Router : Récupère la route
26 | Router -> Kernel : Renvoie les informations\nde la route
27 | Kernel -> BlogController : Appelle la méthode read()
28 | BlogController -> ReadPostRequest : Instancie la requête
29 | note right of BlogController
30 | En lui passant le **slug**
31 | récupérer dans l'url
32 | end note
33 | BlogController -> ReadPostPresenter : Instancie le présenteur
34 | BlogController -> ReadPost : Appelle la méthode execute()
35 | ReadPost -> PostGateway : Récupère l'article
36 | note right of ReadPost
37 | En lui passant le **slug**
38 | récupérer dans **ReadPostRequest**
39 | end note
40 | PostGateway -> PostRepository : Requête DQL pour\npour récupérer l'article
41 | note left of PostRepository
42 | PostRepository implémentant l'interface PostGateway,
43 | en injectant seulement l'interface
44 | Symfony vous injectera une instance de PostRepository.
45 | end note
46 | PostRepository -> Post : Instancie et hydrate un article
47 | PostRepository -> ReadPost : Renvoie un article
48 | ReadPost -> ReadPostResponse : Instancie et hydrate la réponse\navec l'article récupéré
49 | ReadPost -> ReadPostPresenterInterface : Présente la réponse
50 | ReadPostPresenterInterface -> ReadPostPresenter : Prépare la vue à retourner
51 | ReadPostPresenter -> PostViewModel : Instancie et hydrate un PostViewModel\navec les données de la réponse
52 | BlogController -> HTTPFoundation : Créer et renvoie la réponse\nen passant à la vue la vue modèle\nprésenter ultérieurement
53 |
54 | @enduml
--------------------------------------------------------------------------------
/docs/uml/sign_out.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | left to right direction
3 | skinparam packageStyle rectangle
4 | actor Utilisateur
5 | actor Visiteur
6 | rectangle Déconnexion {
7 | Utilisateur -- (Se déconnecter)
8 | (Se déconnecter) .> (Connexion) : include
9 | Visiteur -- (Connexion)
10 | }
11 | @enduml
--------------------------------------------------------------------------------
/docs/uml/sitemap.puml:
--------------------------------------------------------------------------------
1 | @startwbs
2 | * Page d'accueil
3 | ** Authentification
4 | *** Oubli de mot de passe
5 | ** Inscription
6 | ** Gestion des utilisateurs
7 | *** Ajouter un utilisateur
8 | *** Modifier un utilisateur
9 | *** Supprimer un utilisateur
10 | ** Gestion des questions
11 | *** Valider une question
12 | *** Ajouter une question
13 | *** Modifier une question
14 | *** Supprimer une question
15 | ** Quiz
16 | *** Répondre au quiz
17 | *** Résultats du quiz
18 | *** Mise à jour du classement
19 | ** Classement
20 | ** Dashboard
21 | *** Modifier mon profil
22 | *** Modifier mon mot de passe
23 |
24 | @endwbs
--------------------------------------------------------------------------------
/docs/uml/update_password.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | left to right direction
3 | skinparam packageStyle rectangle
4 | actor Utilisateur
5 | database Database
6 | rectangle "Modifier son mot de passe" {
7 | Utilisateur -- (Modifier son mot de passe)
8 | (Modifier son mot de passe) -- Database
9 | }
10 | @enduml
--------------------------------------------------------------------------------
/docs/uml/update_profile.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | left to right direction
3 | skinparam packageStyle rectangle
4 | actor Utilisateur
5 | database Database
6 | database Stockage
7 | rectangle "Modifier son profil" {
8 | Utilisateur -- (Modifier son profil)
9 | (Uploader un avatar) .> (Modifier son profil) : extends
10 | (Modifier son profil) -- Database
11 | (Uploader un avatar) -- Stockage
12 | }
13 | @enduml
--------------------------------------------------------------------------------
/domain/src/Quiz/Entity/Answer.php:
--------------------------------------------------------------------------------
1 | id = $id;
48 | $this->title = $title;
49 | $this->good = $good;
50 | }
51 |
52 | /**
53 | * @return UuidInterface
54 | */
55 | public function getId(): UuidInterface
56 | {
57 | return $this->id;
58 | }
59 |
60 | /**
61 | * @return string
62 | */
63 | public function getTitle(): string
64 | {
65 | return $this->title;
66 | }
67 |
68 | /**
69 | * @return bool
70 | */
71 | public function isGood(): bool
72 | {
73 | return $this->good;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/domain/src/Quiz/Entity/Question.php:
--------------------------------------------------------------------------------
1 | getTitle(),
40 | array_map(fn (array $answer) => Answer::fromArray($answer), $createRequest->getAnswers())
41 | );
42 | }
43 |
44 | /**
45 | * Question constructor.
46 | * @param UuidInterface $id
47 | * @param string $title
48 | * @param Answer[] $answers
49 | */
50 | public function __construct(UuidInterface $id, string $title, array $answers)
51 | {
52 | $this->id = $id;
53 | $this->title = $title;
54 | $this->answers = $answers;
55 | }
56 |
57 | /**
58 | * @return UuidInterface
59 | */
60 | public function getId(): UuidInterface
61 | {
62 | return $this->id;
63 | }
64 |
65 | /**
66 | * @return string
67 | */
68 | public function getTitle(): string
69 | {
70 | return $this->title;
71 | }
72 |
73 | /**
74 | * @param string $title
75 | */
76 | public function setTitle(string $title): void
77 | {
78 | $this->title = $title;
79 | }
80 |
81 | /**
82 | * @return Answer[]
83 | */
84 | public function getAnswers(): array
85 | {
86 | return $this->answers;
87 | }
88 |
89 | /**
90 | * @param Answer[] $answers
91 | */
92 | public function setAnswers(array $answers): void
93 | {
94 | $this->answers = $answers;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/domain/src/Quiz/Gateway/QuestionGateway.php:
--------------------------------------------------------------------------------
1 | title = $title;
42 | $this->answers = $answers;
43 | }
44 |
45 | /**
46 | * @return string
47 | */
48 | public function getTitle(): string
49 | {
50 | return $this->title;
51 | }
52 |
53 | /**
54 | * @return array
55 | */
56 | public function getAnswers(): array
57 | {
58 | return $this->answers;
59 | }
60 |
61 | /**
62 | * @throws AssertionFailedException
63 | */
64 | public function validate(): void
65 | {
66 | Assertion::notBlank($this->title);
67 | Assertion::minCount($this->answers, 2);
68 | Assertion::allNotBlank(array_map(fn (array $answer) => $answer["title"], $this->answers));
69 | Assertion::allBoolean(array_map(fn (array $answer) => $answer["good"], $this->answers));
70 | Assertion::minCount(array_filter($this->answers, fn (array $answer) => $answer["good"]), 1);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/domain/src/Quiz/Request/UpdateRequest.php:
--------------------------------------------------------------------------------
1 | id = $id;
50 | $this->title = $title;
51 | $this->answers = $answers;
52 | }
53 |
54 | /**
55 | * @return UuidInterface
56 | */
57 | public function getId(): UuidInterface
58 | {
59 | return $this->id;
60 | }
61 |
62 | /**
63 | * @return string
64 | */
65 | public function getTitle(): string
66 | {
67 | return $this->title;
68 | }
69 |
70 | /**
71 | * @return array
72 | */
73 | public function getAnswers(): array
74 | {
75 | return $this->answers;
76 | }
77 |
78 | /**
79 | * @throws AssertionFailedException
80 | */
81 | public function validate(): void
82 | {
83 | Assertion::notBlank($this->title);
84 | Assertion::minCount($this->answers, 2);
85 | Assertion::allNotBlank(array_map(fn (array $answer) => $answer["title"], $this->answers));
86 | Assertion::allBoolean(array_map(fn (array $answer) => $answer["good"], $this->answers));
87 | Assertion::minCount(array_filter($this->answers, fn (array $answer) => $answer["good"]), 1);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/domain/src/Quiz/Response/CreateResponse.php:
--------------------------------------------------------------------------------
1 | question = $question;
25 | }
26 |
27 | /**
28 | * @return Question
29 | */
30 | public function getQuestion(): Question
31 | {
32 | return $this->question;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/domain/src/Quiz/Response/UpdateResponse.php:
--------------------------------------------------------------------------------
1 | question = $question;
25 | }
26 |
27 | /**
28 | * @return Question
29 | */
30 | public function getQuestion(): Question
31 | {
32 | return $this->question;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/domain/src/Quiz/UseCase/Create.php:
--------------------------------------------------------------------------------
1 | questionGateway = $questionGateway;
30 | }
31 |
32 | /**
33 | * @param CreateRequest $request
34 | * @param CreatePresenterInterface $presenter
35 | * @throws AssertionFailedException
36 | */
37 | public function execute(CreateRequest $request, CreatePresenterInterface $presenter)
38 | {
39 | $request->validate();
40 |
41 | $question = Question::fromCreate($request);
42 |
43 | $this->questionGateway->create($question);
44 |
45 | $presenter->present(new CreateResponse($question));
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/domain/src/Quiz/UseCase/Update.php:
--------------------------------------------------------------------------------
1 | questionGateway = $questionGateway;
31 | }
32 |
33 | /**
34 | * @param UpdateRequest $request
35 | * @param UpdatePresenterInterface $presenter
36 | * @throws AssertionFailedException
37 | */
38 | public function execute(UpdateRequest $request, UpdatePresenterInterface $presenter)
39 | {
40 | $request->validate();
41 |
42 | $question = $this->questionGateway->getQuestionById($request->getId());
43 | $question->setTitle($request->getTitle());
44 | $question->setAnswers(
45 | array_map(fn (array $answer) => Answer::fromArray($answer), $request->getAnswers())
46 | );
47 |
48 | $this->questionGateway->update($question);
49 |
50 | $presenter->present(new UpdateResponse($question));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/domain/src/Security/Assert/Assertion.php:
--------------------------------------------------------------------------------
1 | isPseudoUnique($pseudo)) {
26 | throw new NonUniqueEmailException("This email should be unique !", self::EXISTING_PSEUDO);
27 | }
28 | }
29 |
30 | /**
31 | * @param string $email
32 | * @param ParticipantGateway $participantGateway
33 | */
34 | public static function nonUniqueEmail(string $email, ParticipantGateway $participantGateway): void
35 | {
36 | if (!$participantGateway->isEmailUnique($email)) {
37 | throw new NonUniqueEmailException("This email should be unique !", self::EXISTING_EMAIL);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/domain/src/Security/Exception/NonUniqueEmailException.php:
--------------------------------------------------------------------------------
1 | email = $email;
27 | }
28 |
29 | public static function create(string $email): self
30 | {
31 | return new self($email);
32 | }
33 |
34 | /**
35 | * @return string
36 | */
37 | public function getEmail(): string
38 | {
39 | return $this->email;
40 | }
41 |
42 | /**
43 | * @throws AssertionFailedException
44 | */
45 | public function validate(): void
46 | {
47 | Assertion::notBlank($this->email);
48 | Assertion::email($this->email);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/domain/src/Security/Request/LoginRequest.php:
--------------------------------------------------------------------------------
1 | email = $email;
41 | $this->password = $password;
42 | }
43 |
44 | /**
45 | * @return string
46 | */
47 | public function getEmail(): string
48 | {
49 | return $this->email;
50 | }
51 |
52 | /**
53 | * @return string
54 | */
55 | public function getPassword(): string
56 | {
57 | return $this->password;
58 | }
59 |
60 | public function validate(): void
61 | {
62 | Assertion::notBlank($this->email, "Email should not be blank.");
63 | Assertion::notBlank($this->password, "Password should not be blank.");
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/domain/src/Security/Request/RecoverPasswordRequest.php:
--------------------------------------------------------------------------------
1 | email = $email;
39 | $this->newPlainPassword = $newPlainPassword;
40 | $this->token = $token;
41 | }
42 |
43 |
44 | /**
45 | * @return string
46 | */
47 | public function getEmail(): string
48 | {
49 | return $this->email;
50 | }
51 |
52 | /**
53 | * @return string
54 | */
55 | public function getNewPlainPassword(): string
56 | {
57 | return $this->newPlainPassword;
58 | }
59 |
60 | /**
61 | * @return string
62 | */
63 | public function getToken(): string
64 | {
65 | return $this->token;
66 | }
67 |
68 | /**
69 | * @throws AssertionFailedException
70 | */
71 | public function validate(): void
72 | {
73 | Assertion::notBlank($this->email);
74 | //Assertion::email($this->email);
75 | Assertion::notBlank($this->token);
76 | Assertion::notBlank($this->newPlainPassword);
77 | Assertion::minLength($this->newPlainPassword, 8);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/domain/src/Security/Response/AskPasswordResetResponse.php:
--------------------------------------------------------------------------------
1 | participant = $participant;
25 | }
26 |
27 | /**
28 | * @return Participant
29 | */
30 | public function getParticipant(): Participant
31 | {
32 | return $this->participant;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/domain/src/Security/Response/LoginResponse.php:
--------------------------------------------------------------------------------
1 | participant = $participant;
31 | $this->passwordValid = $passwordValid;
32 | }
33 |
34 | /**
35 | * @return Participant|null
36 | */
37 | public function getParticipant(): ?Participant
38 | {
39 | return $this->participant;
40 | }
41 |
42 | /**
43 | * @return bool
44 | */
45 | public function isPasswordValid(): bool
46 | {
47 | return $this->passwordValid;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/domain/src/Security/Response/RecoverPasswordResponse.php:
--------------------------------------------------------------------------------
1 | participant = $participant;
25 | }
26 |
27 | /**
28 | * @return Participant
29 | */
30 | public function getParticipant(): Participant
31 | {
32 | return $this->participant;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/domain/src/Security/Response/RegistrationResponse.php:
--------------------------------------------------------------------------------
1 | email = $email;
26 | }
27 |
28 | /**
29 | * @return string
30 | */
31 | public function getEmail(): string
32 | {
33 | return $this->email;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/domain/src/Security/UseCase/Login.php:
--------------------------------------------------------------------------------
1 | participant = $participant;
29 | }
30 |
31 | /**
32 | * @param LoginRequest $request
33 | * @param LoginPresenterInterface $presenter
34 | */
35 | public function execute(LoginRequest $request, LoginPresenterInterface $presenter)
36 | {
37 | $request->validate();
38 |
39 | $participant = $this->participant->getParticipantByEmail($request->getEmail());
40 |
41 | if ($participant) {
42 | $passwordValid = password_verify($request->getPassword(), $participant->getPassword());
43 | }
44 |
45 | $presenter->present(new LoginResponse($participant, $passwordValid ?? false));
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/domain/src/Security/UseCase/Registration.php:
--------------------------------------------------------------------------------
1 | userGateway = $participantGateway;
32 | }
33 |
34 | /**
35 | * @param RegistrationRequest $request
36 | * @param RegistrationPresenterInterface $presenter
37 | */
38 | public function execute(RegistrationRequest $request, RegistrationPresenterInterface $presenter)
39 | {
40 | $request->validate($this->userGateway);
41 | $user = Participant::fromRegistration($request);
42 | $this->userGateway->register($user);
43 | $presenter->present(new RegistrationResponse($user->getEmail()));
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/domain/src/System/Gateway/LogGateway.php:
--------------------------------------------------------------------------------
1 | log = $log;
25 | }
26 |
27 | /**
28 | * @return Log
29 | */
30 | public function getLog(): Log
31 | {
32 | return $this->log;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/domain/src/System/UseCase/Track.php:
--------------------------------------------------------------------------------
1 | participantGateway = $participantGateway;
37 | $this->logGateway = $logGateway;
38 | }
39 |
40 | /**
41 | * @param TrackRequest $request
42 | * @param TrackPresenterInterface $presenter
43 | */
44 | public function execute(TrackRequest $request, TrackPresenterInterface $presenter)
45 | {
46 | $participant = $request->getEmail() === null
47 | ? null
48 | : $this->participantGateway->getParticipantByEmail($request->getEmail())
49 | ;
50 |
51 | $log = Log::fromTrack($request, $participant);
52 |
53 | $this->logGateway->insert($log);
54 |
55 | $presenter->present(new TrackResponse($log));
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/domain/tests/Fixtures/Adapter/LogRepository.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Une demande de réinitialisation de ton mot de passe a été initée. Si tu en es l'auteur, clique sur le lien ci-dessous pour changer 7 | ton mot de passe. Sinon, prière ignorer cet email et notifier l'équipe de Code Challenge.
8 | 9 |Cordialement,
10 | {% endblock %} 11 | 12 | {% block action %} 13 |