├── .env ├── .env.test ├── .gitignore ├── .symfony.cloud.yaml ├── .symfony ├── routes.yaml └── services.yaml ├── README.md ├── assets ├── .gitignore ├── css │ └── app.scss ├── feedback │ ├── Feedback.vue │ └── index.js ├── js │ └── app.js └── reactions │ ├── Reactions.vue │ ├── ReceivedReaction.vue │ └── index.js ├── bin ├── console └── phpunit ├── composer.json ├── composer.lock ├── config ├── bootstrap.php ├── bundles.php ├── packages │ ├── api_platform.yaml │ ├── assets.yaml │ ├── dev │ │ ├── debug.yaml │ │ ├── easy_log_handler.yaml │ │ ├── hautelook_alice.yaml │ │ ├── monolog.yaml │ │ ├── nelmio_alice.yaml │ │ ├── routing.yaml │ │ ├── swiftmailer.yaml │ │ └── web_profiler.yaml │ ├── doctrine.yaml │ ├── doctrine_migrations.yaml │ ├── framework.yaml │ ├── mercure.yaml │ ├── nelmio_cors.yaml │ ├── prod │ │ ├── doctrine.yaml │ │ └── monolog.yaml │ ├── routing.yaml │ ├── security.yaml │ ├── sensio_framework_extra.yaml │ ├── swiftmailer.yaml │ ├── test │ │ ├── framework.yaml │ │ ├── hautelook_alice.yaml │ │ ├── monolog.yaml │ │ ├── nelmio_alice.yaml │ │ ├── routing.yaml │ │ ├── swiftmailer.yaml │ │ └── web_profiler.yaml │ ├── translation.yaml │ ├── twig.yaml │ ├── validator.yaml │ └── webpack_encore.yaml ├── routes.yaml ├── routes │ ├── annotations.yaml │ ├── api_platform.yaml │ └── dev │ │ ├── twig.yaml │ │ └── web_profiler.yaml └── services.yaml ├── fixtures ├── .gitignore └── session.yaml ├── package.json ├── php.ini ├── phpunit.xml.dist ├── public ├── assets │ ├── logotilleuls.png │ ├── symfonycon.jpg │ └── symfonycon.png └── index.php ├── src ├── Controller │ ├── .gitignore │ └── SessionController.php ├── Entity │ ├── .gitignore │ ├── Feedback.php │ ├── Reaction.php │ └── Session.php ├── Kernel.php ├── Migrations │ ├── .gitignore │ ├── Version20181129151241.php │ ├── Version20181129215541.php │ ├── Version20181130211250.php │ ├── Version20181130215702.php │ └── Version20181130221307.php ├── Repository │ ├── .gitignore │ ├── FeedbackRepository.php │ ├── ReactionRepository.php │ └── SessionRepository.php └── Serializer │ └── ReactionCollectionNormalizer.php ├── symfony.lock ├── templates ├── base.html.twig └── session │ ├── detail.html.twig │ ├── index.html.twig │ ├── session.html.twig │ └── title.html.twig ├── tests ├── .gitignore └── SessionTest.php ├── translations └── .gitignore ├── webpack.config.js └── yarn.lock /.env: -------------------------------------------------------------------------------- 1 | # This file defines all environment variables that the application needs. 2 | # DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE. 3 | # Use ".env.local" for local overrides during development. 4 | # Use real environment variables when deploying to production. 5 | # https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration 6 | 7 | ###> symfony/framework-bundle ### 8 | APP_ENV=dev 9 | APP_SECRET=e32bb8872d9ef0b3fdd9c4a57d0b161a 10 | #TRUSTED_PROXIES=127.0.0.1,127.0.0.2 11 | #TRUSTED_HOSTS='^localhost|example\.com$' 12 | ###< symfony/framework-bundle ### 13 | 14 | ###> doctrine/doctrine-bundle ### 15 | # Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url 16 | # For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db" 17 | # Configure your db driver and server_version in config/packages/doctrine.yaml 18 | DATABASE_URL=mysql://panther:password@127.0.0.1:3306/symfonycon 19 | ###< doctrine/doctrine-bundle ### 20 | 21 | ###> symfony/swiftmailer-bundle ### 22 | # For Gmail as a transport, use: "gmail://username:password@localhost" 23 | # For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode=" 24 | # Delivery is disabled by default via "null://localhost" 25 | MAILER_URL=null://localhost 26 | ###< symfony/swiftmailer-bundle ### 27 | 28 | ###> nelmio/cors-bundle ### 29 | CORS_ALLOW_ORIGIN=^https?://localhost(:[0-9]+)?$ 30 | ###< nelmio/cors-bundle ### 31 | 32 | ###> symfony/mercure-bundle ### 33 | MERCURE_PUBLISH_URL=http://localhost:3000/hub 34 | # Secret key: ChangeMe 35 | # Payload: {"mercure": {"publish": ["*"], "subscribe": ["*"]}} 36 | # These values are INSECURE 37 | MERCURE_JWT_SECRET=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdLCJzdWJzY3JpYmUiOlsiKiJdfX0.D9flW5jbVQ8VvpXXTSXGLSjlhV3VNzApsvj_Ho61ig0 38 | ###< symfony/mercure-bundle ### 39 | -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | # define your env variables for the test env here 2 | KERNEL_CLASS='App\Kernel' 3 | APP_SECRET='s$cretf0rt3st' 4 | SYMFONY_DEPRECATIONS_HELPER=999999 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ###> symfony/framework-bundle ### 3 | /.env.local 4 | /.env.*.local 5 | /public/bundles/ 6 | /var/ 7 | /vendor/ 8 | ###< symfony/framework-bundle ### 9 | 10 | ###> symfony/phpunit-bridge ### 11 | .phpunit 12 | /phpunit.xml 13 | ###< symfony/phpunit-bridge ### 14 | 15 | ###> symfony/web-server-bundle ### 16 | /.web-server-pid 17 | ###< symfony/web-server-bundle ### 18 | 19 | .php_cs.cache 20 | 21 | ###> symfony/webpack-encore-bundle ### 22 | /node_modules/ 23 | /public/build/ 24 | npm-debug.log 25 | yarn-error.log 26 | ###< symfony/webpack-encore-bundle ### 27 | -------------------------------------------------------------------------------- /.symfony.cloud.yaml: -------------------------------------------------------------------------------- 1 | name: demo-symfonycon 2 | 3 | type: php:7.2 4 | 5 | dependencies: 6 | nodejs: 7 | yarn: "*" 8 | 9 | runtime: 10 | extensions: 11 | - apcu 12 | - mbstring 13 | - pdo_mysql 14 | 15 | build: 16 | flavor: none 17 | 18 | relationships: 19 | database: mysqldb:mysql 20 | 21 | web: 22 | locations: 23 | "/": 24 | root: "public" 25 | expires: 1h 26 | passthru: "/index.php" 27 | 28 | disk: 2048 29 | 30 | mounts: 31 | "/var": "shared:files/var" 32 | 33 | hooks: 34 | build: | 35 | set -x -e 36 | 37 | curl -s https://get.symfony.com/cloud/configurator | (>&2 bash) 38 | (>&2 symfony-build) 39 | (>&2 yarn install) 40 | (>&2 yarn build) 41 | 42 | deploy: | 43 | set -x -e 44 | 45 | (>&2 symfony-deploy) 46 | -------------------------------------------------------------------------------- /.symfony/routes.yaml: -------------------------------------------------------------------------------- 1 | "https://{default}/": { type: upstream, upstream: "demo-symfonycon:http" } 2 | "http://{default}/": { type: redirect, to: "https://{default}/" } 3 | -------------------------------------------------------------------------------- /.symfony/services.yaml: -------------------------------------------------------------------------------- 1 | mysqldb: 2 | type: mysql:10.2 3 | disk: 1024 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A modern joind.in clone built with Symfony 4, Vue.js and Mercure 2 | 3 | Browse the demo website: https://symfonycon.les-tilleuls.coop 4 | And don't forget to [rate this talk on joind.in](https://joind.in/event/symfonycon-lisbon-2018/integrate-vuejs-components-in-a-symfony-app-add-e2e-tests-with-panther) 5 | 6 | ## Execute locally 7 | 8 | * Install the PHP dependencies: `composer install` 9 | * Install the JS dependencies: `yarn install` 10 | * Start the PHP dev web server: `bin/console server:start` 11 | * Start the Encore dev web server: `yarn encore dev-server --hot` 12 | * Download Mercure hub on https://mercure.rocks 13 | * Start the Mercure hub: `ALLOW_ANONYMOUS=1 CORS_ALLOWED_ORIGINS=http://localhost:8000 JWT_KEY=ChangeMe ADDR=:3000 ./mercure` 14 | -------------------------------------------------------------------------------- /assets/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dunglas/symfonycon-lisbon/0b80de543538563175688801808e159d9a3cb40a/assets/.gitignore -------------------------------------------------------------------------------- /assets/css/app.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | // Import a Google Font 4 | @import url("https://fonts.googleapis.com/css?family=Open+Sans:400,700"); 5 | // Import Font Awesome 6 | @import "~@fortawesome/fontawesome-free/css/all.css"; 7 | 8 | // Update Bulma's global variables 9 | $family-sans-serif: 'Open Sans', sans-serif; 10 | $primary: #e31f1b; 11 | $dark: #332b2c; 12 | $link: $primary; 13 | 14 | // Import only what you need from Bulma 15 | @import "~bulma/sass/utilities/_all.sass"; 16 | @import "~bulma/sass/base/_all.sass"; 17 | @import "~bulma/sass/elements/_all.sass"; 18 | @import "~bulma/sass/components/_all.sass"; 19 | @import "~bulma/sass/layout/_all.sass"; 20 | @import "~bulma/sass/grid/_all.sass"; 21 | 22 | .section { 23 | padding: 1.5rem; 24 | } 25 | 26 | .header-main { 27 | box-shadow: 0 14px 28px rgba(0, 0, 0, .06), 0 10px 10px rgba(0, 0, 0, .06); 28 | background-color: white; 29 | position: fixed; 30 | height: 75px; 31 | width: 100%; 32 | z-index: 50; 33 | } 34 | 35 | .header-main .logo { 36 | width: 120px; 37 | height: auto; 38 | position: absolute; 39 | left: 50%; 40 | } 41 | 42 | .main { 43 | padding-top: 75px; 44 | } 45 | 46 | .title-wrapper { 47 | height: 150px; 48 | display: flex; 49 | flex-direction: column; 50 | justify-content: flex-end; 51 | position: relative; 52 | overflow: hidden; 53 | } 54 | 55 | .title-wrapper:before { 56 | content: ""; 57 | position: absolute; 58 | width: 120%; 59 | height: 200%; 60 | transform: translate(-50%, -50%) rotate(-5deg); 61 | background-image: url(/assets/symfonycon.png); 62 | background-size: 140px; 63 | background-position: center; 64 | opacity: 0.5; 65 | left: 50%; 66 | top: 50%; 67 | 68 | } 69 | 70 | .title-wrapper:after { 71 | content: ""; 72 | width: 100%; 73 | height: 1px; 74 | box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.3); 75 | z-index: 1; 76 | position: absolute; 77 | top: 100%; 78 | left: 0; 79 | } 80 | 81 | .title-wrapper .title-band { 82 | z-index: 2; 83 | text-align: right; 84 | } 85 | 86 | .title-wrapper .title { 87 | background-color: white; 88 | display: inline-block; 89 | padding: 10px 20px; 90 | border-radius: 5px 5px 0 0; 91 | color: #714393; 92 | box-shadow: 0px -14px 28px 0px rgba(0, 0, 0, 0.1); 93 | } 94 | 95 | .session-item { 96 | padding: 10px; 97 | } 98 | 99 | .session-item:last-child { 100 | border-bottom: none; 101 | } 102 | 103 | .is-bordered { 104 | border-bottom: 3px solid #594648; 105 | } 106 | 107 | .is-white { 108 | color: white; 109 | } 110 | 111 | .calendar { 112 | text-align: center; 113 | padding: 10px; 114 | } 115 | 116 | .calendar .day-name { 117 | text-transform: uppercase; 118 | font-size: 0.75rem; 119 | font-weight: bold; 120 | } 121 | 122 | .calendar .hour { 123 | padding: 0 0 10px 0; 124 | } 125 | 126 | .reaction .icon { 127 | border-radius: 50%; 128 | margin: 5px; 129 | } 130 | 131 | .reaction .icon i { 132 | color: white; 133 | } 134 | 135 | .reaction.hearts, .reaction.hearts i, .fa-grin-hearts { 136 | color: $primary; 137 | } 138 | 139 | .reaction.stars, .reaction.stars i, .fa-grin-stars { 140 | color: #ed8746; 141 | } 142 | 143 | .reaction.tears, .reaction.tears i, .fa-grin-tears { 144 | color: #67d3bb; 145 | } 146 | 147 | .detail { 148 | overflow: hidden; 149 | padding: 10px; 150 | } 151 | 152 | .reactions-wrapper { 153 | align-items: flex-end; 154 | display: flex; 155 | justify-content: center; 156 | } 157 | 158 | .reactions { 159 | text-align: center; 160 | transform: translateY(30px) rotate(-3deg); 161 | position: relative; 162 | } 163 | 164 | .comments .media-content { 165 | overflow: hidden; 166 | } 167 | 168 | .comments .media .icon { 169 | background: #f5f5f5; 170 | width: 70px; 171 | height: 70px; 172 | } 173 | 174 | .comments-wrapper { 175 | padding: 20px 0; 176 | } 177 | 178 | .comments .star-txt { 179 | margin: 0 0 0 10px !important; 180 | } 181 | 182 | .comments form { 183 | padding: 20px; 184 | } 185 | 186 | .comments form .vue-star-rating { 187 | margin: 10px 0; 188 | } 189 | 190 | .comments form .media .icon { 191 | background-color: #fff; 192 | } 193 | 194 | .animated { 195 | animation: up 2s ease-in-out; 196 | position: fixed; 197 | bottom: 0; 198 | } 199 | 200 | @keyframes up { 201 | 0% { 202 | opacity: 1; 203 | top: 100%; 204 | } 205 | 100% { 206 | opacity: 0; 207 | top: 0; 208 | transform: scale(20) rotate(0deg); 209 | } 210 | } 211 | 212 | .footer { 213 | a:link, a:visited, a:hover, a:active { 214 | color: $link 215 | } 216 | 217 | #symfonycloud { 218 | width: 100px; 219 | bottom: -3px; 220 | position: relative; 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /assets/feedback/Feedback.vue: -------------------------------------------------------------------------------- 1 | 2 | 57 | 58 | 93 | -------------------------------------------------------------------------------- /assets/feedback/index.js: -------------------------------------------------------------------------------- 1 | // assets/feedback/index.js 2 | import Vue from 'vue'; 3 | import StarRating from 'vue-star-rating'; // yarn add vue-star-rating 4 | import Feedback from './Feedback'; 5 | 6 | Vue.component('star-rating', StarRating); 7 | 8 | new Vue({ 9 | el: '#feedback', 10 | components: {Feedback} 11 | }); 12 | -------------------------------------------------------------------------------- /assets/js/app.js: -------------------------------------------------------------------------------- 1 | require('../css/app.scss'); 2 | -------------------------------------------------------------------------------- /assets/reactions/Reactions.vue: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 84 | -------------------------------------------------------------------------------- /assets/reactions/ReceivedReaction.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 38 | -------------------------------------------------------------------------------- /assets/reactions/index.js: -------------------------------------------------------------------------------- 1 | // assets/reactions/index.js 2 | import Vue from 'vue'; 3 | import Reactions from './Reactions'; 4 | 5 | new Vue({ 6 | el: '#reactions', 7 | components: { Reactions} 8 | }); 9 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getParameterOption(['--env', '-e'], null, true)) { 19 | putenv('APP_ENV='.$_ENV['APP_ENV']); 20 | // force loading .env files when --env is defined 21 | $_SERVER['APP_ENV'] = null; 22 | } 23 | 24 | if ($input->hasParameterOption('--no-debug', true)) { 25 | putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); 26 | } 27 | 28 | require dirname(__DIR__).'/config/bootstrap.php'; 29 | 30 | if ($_SERVER['APP_DEBUG']) { 31 | umask(0000); 32 | 33 | if (class_exists(Debug::class)) { 34 | Debug::enable(); 35 | } 36 | } 37 | 38 | $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); 39 | $application = new Application($kernel); 40 | $application->run($input); 41 | -------------------------------------------------------------------------------- /bin/phpunit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | loadEnv($path); 21 | } else { 22 | // fallback code in case your Dotenv component is not 4.2 or higher (when loadEnv() was added) 23 | 24 | if (file_exists($path) || !file_exists($p = "$path.dist")) { 25 | $dotenv->load($path); 26 | } else { 27 | $dotenv->load($p); 28 | } 29 | 30 | if (null === $env = $_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) { 31 | $dotenv->populate(array('APP_ENV' => $env = 'dev')); 32 | } 33 | 34 | if ('test' !== $env && file_exists($p = "$path.local")) { 35 | $dotenv->load($p); 36 | $env = $_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? $env; 37 | } 38 | 39 | if (file_exists($p = "$path.$env")) { 40 | $dotenv->load($p); 41 | } 42 | 43 | if (file_exists($p = "$path.$env.local")) { 44 | $dotenv->load($p); 45 | } 46 | } 47 | } 48 | 49 | $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $_SERVER['APP_ENV'] ?: $_ENV['APP_ENV'] ?: 'dev'; 50 | $_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; 51 | $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; 52 | -------------------------------------------------------------------------------- /config/bundles.php: -------------------------------------------------------------------------------- 1 | ['all' => true], 5 | Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true], 6 | Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], 7 | Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], 8 | Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], 9 | Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], 10 | Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true], 11 | Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], 12 | Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], 13 | Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], 14 | Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true], 15 | Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], 16 | Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['dev' => true], 17 | Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => ['dev' => true, 'test' => true], 18 | Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => ['dev' => true, 'test' => true], 19 | Hautelook\AliceBundle\HautelookAliceBundle::class => ['dev' => true, 'test' => true], 20 | Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], 21 | Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], 22 | ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], 23 | Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true], 24 | ]; 25 | -------------------------------------------------------------------------------- /config/packages/api_platform.yaml: -------------------------------------------------------------------------------- 1 | api_platform: 2 | mapping: 3 | paths: ['%kernel.project_dir%/src/Entity'] 4 | -------------------------------------------------------------------------------- /config/packages/assets.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | assets: 3 | json_manifest_path: '%kernel.project_dir%/public/build/manifest.json' 4 | -------------------------------------------------------------------------------- /config/packages/dev/debug.yaml: -------------------------------------------------------------------------------- 1 | debug: 2 | # Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. 3 | # See the "server:dump" command to start a new server. 4 | dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" 5 | -------------------------------------------------------------------------------- /config/packages/dev/easy_log_handler.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | EasyCorp\EasyLog\EasyLogHandler: 3 | public: false 4 | arguments: ['%kernel.logs_dir%/%kernel.environment%.log'] 5 | 6 | #// FIXME: How to add this configuration automatically without messing up with the monolog configuration? 7 | #monolog: 8 | # handlers: 9 | # buffered: 10 | # type: buffer 11 | # handler: easylog 12 | # channels: ['!event'] 13 | # level: debug 14 | # easylog: 15 | # type: service 16 | # id: EasyCorp\EasyLog\EasyLogHandler 17 | -------------------------------------------------------------------------------- /config/packages/dev/hautelook_alice.yaml: -------------------------------------------------------------------------------- 1 | hautelook_alice: 2 | fixtures_path: fixtures 3 | -------------------------------------------------------------------------------- /config/packages/dev/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: stream 5 | path: "%kernel.logs_dir%/%kernel.environment%.log" 6 | level: debug 7 | channels: ["!event"] 8 | # uncomment to get logging in your browser 9 | # you may have to allow bigger header sizes in your Web server configuration 10 | #firephp: 11 | # type: firephp 12 | # level: info 13 | #chromephp: 14 | # type: chromephp 15 | # level: info 16 | console: 17 | type: console 18 | process_psr_3_messages: false 19 | channels: ["!event", "!doctrine", "!console"] 20 | -------------------------------------------------------------------------------- /config/packages/dev/nelmio_alice.yaml: -------------------------------------------------------------------------------- 1 | nelmio_alice: 2 | functions_blacklist: 3 | - 'current' 4 | -------------------------------------------------------------------------------- /config/packages/dev/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /config/packages/dev/swiftmailer.yaml: -------------------------------------------------------------------------------- 1 | # See https://symfony.com/doc/current/email/dev_environment.html 2 | swiftmailer: 3 | # send all emails to a specific address 4 | #delivery_addresses: ['me@example.com'] 5 | -------------------------------------------------------------------------------- /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 | parameters: 2 | # Adds a fallback DATABASE_URL if the env var is not set. 3 | # This allows you to run cache:warmup even if your 4 | # environment variables are not available yet. 5 | # You should not need to change this value. 6 | env(DATABASE_URL): '' 7 | 8 | doctrine: 9 | dbal: 10 | # configure these for your database server 11 | driver: 'pdo_mysql' 12 | server_version: '5.7' 13 | charset: utf8mb4 14 | default_table_options: 15 | charset: utf8mb4 16 | collate: utf8mb4_unicode_ci 17 | 18 | url: '%env(resolve:DATABASE_URL)%' 19 | orm: 20 | auto_generate_proxy_classes: true 21 | naming_strategy: doctrine.orm.naming_strategy.underscore 22 | auto_mapping: true 23 | mappings: 24 | App: 25 | is_bundle: false 26 | type: annotation 27 | dir: '%kernel.project_dir%/src/Entity' 28 | prefix: 'App\Entity' 29 | alias: App 30 | -------------------------------------------------------------------------------- /config/packages/doctrine_migrations.yaml: -------------------------------------------------------------------------------- 1 | doctrine_migrations: 2 | dir_name: '%kernel.project_dir%/src/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/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | secret: '%env(APP_SECRET)%' 3 | #default_locale: en 4 | #csrf_protection: true 5 | #http_method_override: true 6 | 7 | # Enables session support. Note that the session will ONLY be started if you read or write from it. 8 | # Remove or comment this section to explicitly disable session support. 9 | session: 10 | handler_id: ~ 11 | 12 | #esi: true 13 | #fragments: true 14 | php_errors: 15 | log: true 16 | 17 | cache: 18 | # Put the unique name of your app here: the prefix seed 19 | # is used to compute stable namespaces for cache keys. 20 | #prefix_seed: your_vendor_name/app_name 21 | 22 | # The app cache caches to the filesystem by default. 23 | # Other options include: 24 | 25 | # Redis 26 | #app: cache.adapter.redis 27 | #default_redis_provider: redis://localhost 28 | 29 | # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) 30 | #app: cache.adapter.apcu 31 | -------------------------------------------------------------------------------- /config/packages/mercure.yaml: -------------------------------------------------------------------------------- 1 | mercure: 2 | hubs: 3 | default: 4 | url: '%env(MERCURE_PUBLISH_URL)%' 5 | jwt: '%env(MERCURE_JWT_SECRET)%' 6 | -------------------------------------------------------------------------------- /config/packages/nelmio_cors.yaml: -------------------------------------------------------------------------------- 1 | nelmio_cors: 2 | defaults: 3 | origin_regex: true 4 | allow_origin: ['%env(CORS_ALLOW_ORIGIN)%'] 5 | allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE'] 6 | allow_headers: ['Content-Type', 'Authorization'] 7 | expose_headers: ['Link'] 8 | max_age: 3600 9 | paths: 10 | '^/': ~ 11 | -------------------------------------------------------------------------------- /config/packages/prod/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | orm: 3 | auto_generate_proxy_classes: false 4 | metadata_cache_driver: 5 | type: service 6 | id: doctrine.system_cache_provider 7 | query_cache_driver: 8 | type: service 9 | id: doctrine.system_cache_provider 10 | result_cache_driver: 11 | type: service 12 | id: doctrine.result_cache_provider 13 | 14 | services: 15 | doctrine.result_cache_provider: 16 | class: Symfony\Component\Cache\DoctrineProvider 17 | public: false 18 | arguments: 19 | - '@doctrine.result_cache_pool' 20 | doctrine.system_cache_provider: 21 | class: Symfony\Component\Cache\DoctrineProvider 22 | public: false 23 | arguments: 24 | - '@doctrine.system_cache_pool' 25 | 26 | framework: 27 | cache: 28 | pools: 29 | doctrine.result_cache_pool: 30 | adapter: cache.app 31 | doctrine.system_cache_pool: 32 | adapter: cache.system 33 | -------------------------------------------------------------------------------- /config/packages/prod/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: fingers_crossed 5 | action_level: error 6 | handler: nested 7 | excluded_404s: 8 | # regex: exclude all 404 errors from the logs 9 | - ^/ 10 | nested: 11 | type: stream 12 | path: "%kernel.logs_dir%/%kernel.environment%.log" 13 | level: debug 14 | console: 15 | type: console 16 | process_psr_3_messages: false 17 | channels: ["!event", "!doctrine"] 18 | deprecation: 19 | type: stream 20 | path: "%kernel.logs_dir%/%kernel.environment%.deprecations.log" 21 | deprecation_filter: 22 | type: filter 23 | handler: deprecation 24 | max_level: info 25 | channels: ["php"] 26 | -------------------------------------------------------------------------------- /config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: ~ 4 | -------------------------------------------------------------------------------- /config/packages/security.yaml: -------------------------------------------------------------------------------- 1 | security: 2 | # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers 3 | providers: 4 | in_memory: { memory: ~ } 5 | firewalls: 6 | dev: 7 | pattern: ^/(_(profiler|wdt)|css|images|js)/ 8 | security: false 9 | main: 10 | anonymous: true 11 | 12 | # activate different ways to authenticate 13 | 14 | # http_basic: true 15 | # https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate 16 | 17 | # form_login: true 18 | # https://symfony.com/doc/current/security/form_login_setup.html 19 | 20 | # Easy way to control access for large sections of your site 21 | # Note: Only the *first* access control that matches will be used 22 | access_control: 23 | # - { path: ^/admin, roles: ROLE_ADMIN } 24 | # - { path: ^/profile, roles: ROLE_USER } 25 | -------------------------------------------------------------------------------- /config/packages/sensio_framework_extra.yaml: -------------------------------------------------------------------------------- 1 | sensio_framework_extra: 2 | router: 3 | annotations: false 4 | -------------------------------------------------------------------------------- /config/packages/swiftmailer.yaml: -------------------------------------------------------------------------------- 1 | swiftmailer: 2 | url: '%env(MAILER_URL)%' 3 | spool: { type: 'memory' } 4 | -------------------------------------------------------------------------------- /config/packages/test/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: true 3 | session: 4 | storage_id: session.storage.mock_file 5 | -------------------------------------------------------------------------------- /config/packages/test/hautelook_alice.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: ../dev/hautelook_alice.yaml } 3 | -------------------------------------------------------------------------------- /config/packages/test/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: stream 5 | path: "%kernel.logs_dir%/%kernel.environment%.log" 6 | level: debug 7 | channels: ["!event"] 8 | -------------------------------------------------------------------------------- /config/packages/test/nelmio_alice.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: ../dev/nelmio_alice.yaml } 3 | -------------------------------------------------------------------------------- /config/packages/test/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /config/packages/test/swiftmailer.yaml: -------------------------------------------------------------------------------- 1 | swiftmailer: 2 | disable_delivery: true 3 | -------------------------------------------------------------------------------- /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/translation.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | default_locale: '%locale%' 3 | translator: 4 | default_path: '%kernel.project_dir%/translations' 5 | fallbacks: 6 | - '%locale%' 7 | -------------------------------------------------------------------------------- /config/packages/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | default_path: '%kernel.project_dir%/templates' 3 | debug: '%kernel.debug%' 4 | strict_variables: '%kernel.debug%' 5 | -------------------------------------------------------------------------------- /config/packages/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | email_validation_mode: html5 4 | -------------------------------------------------------------------------------- /config/packages/webpack_encore.yaml: -------------------------------------------------------------------------------- 1 | webpack_encore: 2 | # The path where Encore is building the assets. 3 | # This should match Encore.setOutputPath() in webpack.config.js. 4 | output_path: '%kernel.project_dir%/public/build' 5 | -------------------------------------------------------------------------------- /config/routes.yaml: -------------------------------------------------------------------------------- 1 | #index: 2 | # path: / 3 | # controller: App\Controller\DefaultController::index 4 | -------------------------------------------------------------------------------- /config/routes/annotations.yaml: -------------------------------------------------------------------------------- 1 | controllers: 2 | resource: ../../src/Controller/ 3 | type: annotation 4 | -------------------------------------------------------------------------------- /config/routes/api_platform.yaml: -------------------------------------------------------------------------------- 1 | api_platform: 2 | resource: . 3 | type: api_platform 4 | prefix: /api 5 | -------------------------------------------------------------------------------- /config/routes/dev/twig.yaml: -------------------------------------------------------------------------------- 1 | _errors: 2 | resource: '@TwigBundle/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 | locale: 'en' 8 | env(MERCURE_PUBLISH_URL): '' 9 | env(MERCURE_JWT_SECRET): '' 10 | 11 | services: 12 | # default configuration for services in *this* file 13 | _defaults: 14 | autowire: true # Automatically injects dependencies in your services. 15 | autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. 16 | public: false # Allows optimizing the container by removing unused services; this also means 17 | # fetching services directly from the container via $container->get() won't work. 18 | # The best practice is to be explicit about your dependencies anyway. 19 | 20 | # makes classes in src/ available to be used as services 21 | # this creates a service per class whose id is the fully-qualified class name 22 | App\: 23 | resource: '../src/*' 24 | exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}' 25 | 26 | # controllers are imported separately to make sure services can be injected 27 | # as action arguments even if you don't extend any base controller class 28 | App\Controller\: 29 | resource: '../src/Controller' 30 | tags: ['controller.service_arguments'] 31 | 32 | # add more service definitions when explicit configuration is needed 33 | # please note that last definitions always *replace* previous ones 34 | 35 | App\Serializer\ReactionCollectionNormalizer: 36 | arguments: {$normalizer: '@api_platform.hydra.normalizer.collection'} 37 | tags: 38 | - {name: serializer.normalizer, priority: 100} 39 | -------------------------------------------------------------------------------- /fixtures/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dunglas/symfonycon-lisbon/0b80de543538563175688801808e159d9a3cb40a/fixtures/.gitignore -------------------------------------------------------------------------------- /fixtures/session.yaml: -------------------------------------------------------------------------------- 1 | #var content = ''; 2 | #document.querySelectorAll('.box_rows_2 div.speaker').forEach((elem, i) => { 3 | # content += 'session_'+i+":\n"; 4 | # content += ' title: '+elem.querySelector('.name').innerText+"\n"; 5 | # content += ' author: '+elem.querySelector('p.speaker a').innerText+"\n"; 6 | # content += ' summary: '+elem.querySelector('p:nth-child(4)').innerText+"\n"; 7 | # var date = elem.querySelector('p.speaker small').innerText.split('-')[3].trim().replace(', ', ' ').replace(/\//gi, '-'); 8 | # content += ` datetime: '<(new DateTime("`+date+`"))>'`+"\n"; 9 | #}); 10 | #console.log(content); 11 | 12 | App\Entity\Session: 13 | session_0: 14 | title: Keynote 15 | author: Fabien Potencier 16 | summary: Keynote 17 | datetime: '<(new DateTime("27-04-2019 09:15"))>' 18 | session_1: 19 | title: Comment faire le bon choix entre GraphQL et REST ? Pourquoi pas gRPC ? 20 | author: Barhoumi Ramzi 21 | summary: 'La présentation comportera les sujets suivants:' 22 | datetime: '<(new DateTime("27-04-2019 10:00"))>' 23 | session_2: 24 | title: Le TDD dans la vraie vie avec Panther 25 | author: Adrien Lucas 26 | summary: Panther est un tout nouveau composant Symfony dont l'objectif est de vous simplifier l'écriture des tests d'intégrations et des tests end-to-end ! 27 | datetime: '<(new DateTime("27-04-2019 11:10"))>' 28 | session_3: 29 | title: ERP Gestion de transport 30 | author: ameni kooli 31 | summary: Présentation d'un projet de réalisation d'un ERP pour le compte du client Log-Xpress, spécialiste dans la logistique et transport aérien et maritime sur la base du framework Symfony. 32 | datetime: '<(new DateTime("27-04-2019 11:55"))>' 33 | session_4: 34 | title: 'CAS REEL: Réalisation d''un POC et son Industrialisation avec Symfony' 35 | author: Selim Masrouki 36 | summary: "Mise en place d'une stratégie BIM (Building Information Modeling), ou bien, le traitement de la maquette numérique. Ce processus concerne l’acquisition, la collecte et le traitement de données sous forme de maquette numérique décrivant l’environnement existant d’un projet, sa conception et son exécution." 37 | datetime: '<(new DateTime("27-04-2019 14:00"))>' 38 | session_5: 39 | title: Symfony HttpClient, quoi d'autre? 40 | author: Nicolas Grekas 41 | summary: Avez-vous entendu parler de Symfony HttpClient ? Ce nouveau composant sera publié fin mai avec Symfony 4.3. Que diriez-vous de l'expérimenter en lançant "composer require symfony/http-client" ? C'est ce que je vous propose de faire lors de cette conférence. Nous passerons en revue ses fonctionnalités et ses caractéristiques essentielles. Suffisant pour remplacer Guzzle et consorts ? À n'en pas douter en ce qui me concerne :) 42 | datetime: '<(new DateTime("27-04-2019 14:45"))>' 43 | session_6: 44 | title: Exploiter le composant ExpressionLanguage pour générer des exports à la volée 45 | author: Mehdi Chibouni 46 | summary: Exporter des données est une tâche qui est souvent recurrente. Dans ce talk, nous allons exploiter le composant Symfony ExpressionLanguage pour créer dynamiquement des requêtes Doctrine. Nous allons également profiter de cette occasion pour montrer comment on peut facilement créer un système de plugins avec Symfony 47 | datetime: '<(new DateTime("27-04-2019 15:55"))>' 48 | session_7: 49 | title: Plongée dans l'injection de dépendances de Symfony 50 | author: Titouan Galopin 51 | summary: L'injection de dépendances est probablement l'élément le plus important dans l'architecture d'une application Symfony. En fournissant un moyen souple et efficace de construire des architectures suivant les principes SOLID, ce modèle de conception constitue un environnement idéal pour vos projets. 52 | datetime: '<(new DateTime("27-04-2019 16:40"))>' 53 | session_8: 54 | title: Des apps Symfony sous stéroïdes ! 55 | author: Kévin Dunglas 56 | summary: Grâce aux nouvelles capacités de la plateforme web (web components, progressive web apps...) et à la montée en puissance des bibliothèques et frameworks JS tels que Vue, React et Angular, quasiment toutes les nouvelles apps Symfony doivent aussi tirer partie de l'écosystème frontend. Symfony 4 contient de nombreux outils rendant très facile d'intégrer les outils JS moderne dans le framework. 57 | datetime: '<(new DateTime("27-04-2019 17:05"))>' 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@fortawesome/fontawesome-free": "^5.5.0", 4 | "@symfony/webpack-encore": "^0.22.0", 5 | "node-sass": "^4.10.0", 6 | "sass-loader": "^7.0.1", 7 | "vue": "^2.5.17", 8 | "vue-loader": "^15.0.11", 9 | "vue-template-compiler": "^2.5.17", 10 | "webpack-notifier": "^1.6.0" 11 | }, 12 | "license": "UNLICENSED", 13 | "private": true, 14 | "scripts": { 15 | "dev-server": "encore dev-server", 16 | "dev": "encore dev", 17 | "watch": "encore dev --watch", 18 | "build": "encore production --progress" 19 | }, 20 | "dependencies": { 21 | "bulma": "^0.7.2", 22 | "vue-star-rating": "^1.6.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /php.ini: -------------------------------------------------------------------------------- 1 | allow_url_include=off 2 | assert.active=off 3 | display_errors=off 4 | display_startup_errors=off 5 | max_execution_time=30 6 | session.use_strict_mode=On 7 | realpath_cache_ttl=3600 8 | zend.detect_unicode=Off 9 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | tests 19 | 20 | 21 | 22 | 23 | 24 | src 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /public/assets/logotilleuls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dunglas/symfonycon-lisbon/0b80de543538563175688801808e159d9a3cb40a/public/assets/logotilleuls.png -------------------------------------------------------------------------------- /public/assets/symfonycon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dunglas/symfonycon-lisbon/0b80de543538563175688801808e159d9a3cb40a/public/assets/symfonycon.jpg -------------------------------------------------------------------------------- /public/assets/symfonycon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dunglas/symfonycon-lisbon/0b80de543538563175688801808e159d9a3cb40a/public/assets/symfonycon.png -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | handle($request); 26 | $response->send(); 27 | $kernel->terminate($request, $response); 28 | -------------------------------------------------------------------------------- /src/Controller/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dunglas/symfonycon-lisbon/0b80de543538563175688801808e159d9a3cb40a/src/Controller/.gitignore -------------------------------------------------------------------------------- /src/Controller/SessionController.php: -------------------------------------------------------------------------------- 1 | getDoctrine()->getRepository(Session::class); 17 | 18 | return $this->render('session/index.html.twig', [ 19 | 'sessions' => $repository->findBy([], ['datetime' => 'ASC']) 20 | ]); 21 | } 22 | 23 | /** 24 | * @Route("/session/{id}", name="session_detail") 25 | */ 26 | public function detail(Session $session) 27 | { 28 | return $this->render('session/detail.html.twig', ['session' => $session]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Entity/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dunglas/symfonycon-lisbon/0b80de543538563175688801808e159d9a3cb40a/src/Entity/.gitignore -------------------------------------------------------------------------------- /src/Entity/Feedback.php: -------------------------------------------------------------------------------- 1 | id; 47 | } 48 | 49 | public function getSession(): ?Session 50 | { 51 | return $this->session; 52 | } 53 | 54 | public function setSession(?Session $session): self 55 | { 56 | $this->session = $session; 57 | 58 | return $this; 59 | } 60 | 61 | public function getAuthor(): ?string 62 | { 63 | return $this->author; 64 | } 65 | 66 | public function setAuthor(string $author): self 67 | { 68 | $this->author = $author; 69 | 70 | return $this; 71 | } 72 | 73 | public function getRating(): ?int 74 | { 75 | return $this->rating; 76 | } 77 | 78 | public function setRating(int $rating): self 79 | { 80 | $this->rating = $rating; 81 | 82 | return $this; 83 | } 84 | 85 | public function getComment(): ?string 86 | { 87 | return $this->comment; 88 | } 89 | 90 | public function setComment(string $comment): self 91 | { 92 | $this->comment = $comment; 93 | 94 | return $this; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Entity/Reaction.php: -------------------------------------------------------------------------------- 1 | createdAt = new \DateTime(); 48 | } 49 | 50 | public function getId(): ?int 51 | { 52 | return $this->id; 53 | } 54 | 55 | public function getSession(): ?Session 56 | { 57 | return $this->session; 58 | } 59 | 60 | public function setSession(?Session $session): self 61 | { 62 | $this->session = $session; 63 | 64 | return $this; 65 | } 66 | 67 | public function getType(): ?string 68 | { 69 | return $this->type; 70 | } 71 | 72 | public function setType(string $type): self 73 | { 74 | $this->type = $type; 75 | 76 | return $this; 77 | } 78 | 79 | public function getCreatedAt(): ?\DateTimeInterface 80 | { 81 | return $this->createdAt; 82 | } 83 | 84 | public function setCreatedAt(\DateTimeInterface $createdAt): self 85 | { 86 | $this->createdAt = $createdAt; 87 | 88 | return $this; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Entity/Session.php: -------------------------------------------------------------------------------- 1 | feedback = new ArrayCollection(); 62 | $this->reactions = new ArrayCollection(); 63 | } 64 | 65 | public function getId(): ?int 66 | { 67 | return $this->id; 68 | } 69 | 70 | public function getTitle(): ?string 71 | { 72 | return $this->title; 73 | } 74 | 75 | public function setTitle(string $title): self 76 | { 77 | $this->title = $title; 78 | 79 | return $this; 80 | } 81 | 82 | public function getSummary(): ?string 83 | { 84 | return $this->summary; 85 | } 86 | 87 | public function setSummary(string $summary): self 88 | { 89 | $this->summary = $summary; 90 | 91 | return $this; 92 | } 93 | 94 | public function getAuthor(): ?string 95 | { 96 | return $this->author; 97 | } 98 | 99 | public function setAuthor(string $author): self 100 | { 101 | $this->author = $author; 102 | 103 | return $this; 104 | } 105 | 106 | public function getDatetime(): ?\DateTimeInterface 107 | { 108 | return $this->datetime; 109 | } 110 | 111 | public function setDatetime(\DateTimeInterface $datetime): self 112 | { 113 | $this->datetime = $datetime; 114 | 115 | return $this; 116 | } 117 | 118 | /** 119 | * @return Collection|Feedback[] 120 | */ 121 | public function getFeedback(): Collection 122 | { 123 | return $this->feedback; 124 | } 125 | 126 | public function addFeedback(Feedback $feedback): self 127 | { 128 | if (!$this->feedback->contains($feedback)) { 129 | $this->feedback[] = $feedback; 130 | $feedback->setSession($this); 131 | } 132 | 133 | return $this; 134 | } 135 | 136 | public function removeFeedback(Feedback $feedback): self 137 | { 138 | if ($this->feedback->contains($feedback)) { 139 | $this->feedback->removeElement($feedback); 140 | // set the owning side to null (unless already changed) 141 | if ($feedback->getSession() === $this) { 142 | $feedback->setSession(null); 143 | } 144 | } 145 | 146 | return $this; 147 | } 148 | 149 | /** 150 | * @return Collection|Reaction[] 151 | */ 152 | public function getReactions(): Collection 153 | { 154 | return $this->reactions; 155 | } 156 | 157 | public function addReaction(Reaction $reaction): self 158 | { 159 | if (!$this->reactions->contains($reaction)) { 160 | $this->reactions[] = $reaction; 161 | $reaction->setSession($this); 162 | } 163 | 164 | return $this; 165 | } 166 | 167 | public function removeReaction(Reaction $reaction): self 168 | { 169 | if ($this->reactions->contains($reaction)) { 170 | $this->reactions->removeElement($reaction); 171 | // set the owning side to null (unless already changed) 172 | if ($reaction->getSession() === $this) { 173 | $reaction->setSession(null); 174 | } 175 | } 176 | 177 | return $this; 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/Kernel.php: -------------------------------------------------------------------------------- 1 | getProjectDir().'/var/cache/'.$this->environment; 21 | } 22 | 23 | public function getLogDir() 24 | { 25 | return $this->getProjectDir().'/var/log'; 26 | } 27 | 28 | public function registerBundles() 29 | { 30 | $contents = require $this->getProjectDir().'/config/bundles.php'; 31 | foreach ($contents as $class => $envs) { 32 | if ($envs[$this->environment] ?? $envs['all'] ?? false) { 33 | yield new $class(); 34 | } 35 | } 36 | } 37 | 38 | protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader) 39 | { 40 | $container->addResource(new FileResource($this->getProjectDir().'/config/bundles.php')); 41 | // Feel free to remove the "container.autowiring.strict_mode" parameter 42 | // if you are using symfony/dependency-injection 4.0+ as it's the default behavior 43 | $container->setParameter('container.autowiring.strict_mode', true); 44 | $container->setParameter('container.dumper.inline_class_loader', true); 45 | $confDir = $this->getProjectDir().'/config'; 46 | 47 | $loader->load($confDir.'/{packages}/*'.self::CONFIG_EXTS, 'glob'); 48 | $loader->load($confDir.'/{packages}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob'); 49 | $loader->load($confDir.'/{services}'.self::CONFIG_EXTS, 'glob'); 50 | $loader->load($confDir.'/{services}_'.$this->environment.self::CONFIG_EXTS, 'glob'); 51 | } 52 | 53 | protected function configureRoutes(RouteCollectionBuilder $routes) 54 | { 55 | $confDir = $this->getProjectDir().'/config'; 56 | 57 | $routes->import($confDir.'/{routes}/*'.self::CONFIG_EXTS, '/', 'glob'); 58 | $routes->import($confDir.'/{routes}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, '/', 'glob'); 59 | $routes->import($confDir.'/{routes}'.self::CONFIG_EXTS, '/', 'glob'); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Migrations/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dunglas/symfonycon-lisbon/0b80de543538563175688801808e159d9a3cb40a/src/Migrations/.gitignore -------------------------------------------------------------------------------- /src/Migrations/Version20181129151241.php: -------------------------------------------------------------------------------- 1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); 17 | 18 | $this->addSql('CREATE TABLE session (id INT AUTO_INCREMENT NOT NULL, title VARCHAR(255) NOT NULL, summary LONGTEXT NOT NULL, author VARCHAR(255) NOT NULL, datetime DATETIME NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB'); 19 | } 20 | 21 | public function down(Schema $schema) : void 22 | { 23 | // this down() migration is auto-generated, please modify it to your needs 24 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); 25 | 26 | $this->addSql('DROP TABLE session'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Migrations/Version20181129215541.php: -------------------------------------------------------------------------------- 1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); 17 | 18 | $this->addSql('CREATE TABLE feedback (id INT AUTO_INCREMENT NOT NULL, session_id INT DEFAULT NULL, author VARCHAR(255) NOT NULL, rating INT NOT NULL, comment LONGTEXT NOT NULL, INDEX IDX_D2294458613FECDF (session_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB'); 19 | $this->addSql('ALTER TABLE feedback ADD CONSTRAINT FK_D2294458613FECDF FOREIGN KEY (session_id) REFERENCES session (id)'); 20 | } 21 | 22 | public function down(Schema $schema) : void 23 | { 24 | // this down() migration is auto-generated, please modify it to your needs 25 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); 26 | 27 | $this->addSql('DROP TABLE feedback'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Migrations/Version20181130211250.php: -------------------------------------------------------------------------------- 1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); 17 | 18 | $this->addSql('CREATE TABLE reaction (id INT AUTO_INCREMENT NOT NULL, session_id INT NOT NULL, type VARCHAR(1) NOT NULL, created_at DATETIME NOT NULL, INDEX IDX_A4D707F7613FECDF (session_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB'); 19 | $this->addSql('ALTER TABLE reaction ADD CONSTRAINT FK_A4D707F7613FECDF FOREIGN KEY (session_id) REFERENCES session (id)'); 20 | } 21 | 22 | public function down(Schema $schema) : void 23 | { 24 | // this down() migration is auto-generated, please modify it to your needs 25 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); 26 | 27 | $this->addSql('DROP TABLE reaction'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Migrations/Version20181130215702.php: -------------------------------------------------------------------------------- 1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); 17 | 18 | $this->addSql('ALTER TABLE reaction CHANGE type type VARCHAR(2) NOT NULL'); 19 | } 20 | 21 | public function down(Schema $schema) : void 22 | { 23 | // this down() migration is auto-generated, please modify it to your needs 24 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); 25 | 26 | $this->addSql('ALTER TABLE reaction CHANGE type type VARCHAR(1) NOT NULL COLLATE utf8mb4_unicode_ci'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Migrations/Version20181130221307.php: -------------------------------------------------------------------------------- 1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); 17 | 18 | $this->addSql('ALTER TABLE reaction CHANGE type type VARCHAR(10) NOT NULL'); 19 | } 20 | 21 | public function down(Schema $schema) : void 22 | { 23 | // this down() migration is auto-generated, please modify it to your needs 24 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); 25 | 26 | $this->addSql('ALTER TABLE reaction CHANGE type type VARCHAR(2) NOT NULL COLLATE utf8mb4_unicode_ci'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Repository/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dunglas/symfonycon-lisbon/0b80de543538563175688801808e159d9a3cb40a/src/Repository/.gitignore -------------------------------------------------------------------------------- /src/Repository/FeedbackRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('f') 29 | ->andWhere('f.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('f.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?Feedback 41 | { 42 | return $this->createQueryBuilder('f') 43 | ->andWhere('f.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /src/Repository/ReactionRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('r') 27 | ->select('r.type, COUNT(r.id) as nb') 28 | ->where('r.session = :session_id') 29 | ->setParameter('session_id', $sessionId) 30 | ->groupBy('r.type') 31 | ->getQuery() 32 | ->getArrayResult(); 33 | 34 | $data = []; 35 | foreach ($rows as $row) { 36 | $data[$row['type']] = (int) $row['nb']; 37 | } 38 | 39 | return $data; 40 | } 41 | 42 | // /** 43 | // * @return Reaction[] Returns an array of Reaction objects 44 | // */ 45 | /* 46 | public function findByExampleField($value) 47 | { 48 | return $this->createQueryBuilder('r') 49 | ->andWhere('r.exampleField = :val') 50 | ->setParameter('val', $value) 51 | ->orderBy('r.id', 'ASC') 52 | ->setMaxResults(10) 53 | ->getQuery() 54 | ->getResult() 55 | ; 56 | } 57 | */ 58 | 59 | /* 60 | public function findOneBySomeField($value): ?Reaction 61 | { 62 | return $this->createQueryBuilder('r') 63 | ->andWhere('r.exampleField = :val') 64 | ->setParameter('val', $value) 65 | ->getQuery() 66 | ->getOneOrNullResult() 67 | ; 68 | } 69 | */ 70 | } 71 | -------------------------------------------------------------------------------- /src/Repository/SessionRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('s') 29 | ->andWhere('s.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('s.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?Session 41 | { 42 | return $this->createQueryBuilder('s') 43 | ->andWhere('s.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /src/Serializer/ReactionCollectionNormalizer.php: -------------------------------------------------------------------------------- 1 | normalizer = $normalizer; 17 | $this->reactionRepository = $reactionRepository; 18 | } 19 | 20 | public function normalize($object, $format = null, array $context = []) 21 | { 22 | $data = $this->normalizer->normalize($object, $format, $context); 23 | $data += $this->reactionRepository->countReactions($context['subresource_identifiers']['id']); 24 | unset($data['hydra:member']); 25 | 26 | return $data; 27 | } 28 | 29 | public function supportsNormalization($data, $format = null, array $context = []): bool 30 | { 31 | return 32 | 'api_sessions_reactions_get_subresource' === ($context['subresource_operation_name'] ?? null) 33 | && false === ($context['api_sub_level'] ?? false) 34 | && $this->normalizer->supportsNormalization($data, $format, $context); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /symfony.lock: -------------------------------------------------------------------------------- 1 | { 2 | "api-platform/api-pack": { 3 | "version": "1.1.0" 4 | }, 5 | "api-platform/core": { 6 | "version": "2.1", 7 | "recipe": { 8 | "repo": "github.com/symfony/recipes", 9 | "branch": "master", 10 | "version": "2.1", 11 | "ref": "18727d8f229306860b46955f438e1897421da689" 12 | } 13 | }, 14 | "doctrine/annotations": { 15 | "version": "1.0", 16 | "recipe": { 17 | "repo": "github.com/symfony/recipes", 18 | "branch": "master", 19 | "version": "1.0", 20 | "ref": "cb4152ebcadbe620ea2261da1a1c5a9b8cea7672" 21 | } 22 | }, 23 | "doctrine/cache": { 24 | "version": "v1.8.0" 25 | }, 26 | "doctrine/collections": { 27 | "version": "v1.5.0" 28 | }, 29 | "doctrine/common": { 30 | "version": "v2.10.0" 31 | }, 32 | "doctrine/data-fixtures": { 33 | "version": "v1.3.1" 34 | }, 35 | "doctrine/dbal": { 36 | "version": "v2.8.0" 37 | }, 38 | "doctrine/doctrine-bundle": { 39 | "version": "1.6", 40 | "recipe": { 41 | "repo": "github.com/symfony/recipes", 42 | "branch": "master", 43 | "version": "1.6", 44 | "ref": "453e89b78ded666f351617baca5ae40d20622351" 45 | } 46 | }, 47 | "doctrine/doctrine-cache-bundle": { 48 | "version": "1.3.5" 49 | }, 50 | "doctrine/doctrine-migrations-bundle": { 51 | "version": "1.2", 52 | "recipe": { 53 | "repo": "github.com/symfony/recipes", 54 | "branch": "master", 55 | "version": "1.2", 56 | "ref": "c1431086fec31f17fbcfe6d6d7e92059458facc1" 57 | } 58 | }, 59 | "doctrine/event-manager": { 60 | "version": "v1.0.0" 61 | }, 62 | "doctrine/inflector": { 63 | "version": "v1.3.0" 64 | }, 65 | "doctrine/instantiator": { 66 | "version": "1.1.0" 67 | }, 68 | "doctrine/lexer": { 69 | "version": "v1.0.1" 70 | }, 71 | "doctrine/migrations": { 72 | "version": "v1.8.1" 73 | }, 74 | "doctrine/orm": { 75 | "version": "v2.6.3" 76 | }, 77 | "doctrine/persistence": { 78 | "version": "v1.1.0" 79 | }, 80 | "doctrine/reflection": { 81 | "version": "v1.0.0" 82 | }, 83 | "easycorp/easy-log-handler": { 84 | "version": "1.0", 85 | "recipe": { 86 | "repo": "github.com/symfony/recipes", 87 | "branch": "master", 88 | "version": "1.0", 89 | "ref": "70062abc2cd58794d2a90274502f81b55cd9951b" 90 | } 91 | }, 92 | "egulias/email-validator": { 93 | "version": "2.1.6" 94 | }, 95 | "facebook/webdriver": { 96 | "version": "1.6.0" 97 | }, 98 | "fig/link-util": { 99 | "version": "1.0.0" 100 | }, 101 | "fzaninotto/faker": { 102 | "version": "v1.8.0" 103 | }, 104 | "hautelook/alice-bundle": { 105 | "version": "2.1", 106 | "recipe": { 107 | "repo": "github.com/symfony/recipes", 108 | "branch": "master", 109 | "version": "2.1", 110 | "ref": "71822522faf7ed2792d86b7f94ce73443358ccb9" 111 | } 112 | }, 113 | "jdorn/sql-formatter": { 114 | "version": "v1.2.17" 115 | }, 116 | "monolog/monolog": { 117 | "version": "1.24.0" 118 | }, 119 | "myclabs/deep-copy": { 120 | "version": "1.8.1" 121 | }, 122 | "nelmio/alice": { 123 | "version": "3.2", 124 | "recipe": { 125 | "repo": "github.com/symfony/recipes", 126 | "branch": "master", 127 | "version": "3.2", 128 | "ref": "aaf440eb4d289ead8e2ef986c4f25c97fffec4ac" 129 | } 130 | }, 131 | "nelmio/cors-bundle": { 132 | "version": "1.5", 133 | "recipe": { 134 | "repo": "github.com/symfony/recipes", 135 | "branch": "master", 136 | "version": "1.5", 137 | "ref": "f0436fc35fca88eada758311f8de43bfb61f1980" 138 | } 139 | }, 140 | "nikic/php-parser": { 141 | "version": "v4.1.0" 142 | }, 143 | "ocramius/package-versions": { 144 | "version": "1.3.0" 145 | }, 146 | "ocramius/proxy-manager": { 147 | "version": "2.1.1" 148 | }, 149 | "phpdocumentor/reflection-common": { 150 | "version": "1.0.1" 151 | }, 152 | "phpdocumentor/reflection-docblock": { 153 | "version": "4.3.0" 154 | }, 155 | "phpdocumentor/type-resolver": { 156 | "version": "0.4.0" 157 | }, 158 | "psr/cache": { 159 | "version": "1.0.1" 160 | }, 161 | "psr/container": { 162 | "version": "1.0.0" 163 | }, 164 | "psr/link": { 165 | "version": "1.0.0" 166 | }, 167 | "psr/log": { 168 | "version": "1.1.0" 169 | }, 170 | "psr/simple-cache": { 171 | "version": "1.0.1" 172 | }, 173 | "sebastian/comparator": { 174 | "version": "3.0.2" 175 | }, 176 | "sebastian/diff": { 177 | "version": "3.0.1" 178 | }, 179 | "sebastian/exporter": { 180 | "version": "3.1.0" 181 | }, 182 | "sebastian/recursion-context": { 183 | "version": "3.0.0" 184 | }, 185 | "sensio/framework-extra-bundle": { 186 | "version": "5.2", 187 | "recipe": { 188 | "repo": "github.com/symfony/recipes", 189 | "branch": "master", 190 | "version": "5.2", 191 | "ref": "fb7e19da7f013d0d422fa9bce16f5c510e27609b" 192 | } 193 | }, 194 | "swiftmailer/swiftmailer": { 195 | "version": "v6.1.3" 196 | }, 197 | "symfony/asset": { 198 | "version": "v4.1.8" 199 | }, 200 | "symfony/browser-kit": { 201 | "version": "v4.1.8" 202 | }, 203 | "symfony/cache": { 204 | "version": "v4.1.8" 205 | }, 206 | "symfony/config": { 207 | "version": "v4.1.8" 208 | }, 209 | "symfony/console": { 210 | "version": "3.3", 211 | "recipe": { 212 | "repo": "github.com/symfony/recipes", 213 | "branch": "master", 214 | "version": "3.3", 215 | "ref": "f3a28efb32f20b8740fd763651cabd780bce43da" 216 | } 217 | }, 218 | "symfony/contracts": { 219 | "version": "v1.0.0" 220 | }, 221 | "symfony/css-selector": { 222 | "version": "v4.1.8" 223 | }, 224 | "symfony/debug": { 225 | "version": "v4.1.8" 226 | }, 227 | "symfony/debug-bundle": { 228 | "version": "4.1", 229 | "recipe": { 230 | "repo": "github.com/symfony/recipes", 231 | "branch": "master", 232 | "version": "4.1", 233 | "ref": "f8863cbad2f2e58c4b65fa1eac892ab189971bea" 234 | } 235 | }, 236 | "symfony/debug-pack": { 237 | "version": "v1.0.6" 238 | }, 239 | "symfony/dependency-injection": { 240 | "version": "v4.1.8" 241 | }, 242 | "symfony/doctrine-bridge": { 243 | "version": "v4.1.8" 244 | }, 245 | "symfony/dom-crawler": { 246 | "version": "v4.1.8" 247 | }, 248 | "symfony/dotenv": { 249 | "version": "v4.1.8" 250 | }, 251 | "symfony/event-dispatcher": { 252 | "version": "v4.1.8" 253 | }, 254 | "symfony/expression-language": { 255 | "version": "v4.1.8" 256 | }, 257 | "symfony/filesystem": { 258 | "version": "v4.1.8" 259 | }, 260 | "symfony/finder": { 261 | "version": "v4.1.8" 262 | }, 263 | "symfony/flex": { 264 | "version": "1.0", 265 | "recipe": { 266 | "repo": "github.com/symfony/recipes", 267 | "branch": "master", 268 | "version": "1.0", 269 | "ref": "5f8a51c0fad684396f6b6c0fd770e043439cb632" 270 | } 271 | }, 272 | "symfony/form": { 273 | "version": "v4.1.8" 274 | }, 275 | "symfony/framework-bundle": { 276 | "version": "3.3", 277 | "recipe": { 278 | "repo": "github.com/symfony/recipes", 279 | "branch": "master", 280 | "version": "3.3", 281 | "ref": "fa24f6388ea987b615a2704dc2c9f3a358c8da11" 282 | } 283 | }, 284 | "symfony/http-foundation": { 285 | "version": "v4.1.8" 286 | }, 287 | "symfony/http-kernel": { 288 | "version": "v4.1.8" 289 | }, 290 | "symfony/inflector": { 291 | "version": "v4.1.8" 292 | }, 293 | "symfony/intl": { 294 | "version": "v4.1.8" 295 | }, 296 | "symfony/maker-bundle": { 297 | "version": "1.0", 298 | "recipe": { 299 | "repo": "github.com/symfony/recipes", 300 | "branch": "master", 301 | "version": "1.0", 302 | "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f" 303 | } 304 | }, 305 | "symfony/mercure": { 306 | "version": "v0.1.0" 307 | }, 308 | "symfony/mercure-bundle": { 309 | "version": "0.1", 310 | "recipe": { 311 | "repo": "github.com/symfony/recipes", 312 | "branch": "master", 313 | "version": "0.1", 314 | "ref": "c78ab1f56e700004fc5cd675c26b3c1c26be281d" 315 | } 316 | }, 317 | "symfony/monolog-bridge": { 318 | "version": "v4.1.8" 319 | }, 320 | "symfony/monolog-bundle": { 321 | "version": "3.1", 322 | "recipe": { 323 | "repo": "github.com/symfony/recipes", 324 | "branch": "master", 325 | "version": "3.1", 326 | "ref": "18ebf5a940573a20de06f9c4060101eeb438cf3d" 327 | } 328 | }, 329 | "symfony/options-resolver": { 330 | "version": "v4.1.8" 331 | }, 332 | "symfony/orm-pack": { 333 | "version": "v1.0.5" 334 | }, 335 | "symfony/panther": { 336 | "version": "v0.2.0" 337 | }, 338 | "symfony/phpunit-bridge": { 339 | "version": "4.1", 340 | "recipe": { 341 | "repo": "github.com/symfony/recipes", 342 | "branch": "master", 343 | "version": "4.1", 344 | "ref": "0e548dd90adba18fabd4ef419b14d361fe4d6c74" 345 | } 346 | }, 347 | "symfony/polyfill-intl-icu": { 348 | "version": "v1.10.0" 349 | }, 350 | "symfony/polyfill-intl-idn": { 351 | "version": "v1.11.0" 352 | }, 353 | "symfony/polyfill-mbstring": { 354 | "version": "v1.10.0" 355 | }, 356 | "symfony/polyfill-php72": { 357 | "version": "v1.10.0" 358 | }, 359 | "symfony/process": { 360 | "version": "v4.1.8" 361 | }, 362 | "symfony/profiler-pack": { 363 | "version": "v1.0.3" 364 | }, 365 | "symfony/property-access": { 366 | "version": "v4.1.8" 367 | }, 368 | "symfony/property-info": { 369 | "version": "v4.1.8" 370 | }, 371 | "symfony/routing": { 372 | "version": "4.0", 373 | "recipe": { 374 | "repo": "github.com/symfony/recipes", 375 | "branch": "master", 376 | "version": "4.0", 377 | "ref": "5f514d9d3b8a8aac3d62ae6a86b18b90ed0c7826" 378 | } 379 | }, 380 | "symfony/security-bundle": { 381 | "version": "3.3", 382 | "recipe": { 383 | "repo": "github.com/symfony/recipes", 384 | "branch": "master", 385 | "version": "3.3", 386 | "ref": "f8a63faa0d9521526499c0a8f403c9964ecb0527" 387 | } 388 | }, 389 | "symfony/security-core": { 390 | "version": "v4.2.0" 391 | }, 392 | "symfony/security-csrf": { 393 | "version": "v4.2.0" 394 | }, 395 | "symfony/security-guard": { 396 | "version": "v4.2.0" 397 | }, 398 | "symfony/security-http": { 399 | "version": "v4.2.0" 400 | }, 401 | "symfony/serializer": { 402 | "version": "v4.1.8" 403 | }, 404 | "symfony/serializer-pack": { 405 | "version": "v1.0.1" 406 | }, 407 | "symfony/stopwatch": { 408 | "version": "v4.1.8" 409 | }, 410 | "symfony/swiftmailer-bundle": { 411 | "version": "2.5", 412 | "recipe": { 413 | "repo": "github.com/symfony/recipes", 414 | "branch": "master", 415 | "version": "2.5", 416 | "ref": "3db029c03e452b4a23f7fc45cec7c922c2247eb8" 417 | } 418 | }, 419 | "symfony/test-pack": { 420 | "version": "v1.0.4" 421 | }, 422 | "symfony/translation": { 423 | "version": "3.3", 424 | "recipe": { 425 | "repo": "github.com/symfony/recipes", 426 | "branch": "master", 427 | "version": "3.3", 428 | "ref": "1fb02a6e1c8f3d4232cce485c9afa868d63b115a" 429 | } 430 | }, 431 | "symfony/twig-bridge": { 432 | "version": "v4.1.8" 433 | }, 434 | "symfony/twig-bundle": { 435 | "version": "3.3", 436 | "recipe": { 437 | "repo": "github.com/symfony/recipes", 438 | "branch": "master", 439 | "version": "3.3", 440 | "ref": "369b5b29dc52b2c190002825ae7ec24ab6f962dd" 441 | } 442 | }, 443 | "symfony/validator": { 444 | "version": "4.1", 445 | "recipe": { 446 | "repo": "github.com/symfony/recipes", 447 | "branch": "master", 448 | "version": "4.1", 449 | "ref": "0cdc982334f45d554957a6167e030482795bf9d7" 450 | } 451 | }, 452 | "symfony/var-dumper": { 453 | "version": "v4.1.8" 454 | }, 455 | "symfony/var-exporter": { 456 | "version": "v4.2.0" 457 | }, 458 | "symfony/web-link": { 459 | "version": "v4.1.8" 460 | }, 461 | "symfony/web-profiler-bundle": { 462 | "version": "3.3", 463 | "recipe": { 464 | "repo": "github.com/symfony/recipes", 465 | "branch": "master", 466 | "version": "3.3", 467 | "ref": "6bdfa1a95f6b2e677ab985cd1af2eae35d62e0f6" 468 | } 469 | }, 470 | "symfony/web-server-bundle": { 471 | "version": "3.3", 472 | "recipe": { 473 | "repo": "github.com/symfony/recipes", 474 | "branch": "master", 475 | "version": "3.3", 476 | "ref": "dae9b39fd6717970be7601101ce5aa960bf53d9a" 477 | } 478 | }, 479 | "symfony/webpack-encore-bundle": { 480 | "version": "1.0", 481 | "recipe": { 482 | "repo": "github.com/symfony/recipes", 483 | "branch": "master", 484 | "version": "1.0", 485 | "ref": "dd76e869a5d10fac29f4c0c02f8feaac613471ff" 486 | } 487 | }, 488 | "symfony/yaml": { 489 | "version": "v4.1.8" 490 | }, 491 | "theofidry/alice-data-fixtures": { 492 | "version": "1.0", 493 | "recipe": { 494 | "repo": "github.com/symfony/recipes", 495 | "branch": "master", 496 | "version": "1.0", 497 | "ref": "fe5a50faf580eb58f08ada2abe8afbd2d4941e05" 498 | } 499 | }, 500 | "twig/twig": { 501 | "version": "v2.5.0" 502 | }, 503 | "webmozart/assert": { 504 | "version": "1.3.0" 505 | }, 506 | "willdurand/negotiation": { 507 | "version": "v2.3.1" 508 | }, 509 | "zendframework/zend-code": { 510 | "version": "3.3.1" 511 | }, 512 | "zendframework/zend-eventmanager": { 513 | "version": "3.2.1" 514 | } 515 | } 516 | -------------------------------------------------------------------------------- /templates/base.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {% block title %}Welcome!{% endblock %} 7 | {{ encore_entry_link_tags('app') }} 8 | {% block stylesheets %} 9 | {% endblock %} 10 | 11 | 12 |
13 | 14 | 15 | 16 |
17 |
18 | {% block body %}{% endblock %} 19 |
20 | 26 | {{ encore_entry_script_tags('app') }} 27 | {% block javascripts %}{% endblock %} 28 | 29 | 30 | -------------------------------------------------------------------------------- /templates/session/detail.html.twig: -------------------------------------------------------------------------------- 1 | {# templates/session/detail.html.twig #} 2 | {% extends 'base.html.twig' %} 3 | 4 | {% block title %}{{ session.title }} | SymfonyCon{% endblock %} 5 | 6 | {% block body %} 7 | {% include 'session/title.html.twig' with {title: 'Detail'} %} 8 |
9 |
10 | 11 | 12 | 13 | 14 | Back to the list 15 | 16 |
17 |
18 |
19 |
20 |
21 |

{{ session.datetime|date('l') }}

22 |

{{ session.datetime|date('H:i') }}

23 |

{{ session.datetime|date('d M') }}

24 |
25 |
26 |
27 |
{{ session.author }}
28 |

{{ session.title }}

29 |

{{ session.summary }}

30 |
31 |
32 |
33 |
34 | 35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 | {% endblock %} 43 | 44 | {% block javascripts %} 45 | {{ encore_entry_script_tags('js/feedback') }} 46 | {{ encore_entry_script_tags('js/reactions') }} 47 | {% endblock %} 48 | -------------------------------------------------------------------------------- /templates/session/index.html.twig: -------------------------------------------------------------------------------- 1 | {# templates/session/index.html.twig #} 2 | {% extends 'base.html.twig' %} 3 | 4 | {% block title %}SymfonyCon{% endblock %} 5 | 6 | {% block body %} 7 | {% include 'session/title.html.twig' with {title: 'Schedule'} %} 8 |
9 |
10 | {% for session in sessions %} 11 |
12 | {% include 'session/session.html.twig' with {session: session} %} 13 |
14 | {% endfor %} 15 |
16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /templates/session/session.html.twig: -------------------------------------------------------------------------------- 1 | {# templates/session/session.html.twig #} 2 |
3 |
4 |
5 |

{{ session.datetime|date('l') }}

6 |

{{ session.datetime|date('H:i') }}

7 |

{{ session.datetime|date('d M') }}

8 |
9 |
10 |
11 |

{{ session.author }}

12 |

{{ session.title }}

13 |
14 |
{{ session.summary }}
15 |
16 | -------------------------------------------------------------------------------- /templates/session/title.html.twig: -------------------------------------------------------------------------------- 1 | {# templates/session/title.html.twig #} 2 |
3 |

{{ title }}

4 |
5 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dunglas/symfonycon-lisbon/0b80de543538563175688801808e159d9a3cb40a/tests/.gitignore -------------------------------------------------------------------------------- /tests/SessionTest.php: -------------------------------------------------------------------------------- 1 | request('GET', '/'); 18 | $this->assertSame('Schedule', $crawler->filter('h1:first-of-type')->text()); 19 | 20 | $crawler = $client->clickLink(self::SESSION_TITLE); 21 | $this->assertSame(self::SESSION_TITLE, $crawler->filter('article h1:first-of-type')->text()); 22 | 23 | $client->waitFor('#no-comments'); 24 | $this->assertSame('No feedback yet !', $crawler->filter('#no-comments')->text()); 25 | 26 | $client->getMouse()->clickTo('.vue-star-rating-pointer:nth-of-type(5)'); 27 | $crawler = $client->submitForm('Post', [ 28 | 'author' => 'Kévin', 29 | 'comment' => 'I hope you\'re enjoying the talk!', 30 | ]); 31 | 32 | $client->waitFor('#comments article p'); 33 | $this->assertContains('I hope you\'re enjoying the talk!', $crawler->filter('#comments article p')->text()); 34 | $this->assertEmpty($crawler->filter('#no-comments')); 35 | 36 | $client->takeScreenshot('/tmp/panther.png'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /translations/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dunglas/symfonycon-lisbon/0b80de543538563175688801808e159d9a3cb40a/translations/.gitignore -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // webpack.config.js 2 | var Encore = require('@symfony/webpack-encore'); 3 | 4 | Encore 5 | // directory where compiled assets will be stored 6 | .setOutputPath('public/build/') 7 | // public path used by the web server to access the output path 8 | .setPublicPath('/build') 9 | // only needed for CDN's or sub-directory deploy 10 | //.setManifestKeyPrefix('build/') 11 | 12 | /* 13 | * ENTRY CONFIG 14 | * 15 | * Add 1 entry for each "page" of your app 16 | * (including one that's included on every page - e.g. "app") 17 | * 18 | * Each entry will result in one JavaScript file (e.g. app.js) 19 | * and one CSS file (e.g. app.css) if you JavaScript imports CSS. 20 | */ 21 | .addEntry('app', './assets/js/app.js') 22 | //.addEntry('page1', './assets/js/page1.js') 23 | //.addEntry('page2', './assets/js/page2.js') 24 | 25 | // will require an extra script tag for runtime.js 26 | // but, you probably want this, unless you're building a single-page app 27 | .enableSingleRuntimeChunk() 28 | 29 | /* 30 | * FEATURE CONFIG 31 | * 32 | * Enable & configure other features below. For a full 33 | * list of features, see: 34 | * https://symfony.com/doc/current/frontend.html#adding-more-features 35 | */ 36 | .cleanupOutputBeforeBuild() 37 | .enableBuildNotifications() 38 | .enableSourceMaps(!Encore.isProduction()) 39 | // enables hashed filenames (e.g. app.abc123.css) 40 | .enableVersioning(Encore.isProduction()) 41 | 42 | // enables Sass/SCSS support 43 | //.enableSassLoader() 44 | 45 | // uncomment if you use TypeScript 46 | //.enableTypeScriptLoader() 47 | 48 | // uncomment if you're having problems with a jQuery plugin 49 | //.autoProvidejQuery() 50 | 51 | .addEntry('js/feedback', './assets/feedback/index.js') 52 | .addEntry('js/reactions', './assets/reactions/index.js') 53 | .enableSassLoader() 54 | .enableVueLoader() 55 | ; 56 | 57 | module.exports = Encore.getWebpackConfig(); 58 | --------------------------------------------------------------------------------