From 126301954b05bae4a1df0e5d0b68f5d71bfa9fa0 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 4 Jun 2018 18:52:42 +0200 Subject: [PATCH 1/7] Dockerfile init --- Dockerfile | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Dockerfile b/Dockerfile index e69de29..15dd9a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -0,0 +1,28 @@ +FROM janitortechnology/ubuntu-dev + +# Install dependencies. +RUN sudo apt-get update -q && sudo apt-get install -qy \ + php \ + composer \ + npm + +# Download source code. +RUN git clone -b master https://github.com/SundownDEV/Am-I-late /home/user/Am-I-late +WORKDIR /home/user/am-i-late + +# Configure the workspace. +ENV WORKSPACE /home/user/Am-I-late/ + +# Install dependencies. +RUN composer install \ + && cd client \ + && npm install + +# Configure and build +RUN php bin/console doctrine:database:create \ + && php bin/console doctrine:migration:migrate + +#COPY --chown=user:user supervisord.conf /tmp/supervisord-extra.conf +#RUN cat /tmp/supervisord-extra.conf | sudo tee -a /etc/supervisord.conf + +EXPOSE 3000 8000 \ No newline at end of file From f4434b7a46906d02e2d198a2cae0220d7468739c Mon Sep 17 00:00:00 2001 From: root Date: Tue, 5 Jun 2018 17:07:03 +0200 Subject: [PATCH 2/7] Docker files --- .travis.yml | 19 +++++ Dockerfile | 28 -------- docker-compose.travis.yml | 38 ++++++++++ docker-compose.yml | 53 ++++++++++++++ docker/elk/logstash/logstash.conf | 39 +++++++++++ docker/elk/logstash/patterns/default.conf | 85 +++++++++++++++++++++++ docker/elk/logstash/patterns/nginx.conf | 1 + docker/elk/logstash/patterns/symfony.conf | 34 +++++++++ docker/nginx/Dockerfile | 18 +++++ docker/nginx/nginx.conf | 29 ++++++++ docker/nginx/symfony.conf | 23 ++++++ docker/php-fpm/Dockerfile | 44 ++++++++++++ docker/php-fpm/symfony.ini | 1 + docker/php-fpm/symfony.pool.conf | 81 +++++++++++++++++++++ docker/react/Dockerfile | 17 +++++ 15 files changed, 482 insertions(+), 28 deletions(-) create mode 100644 .travis.yml delete mode 100644 Dockerfile create mode 100644 docker-compose.travis.yml create mode 100644 docker-compose.yml create mode 100644 docker/elk/logstash/logstash.conf create mode 100644 docker/elk/logstash/patterns/default.conf create mode 100644 docker/elk/logstash/patterns/nginx.conf create mode 100644 docker/elk/logstash/patterns/symfony.conf create mode 100644 docker/nginx/Dockerfile create mode 100644 docker/nginx/nginx.conf create mode 100644 docker/nginx/symfony.conf create mode 100644 docker/php-fpm/Dockerfile create mode 100644 docker/php-fpm/symfony.ini create mode 100644 docker/php-fpm/symfony.pool.conf create mode 100644 docker/react/Dockerfile diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..8d8adc0 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +sudo: required + +env: + DOCKER_COMPOSE_VERSION: 1.16.1 + +services: + - docker + +before_install: + - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose + - chmod +x docker-compose + - sudo mv docker-compose /usr/local/bin + - docker-compose -v + - docker -v + - mkdir symfony + +script: + - docker-compose -f docker-compose.travis.yml up -d + - docker-compose -f docker-compose.travis.yml ps \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 15dd9a0..0000000 --- a/Dockerfile +++ /dev/null @@ -1,28 +0,0 @@ -FROM janitortechnology/ubuntu-dev - -# Install dependencies. -RUN sudo apt-get update -q && sudo apt-get install -qy \ - php \ - composer \ - npm - -# Download source code. -RUN git clone -b master https://github.com/SundownDEV/Am-I-late /home/user/Am-I-late -WORKDIR /home/user/am-i-late - -# Configure the workspace. -ENV WORKSPACE /home/user/Am-I-late/ - -# Install dependencies. -RUN composer install \ - && cd client \ - && npm install - -# Configure and build -RUN php bin/console doctrine:database:create \ - && php bin/console doctrine:migration:migrate - -#COPY --chown=user:user supervisord.conf /tmp/supervisord-extra.conf -#RUN cat /tmp/supervisord-extra.conf | sudo tee -a /etc/supervisord.conf - -EXPOSE 3000 8000 \ No newline at end of file diff --git a/docker-compose.travis.yml b/docker-compose.travis.yml new file mode 100644 index 0000000..5e90adc --- /dev/null +++ b/docker-compose.travis.yml @@ -0,0 +1,38 @@ +version: '2' +services: + db: + image: mysql + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: symfony + MYSQL_USER: symfony + MYSQL_PASSWORD: symfony + php: + build: ./php-fpm + expose: + - "9000" + volumes: + - ./symfony:/var/www/symfony + - ./logs/symfony:/var/www/symfony/var/logs + links: + - db + nginx: + build: ./nginx + ports: + - "80:80" + links: + - php + volumes_from: + - php + volumes: + - ./logs/nginx/:/var/log/nginx + elk: + image: willdurand/elk + ports: + - "81:80" + volumes: + - ./elk/logstash:/etc/logstash + - ./elk/logstash/patterns:/opt/logstash/patterns + volumes_from: + - php + - nginx diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c8e0a96 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,53 @@ +version: '2' + +services: + db: + image: mysql + ports: + - "3311:3306" + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: symfony + MYSQL_USER: symfony + MYSQL_PASSWORD: symfony + php: + build: ./docker/php-fpm + ports: + - "9001:9000" + #volumes: + # - ./symfony:/var/www/symfony:cached + # - ./logs/symfony:/var/www/symfony/var/logs:cached + links: + - db + nginx: + build: ./docker/nginx + ports: + - "81:80" + links: + - php + volumes_from: + - php + volumes: + - ./logs/nginx/:/var/log/nginx:cached + elk: + image: willdurand/elk + ports: + - "82:80" + volumes: + - ./docker/elk/logstash:/etc/logstash:cached + - ./docker/elk/logstash/patterns:/opt/logstash/patterns:cached + volumes_from: + - php + - nginx + react-app: + container_name: react-app + build: + context: . + dockerfile: ./docker/react/Dockerfile + volumes: + - '.:/usr/src/app' + - '/usr/src/app/node_modules' + ports: + - '3000:3000' + environment: + - NODE_ENV=development diff --git a/docker/elk/logstash/logstash.conf b/docker/elk/logstash/logstash.conf new file mode 100644 index 0000000..71df0a8 --- /dev/null +++ b/docker/elk/logstash/logstash.conf @@ -0,0 +1,39 @@ +input { + file { + type => "nginx_access" + path => "/var/log/nginx/symfony_access.log" + start_position => beginning + } + file { + type => "symfony_dev" + path => "/var/www/symfony/var/log/dev.log" + start_position => beginning + } + file { + type => "symfony_prod" + path => "/var/www/symfony/var/log/prod.log" + start_position => beginning + } +} + +filter { + if [type] == "nginx_access" { + grok { + patterns_dir => "./patterns" + match => { "message" => "%{NGINXACCESS}"} + } + } + else if [type] in ["symfony_dev", "symfony_prod"] { + grok { + patterns_dir => "./patterns" + match => { "message" => "%{SYMFONY}"} + } + } +} + +output { + elasticsearch { + host => "localhost" + cluster => "logstash" + } +} \ No newline at end of file diff --git a/docker/elk/logstash/patterns/default.conf b/docker/elk/logstash/patterns/default.conf new file mode 100644 index 0000000..70a2900 --- /dev/null +++ b/docker/elk/logstash/patterns/default.conf @@ -0,0 +1,85 @@ +USERNAME [a-zA-Z0-9._-]+ +USER %{USERNAME} +INT (?:[+-]?(?:[0-9]+)) +BASE10NUM (?[+-]?(?:(?:[0-9]+(?:\.[0-9]+)?)|(?:\.[0-9]+))) +NUMBER (?:%{BASE10NUM}) +BASE16NUM (?(?"(?>\\.|[^\\"]+)+"|""|(?>'(?>\\.|[^\\']+)+')|''|(?>`(?>\\.|[^\\`]+)+`)|``)) +UUID [A-Fa-f0-9]{8}-(?:[A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12} +# Networking +MAC (?:%{CISCOMAC}|%{WINDOWSMAC}|%{COMMONMAC}) +CISCOMAC (?:(?:[A-Fa-f0-9]{4}\.){2}[A-Fa-f0-9]{4}) +WINDOWSMAC (?:(?:[A-Fa-f0-9]{2}-){5}[A-Fa-f0-9]{2}) +COMMONMAC (?:(?:[A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}) +IPV6 ((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)? +IPV4 (?/(?>[\w_%!$@:.,-]+|\\.)*)+ +TTY (?:/dev/(pts|tty([pq])?)(\w+)?/?(?:[0-9]+)) +WINPATH (?>[A-Za-z]+:|\\)(?:\\[^\\?*]*)+ +URIPROTO [A-Za-z]+(\+[A-Za-z+]+)? +URIHOST %{IPORHOST}(?::%{POSINT:port})? +# uripath comes loosely from RFC1738, but mostly from what Firefox +# doesn't turn into %XX +URIPATH (?:/[A-Za-z0-9$.+!*'(){},~:;=@#%_\-]*)+ +#URIPARAM \?(?:[A-Za-z0-9]+(?:=(?:[^&]*))?(?:&(?:[A-Za-z0-9]+(?:=(?:[^&]*))?)?)*)? +URIPARAM \?[A-Za-z0-9$.+!*'|(){},~@#%&/=:;_?\-\[\]]* +URIPATHPARAM %{URIPATH}(?:%{URIPARAM})? +URI %{URIPROTO}://(?:%{USER}(?::[^@]*)?@)?(?:%{URIHOST})?(?:%{URIPATHPARAM})? +# Months: January, Feb, 3, 03, 12, December +MONTH \b(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\b +MONTHNUM (?:0?[1-9]|1[0-2]) +MONTHNUM2 (?:0[1-9]|1[0-2]) +MONTHDAY (?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9]) +# Days: Monday, Tue, Thu, etc... +DAY (?:Mon(?:day)?|Tue(?:sday)?|Wed(?:nesday)?|Thu(?:rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?) +# Years? +YEAR (?>\d\d){1,2} +HOUR (?:2[0123]|[01]?[0-9]) +MINUTE (?:[0-5][0-9]) +# '60' is a leap second in most time standards and thus is valid. +SECOND (?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?) +TIME (?!<[0-9])%{HOUR}:%{MINUTE}(?::%{SECOND})(?![0-9]) +# datestamp is YYYY/MM/DD-HH:MM:SS.UUUU (or something like it) +DATE_US %{MONTHNUM}[/-]%{MONTHDAY}[/-]%{YEAR} +DATE_EU %{MONTHDAY}[./-]%{MONTHNUM}[./-]%{YEAR} +ISO8601_TIMEZONE (?:Z|[+-]%{HOUR}(?::?%{MINUTE})) +ISO8601_SECOND (?:%{SECOND}|60) +TIMESTAMP_ISO8601 %{YEAR}-%{MONTHNUM}-%{MONTHDAY}[T ]%{HOUR}:?%{MINUTE}(?::?%{SECOND})?%{ISO8601_TIMEZONE}? +DATE %{DATE_US}|%{DATE_EU} +DATESTAMP %{DATE}[- ]%{TIME} +TZ (?:[PMCE][SD]T|UTC) +DATESTAMP_RFC822 %{DAY} %{MONTH} %{MONTHDAY} %{YEAR} %{TIME} %{TZ} +DATESTAMP_RFC2822 %{DAY}, %{MONTHDAY} %{MONTH} %{YEAR} %{TIME} %{ISO8601_TIMEZONE} +DATESTAMP_OTHER %{DAY} %{MONTH} %{MONTHDAY} %{TIME} %{TZ} %{YEAR} +DATESTAMP_EVENTLOG %{YEAR}%{MONTHNUM2}%{MONTHDAY}%{HOUR}%{MINUTE}%{SECOND} +# Syslog Dates: Month Day HH:MM:SS +SYSLOGTIMESTAMP %{MONTH} +%{MONTHDAY} %{TIME} +PROG (?:[\w._/%-]+) +SYSLOGPROG %{PROG:program}(?:\[%{POSINT:pid}\])? +SYSLOGHOST %{IPORHOST} +SYSLOGFACILITY <%{NONNEGINT:facility}.%{NONNEGINT:priority}> +HTTPDATE %{MONTHDAY}/%{MONTH}/%{YEAR}:%{TIME} %{INT} +# Shortcuts +QS %{QUOTEDSTRING} +# Log formats +SYSLOGBASE %{SYSLOGTIMESTAMP:timestamp} (?:%{SYSLOGFACILITY} )?%{SYSLOGHOST:logsource} %{SYSLOGPROG}: +COMMONAPACHELOG %{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] "(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})" %{NUMBER:response} (?:%{NUMBER:bytes}|-) +COMBINEDAPACHELOG %{COMMONAPACHELOG} %{QS:referrer} %{QS:agent} +# Log Levels +LOGLEVEL ([Aa]lert|ALERT|[Tt]race|TRACE|[Dd]ebug|DEBUG|[Nn]otice|NOTICE|[Ii]nfo|INFO|[Ww]arn?(?:ing)?|WARN?(?:ING)?|[Ee]rr?(?:or)?|ERR?(?:OR)?|[Cc]rit?(?:ical)?|CRIT?(?:ICAL)?|[Ff]atal|FATAL|[Ss]evere|SEVERE|EMERG(?:ENCY)?|[Ee]merg(?:ency)?) \ No newline at end of file diff --git a/docker/elk/logstash/patterns/nginx.conf b/docker/elk/logstash/patterns/nginx.conf new file mode 100644 index 0000000..9025e08 --- /dev/null +++ b/docker/elk/logstash/patterns/nginx.conf @@ -0,0 +1 @@ +NGINXACCESS %{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] "(?:%{WORD:verb} %{URIPATHPARAM:request}(?: HTTP/%{NUMBER:httpversion})?|-)" %{NUMBER:response} (?:%{NUMBER:bytes}|-) "(?:%{URI:referrer}|-)" %{QS:agent} %{NUMBER:request_time} %{NUMBER:upstream_response_time} %{NUMBER:gzip_ratio} (?:%{WORD:cache_hit}|-)%{GREEDYDATA} \ No newline at end of file diff --git a/docker/elk/logstash/patterns/symfony.conf b/docker/elk/logstash/patterns/symfony.conf new file mode 100644 index 0000000..d8d5914 --- /dev/null +++ b/docker/elk/logstash/patterns/symfony.conf @@ -0,0 +1,34 @@ +VERYGREEDYDATA (.|\n)* + +SYMFONY_EXCEPTION [^:]* + +SYMFONY_LOG_TYPE request|security|app|profiler|doctrine|event +SYMFONY_LOG_LEVEL DEBUG|INFO|WARNING|ERROR|CRITICAL|ALERT +SYMFONY_LOG %{SYMFONY_LOG_TYPE:log_type}\.%{SYMFONY_LOG_LEVEL:log_level} + +SYMFONY_PARAMETER "[^"]*":( )?"[^"]*" +SYMFONY_PARAMETERS (%{SYMFONY_PARAMETER}(, )?)* +SYMFONY_CONTEXT {.*} +SYMFONY_REQUEST_METHOD GET|POST|PUT|DELETE|HEAD|OPTIONS|CONNECT +SYMFONY_REQUEST_PARAMETERS {"url":"%{GREEDYDATA:request_url}","ip":"%{IP:request_ip}","http_method":"%{SYMFONY_REQUEST_METHOD:request_method}"} + +SYMFONY_REQUEST_INFO Matched route "%{GREEDYDATA:route}" \(parameters: %{SYMFONY_PARAMETERS:parameters}\) +SYMFONY_REQUEST_UNCAUGHT_EXCEPTION %{SYMFONY_EXCEPTION:exception}: %{VERYGREEDYDATA:exception_message} \(uncaught exception\) at %{VERYGREEDYDATA:exception_file} line %{NUMBER:exception_file_line} +SYMFONY_REQUEST_CRITICAL Exception thrown when handling an exception \(ErrorException: %{GREEDYDATA:exception_message} in %{GREEDYDATA:exception_file} line %{NUMBER:exception_file_line}\) +SYMFONY_SECURITY_WARNING_USER_MISSING Username "%{GREEDYDATA:user}" could not be found. +SYMFONY_SECURITY_INFO_USER_AUTHENTICATED User "%{GREEDYDATA:user}" has been authenticated successfully +SYMFONY_SECURITY_INFO_AUTHENTICATION_FAILED Authentication request failed: %{GREEDYDATA:authentication_fail_reason} +SYMFONY_SECURITY_DEBUG Username "%{GREEDYDATA:user}" was reloaded from user provider. +SYMFONY_EVENT_DEBUG_NOTIFICATION Notified event "%{GREEDYDATA:event}" to listener "%{GREEDYDATA:listener}". +SYMFONY_EVENT_DEBUG_PROPAGATION_STOP Listener "%{GREEDYDATA:listener}" stopped propagation of the event "%{GREEDYDATA:event}". +SYMFONY_DOCTRINE_DEBUG (?<=doctrine.DEBUG: ).* + +SYMFONY_REQUEST %{SYMFONY_REQUEST_INFO}|%{SYMFONY_REQUEST_UNCAUGHT_EXCEPTION}|%{SYMFONY_REQUEST_CRITICAL} +SYMFONY_SECURITY %{SYMFONY_SECURITY_WARNING_USER_MISSING}|%{SYMFONY_SECURITY_INFO_USER_AUTHENTICATED}|%{SYMFONY_SECURITY_DEBUG}|%{SYMFONY_SECURITY_INFO_AUTHENTICATION_FAILED} +SYMFONY_EVENT %{SYMFONY_EVENT_DEBUG_NOTIFICATION}|%{SYMFONY_EVENT_DEBUG_PROPAGATION_STOP} +SYMFONY_DOCTRINE %{SYMFONY_DOCTRINE_DEBUG:doctrine_sql_query} +SYMFONY_VARIOUS_INFO Write SecurityContext in the session|Reloading user from user provider.|Read SecurityContext from the session|Populated SecurityContext with an anonymous Token|Access is denied (and user is neither anonymous, nor remember-me)|Unable to store the profiler information.|Remember-me cookie accepted. + +SYMFONY_LOG_MESSAGE %{SYMFONY_REQUEST}|%{SYMFONY_SECURITY}|%{SYMFONY_EVENT}|%{SYMFONY_DOCTRINE}|%{SYMFONY_VARIOUS_INFO:log_various_info}|%{VERYGREEDYDATA:log_unparsed_message} + +SYMFONY ^\[%{TIMESTAMP_ISO8601:date}\] %{SYMFONY_LOG}: %{SYMFONY_LOG_MESSAGE:log_message} (\[\]|%{SYMFONY_CONTEXT:log_context}) (\[\]|%{SYMFONY_REQUEST_PARAMETERS:log_request}) \ No newline at end of file diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile new file mode 100644 index 0000000..b361e74 --- /dev/null +++ b/docker/nginx/Dockerfile @@ -0,0 +1,18 @@ +FROM alpine:3.6 + +MAINTAINER Vincent Composieux + +RUN apk add --update nginx +RUN rm -rf /var/cache/apk/* && rm -rf /tmp/* + +ADD nginx.conf /etc/nginx/ +ADD symfony.conf /etc/nginx/conf.d/ + +RUN echo "upstream php-upstream { server php:9001; }" > /etc/nginx/conf.d/upstream.conf + +RUN adduser -D -g '' -G www-data www-data + +CMD ["nginx"] + +EXPOSE 80 +EXPOSE 443 diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf new file mode 100644 index 0000000..a9ecf0e --- /dev/null +++ b/docker/nginx/nginx.conf @@ -0,0 +1,29 @@ +user www-data; +worker_processes 4; +pid /run/nginx.pid; + +events { + worker_connections 2048; + multi_accept on; + use epoll; +} + +http { + server_tokens off; + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 15; + types_hash_max_size 2048; + include /etc/nginx/mime.types; + default_type application/octet-stream; + access_log off; + error_log off; + gzip on; + gzip_disable "msie6"; + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/sites-enabled/*; + open_file_cache max=100; +} + +daemon off; diff --git a/docker/nginx/symfony.conf b/docker/nginx/symfony.conf new file mode 100644 index 0000000..07e4838 --- /dev/null +++ b/docker/nginx/symfony.conf @@ -0,0 +1,23 @@ +server { + server_name localhost; + root /var/www/symfony/public; + + location / { + try_files $uri @rewriteapp; + } + + location @rewriteapp { + rewrite ^(.*)$ /index.php/$1 last; + } + + location ~ ^/index\.php(/|$) { + fastcgi_pass php-upstream; + fastcgi_split_path_info ^(.+\.php)(/.*)$; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param HTTPS off; + } + + error_log /var/log/nginx/symfony_error.log; + access_log /var/log/nginx/symfony_access.log; +} diff --git a/docker/php-fpm/Dockerfile b/docker/php-fpm/Dockerfile new file mode 100644 index 0000000..904a7ea --- /dev/null +++ b/docker/php-fpm/Dockerfile @@ -0,0 +1,44 @@ +FROM alpine:3.6 + +LABEL maintainer="Vincent Composieux " + +RUN apk add --update \ + php7-fpm \ + php7-apcu \ + php7-ctype \ + php7-curl \ + php7-dom \ + php7-gd \ + php7-iconv \ + php7-imagick \ + php7-json \ + php7-intl \ + php7-mcrypt \ + php7-mbstring \ + php7-opcache \ + php7-openssl \ + php7-pdo \ + php7-pdo_mysql \ + php7-mysqli \ + php7-xml \ + php7-zlib \ + php7-phar \ + php7-tokenizer \ + php7-session \ + php7-simplexml \ + make \ + curl + +RUN rm -rf /var/cache/apk/* && rm -rf /tmp/* + +RUN curl --insecure https://getcomposer.org/composer.phar -o /usr/bin/composer && chmod +x /usr/bin/composer + +ADD symfony.ini /etc/php7/php-fpm.d/ +ADD symfony.ini /etc/php7/cli/conf.d/ + +ADD symfony.pool.conf /etc/php7/php-fpm.d/ + +CMD ["php-fpm7", "-F"] + +WORKDIR /var/www/symfony +EXPOSE 9000 diff --git a/docker/php-fpm/symfony.ini b/docker/php-fpm/symfony.ini new file mode 100644 index 0000000..c3d7967 --- /dev/null +++ b/docker/php-fpm/symfony.ini @@ -0,0 +1 @@ +date.timezone = UTC diff --git a/docker/php-fpm/symfony.pool.conf b/docker/php-fpm/symfony.pool.conf new file mode 100644 index 0000000..c0bb18e --- /dev/null +++ b/docker/php-fpm/symfony.pool.conf @@ -0,0 +1,81 @@ +; Start a new pool named 'symfony'. +; the variable $pool can be used in any directive and will be replaced by the +; pool name ('symfony' here) +[symfony] + +; Unix user/group of processes +; Note: The user is mandatory. If the group is not set, the default user's group +; will be used. +user = nobody +group = nobody + +; The address on which to accept FastCGI requests. +; Valid syntaxes are: +; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific address on +; a specific port; +; 'port' - to listen on a TCP socket to all addresses on a +; specific port; +; '/path/to/unix/socket' - to listen on a unix socket. +; Note: This value is mandatory. +listen = 0.0.0.0:9001 + +; Choose how the process manager will control the number of child processes. +; Possible Values: +; static - a fixed number (pm.max_children) of child processes; +; dynamic - the number of child processes are set dynamically based on the +; following directives. With this process management, there will be +; always at least 1 children. +; pm.max_children - the maximum number of children that can +; be alive at the same time. +; pm.start_servers - the number of children created on startup. +; pm.min_spare_servers - the minimum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is less than this +; number then some children will be created. +; pm.max_spare_servers - the maximum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is greater than this +; number then some children will be killed. +; ondemand - no children are created at startup. Children will be forked when +; new requests will connect. The following parameter are used: +; pm.max_children - the maximum number of children that +; can be alive at the same time. +; pm.process_idle_timeout - The number of seconds after which +; an idle process will be killed. +; Note: This value is mandatory. +pm = dynamic + +; The number of child processes to be created when pm is set to 'static' and the +; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. +; This value sets the limit on the number of simultaneous requests that will be +; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. +; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP +; CGI. The below defaults are based on a server without much resources. Don't +; forget to tweak pm.* to fit your needs. +; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' +; Note: This value is mandatory. +pm.max_children = 20 + +; The number of child processes created on startup. +; Note: Used only when pm is set to 'dynamic' +; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 +pm.start_servers = 2 + +; The desired minimum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.min_spare_servers = 1 + +; The desired maximum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.max_spare_servers = 3 + +;--------------------- + +; Make specific Docker environment variables available to PHP +env[DB_1_ENV_MYSQL_DATABASE] = $DB_1_ENV_MYSQL_DATABASE +env[DB_1_ENV_MYSQL_USER] = $DB_1_ENV_MYSQL_USER +env[DB_1_ENV_MYSQL_PASSWORD] = $DB_1_ENV_MYSQL_PASSWORD + +catch_workers_output = yes diff --git a/docker/react/Dockerfile b/docker/react/Dockerfile new file mode 100644 index 0000000..e9b1c13 --- /dev/null +++ b/docker/react/Dockerfile @@ -0,0 +1,17 @@ +# base image +FROM node:9.6.1 + +# set working directory +RUN mkdir /usr/src/app +WORKDIR /usr/src/app + +# add `/usr/src/app/node_modules/.bin` to $PATH +ENV PATH /usr/src/app/node_modules/.bin:$PATH + +# install and cache app dependencies +COPY client/package.json /usr/src/app/package.json +RUN npm install --silent +#RUN npm install react-scripts@1.1.1 -g --silent + +# start app +CMD ["make", "front-run"] \ No newline at end of file From c512867bdb2dee3331696ce1c791ad8a64d7480d Mon Sep 17 00:00:00 2001 From: root Date: Tue, 5 Jun 2018 17:07:26 +0200 Subject: [PATCH 3/7] Ignore logs --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 67989d0..3587180 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ node_modules ###> symfony/web-server-bundle ### /.web-server-pid ###< symfony/web-server-bundle ### +/logs/ \ No newline at end of file From 668b0e66c3d73a25ade130c426e26851f019aff1 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 5 Jun 2018 17:08:16 +0200 Subject: [PATCH 4/7] Require form --- composer.json | 2 + composer.lock | 339 +++++++++++++++++++++++++++++++++++++++++++++++++- symfony.lock | 21 ++++ 3 files changed, 361 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 272232c..2d53b9a 100644 --- a/composer.json +++ b/composer.json @@ -6,8 +6,10 @@ "ext-iconv": "*", "api-platform/api-pack": "^1.1", "doctrine/doctrine-fixtures-bundle": "^3.0", + "sensio/framework-extra-bundle": "^5.1", "symfony/console": "^4.0", "symfony/flex": "^1.0", + "symfony/form": "^4.0", "symfony/framework-bundle": "^4.0", "symfony/lts": "^4@dev", "symfony/maker-bundle": "^1.4", diff --git a/composer.lock b/composer.lock index 366967e..5a5003a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "6fb85b3fa57133c433a33fb80ed9c9c6", + "content-hash": "390029222132b498c5dda4068ec3b022", "packages": [ { "name": "api-platform/api-pack", @@ -1821,6 +1821,75 @@ ], "time": "2017-10-23T01:57:42+00:00" }, + { + "name": "sensio/framework-extra-bundle", + "version": "v5.1.6", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git", + "reference": "bf4940572e43af679aaa13be98f3446a1c237bd8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/bf4940572e43af679aaa13be98f3446a1c237bd8", + "reference": "bf4940572e43af679aaa13be98f3446a1c237bd8", + "shasum": "" + }, + "require": { + "doctrine/common": "^2.2", + "symfony/config": "^3.3|^4.0", + "symfony/dependency-injection": "^3.3|^4.0", + "symfony/framework-bundle": "^3.3|^4.0", + "symfony/http-kernel": "^3.3|^4.0" + }, + "require-dev": { + "doctrine/doctrine-bundle": "^1.6", + "doctrine/orm": "^2.5", + "symfony/browser-kit": "^3.3|^4.0", + "symfony/dom-crawler": "^3.3|^4.0", + "symfony/expression-language": "^3.3|^4.0", + "symfony/finder": "^3.3|^4.0", + "symfony/phpunit-bridge": "^3.3|^4.0", + "symfony/psr-http-message-bridge": "^0.3", + "symfony/security-bundle": "^3.3|^4.0", + "symfony/twig-bundle": "^3.3|^4.0", + "symfony/yaml": "^3.3|^4.0", + "twig/twig": "~1.12|~2.0", + "zendframework/zend-diactoros": "^1.3" + }, + "suggest": { + "symfony/expression-language": "", + "symfony/psr-http-message-bridge": "To use the PSR-7 converters", + "symfony/security-bundle": "" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "5.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Sensio\\Bundle\\FrameworkExtraBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "This bundle provides a way to configure your controllers with annotations", + "keywords": [ + "annotations", + "controllers" + ], + "time": "2018-02-14T08:40:54+00:00" + }, { "name": "symfony/asset", "version": "v4.1.0", @@ -2542,6 +2611,87 @@ ], "time": "2018-05-02T19:08:56+00:00" }, + { + "name": "symfony/form", + "version": "v4.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/form.git", + "reference": "95f8237303e1f7101fee0e72d6ba630a3e4c5178" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/form/zipball/95f8237303e1f7101fee0e72d6ba630a3e4c5178", + "reference": "95f8237303e1f7101fee0e72d6ba630a3e4c5178", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/intl": "~3.4|~4.0", + "symfony/options-resolver": "~3.4|~4.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0", + "symfony/property-access": "~3.4|~4.0" + }, + "conflict": { + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/dependency-injection": "<3.4", + "symfony/doctrine-bridge": "<3.4", + "symfony/framework-bundle": "<3.4", + "symfony/http-kernel": "<3.4", + "symfony/twig-bridge": "<3.4.5|<4.0.5,>=4.0" + }, + "require-dev": { + "doctrine/collections": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/http-foundation": "~3.4|~4.0", + "symfony/http-kernel": "~3.4|~4.0", + "symfony/security-csrf": "~3.4|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/validator": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0" + }, + "suggest": { + "symfony/framework-bundle": "For templating with PHP.", + "symfony/security-csrf": "For protecting forms against CSRF attacks.", + "symfony/twig-bridge": "For templating with Twig.", + "symfony/validator": "For form validation." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Form\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Form Component", + "homepage": "https://symfony.com", + "time": "2018-05-30T07:26:09+00:00" + }, { "name": "symfony/framework-bundle", "version": "v4.1.0", @@ -2856,6 +3006,81 @@ ], "time": "2018-05-01T23:02:13+00:00" }, + { + "name": "symfony/intl", + "version": "v4.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/intl.git", + "reference": "e2a48225f7d525b23a6e34caaa7320205abcf179" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/intl/zipball/e2a48225f7d525b23a6e34caaa7320205abcf179", + "reference": "e2a48225f7d525b23a6e34caaa7320205abcf179", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-intl-icu": "~1.0" + }, + "require-dev": { + "symfony/filesystem": "~3.4|~4.0" + }, + "suggest": { + "ext-intl": "to use the component with locales other than \"en\"" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Intl\\": "" + }, + "classmap": [ + "Resources/stubs" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Eriksen Costa", + "email": "eriksen.costa@infranology.com.br" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A PHP replacement layer for the C intl extension that includes additional data from the ICU library.", + "homepage": "https://symfony.com", + "keywords": [ + "i18n", + "icu", + "internationalization", + "intl", + "l10n", + "localization" + ], + "time": "2018-05-30T07:26:09+00:00" + }, { "name": "symfony/lts", "version": "dev-master", @@ -3014,6 +3239,60 @@ ], "time": "2018-05-17T19:26:29+00:00" }, + { + "name": "symfony/options-resolver", + "version": "v4.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "9b9ab6043c57c8c5571bc846e6ebfd27dff3b589" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/9b9ab6043c57c8c5571bc846e6ebfd27dff3b589", + "reference": "9b9ab6043c57c8c5571bc846e6ebfd27dff3b589", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony OptionsResolver Component", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "time": "2018-05-30T07:26:09+00:00" + }, { "name": "symfony/orm-pack", "version": "v1.0.5", @@ -3097,6 +3376,64 @@ ], "time": "2018-04-30T19:57:29+00:00" }, + { + "name": "symfony/polyfill-intl-icu", + "version": "v1.8.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-icu.git", + "reference": "80ee17ae83c10cd513e5144f91a73607a21edb4e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/80ee17ae83c10cd513e5144f91a73607a21edb4e", + "reference": "80ee17ae83c10cd513e5144f91a73607a21edb4e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/intl": "~2.3|~3.0|~4.0" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's ICU-related data and classes", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "icu", + "intl", + "polyfill", + "portable", + "shim" + ], + "time": "2018-04-25T14:53:50+00:00" + }, { "name": "symfony/polyfill-mbstring", "version": "v1.8.0", diff --git a/symfony.lock b/symfony.lock index 3613c7d..f0aae24 100644 --- a/symfony.lock +++ b/symfony.lock @@ -122,6 +122,15 @@ "psr/simple-cache": { "version": "1.0.1" }, + "sensio/framework-extra-bundle": { + "version": "4.0", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "4.0", + "ref": "aaddfdf43cdecd4cf91f992052d76c2cadc04543" + } + }, "symfony/asset": { "version": "v4.0.9" }, @@ -173,6 +182,9 @@ "ref": "cc1afd81841db36fbef982fe56b48ade6716fac4" } }, + "symfony/form": { + "version": "v4.1.0" + }, "symfony/framework-bundle": { "version": "3.3", "recipe": { @@ -191,6 +203,9 @@ "symfony/inflector": { "version": "v4.0.9" }, + "symfony/intl": { + "version": "v4.1.0" + }, "symfony/lts": { "version": "4-dev" }, @@ -203,12 +218,18 @@ "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f" } }, + "symfony/options-resolver": { + "version": "v4.1.0" + }, "symfony/orm-pack": { "version": "v1.0.5" }, "symfony/polyfill-ctype": { "version": "v1.8.0" }, + "symfony/polyfill-intl-icu": { + "version": "v1.8.0" + }, "symfony/polyfill-mbstring": { "version": "v1.8.0" }, From a3e0c7e3d13fbd552159c5c6bb8625902d5f55b1 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 5 Jun 2018 17:08:47 +0200 Subject: [PATCH 5/7] Test fixtures --- src/DataFixtures/AppFixtures.php | 46 ++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/src/DataFixtures/AppFixtures.php b/src/DataFixtures/AppFixtures.php index 6e5b657..7404c31 100644 --- a/src/DataFixtures/AppFixtures.php +++ b/src/DataFixtures/AppFixtures.php @@ -12,21 +12,26 @@ namespace App\DataFixtures; use App\Entity\Question; use App\Entity\Response; +use App\Repository\ResponseRepository; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\Common\DataFixtures\FixtureInterface; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; class AppFixtures extends Fixture { + private $container; - public function __construct() + public function __construct(ContainerInterface $container = null) { - // + $this->container = $container; } public function load(ObjectManager $manager) { $this->loadQuestions($manager); - //$this->loadResponses($manager); + $this->loadResponses($manager); } private function loadQuestions(ObjectManager $manager) @@ -34,18 +39,15 @@ class AppFixtures extends Fixture foreach ($this->getData() as $data) { $question = new Question(); $question->setText($data['text']); + $question->setToken($data['token']); + $manager->persist($question); foreach ($data['responses'] as $response) { $newResponse = new Response(); $newResponse->setText($response['text']); - $newResponse->setQuestion($question->getId()); + $newResponse->setQuestion($question); $manager->persist($newResponse); - - $question->addResponse($newResponse); } - - $manager->persist($question); - //$this->addReference($username, $user); } $manager->flush(); @@ -53,35 +55,57 @@ class AppFixtures extends Fixture private function loadResponses(ObjectManager $manager) { - // + $container = $this->container->get('doctrine'); + $responseRepository = $container->getRepository('App:Response'); + $questionRepository = $container->getRepository('App:Question'); + + foreach ($this->getData() as $data) { + foreach ($data['responses'] as $response) { + if (null !== $response['child']) { + $newResponse = $responseRepository->findOneBy(['text' => $response['text']]); + $question = $questionRepository->findByToken($response['child']); + + if (null !== $newResponse && null !== $question) { + $newResponse->setChild($question); + $manager->flush(); + } + } + } + } } private function getData() { return [ [ + "token" => "aNs9skL", "text" => "ma question 1", "responses" => [ [ "text" => "option1", - "child" => 1, + "child" => "0ks6dkP", ], [ "text" => "option2", + "child" => null, ], ] ], [ + "token" => "0ks6dkP", "text" => "ma question 2", "responses" => [ [ "text" => "option1", + "child" => null, ], [ "text" => "option2", + "child" => null, ], [ "text" => "option3", + "child" => null, ], ] ] From e78e174fdb3ee2246b3585c67d63c89ed0a98630 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 5 Jun 2018 17:09:33 +0200 Subject: [PATCH 6/7] Entities --- src/Entity/Question.php | 18 +++++++++--------- src/Entity/Response.php | 6 +++++- src/Repository/QuestionRepository.php | 17 +++++++---------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/Entity/Question.php b/src/Entity/Question.php index 23a2ae2..f518c55 100644 --- a/src/Entity/Question.php +++ b/src/Entity/Question.php @@ -6,18 +6,13 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use ApiPlatform\Core\Annotation\ApiResource; -use Doctrine\ORM\Query; -use Symfony\Component\Validator\Constraints as Assert; use ApiPlatform\Core\Annotation\ApiSubresource; -use Symfony\Component\Serializer\Annotation\Groups; - -use Symfony\Component\Serializer\Serializer; -use Symfony\Component\Serializer\Encoder\XmlEncoder; -use Symfony\Component\Serializer\Encoder\JsonEncoder; -use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; /** - * @ApiResource() + * @ApiResource( + * collectionOperations={"get"={"method"="GET"}}, + * itemOperations={"get"={"method"="GET"}} + * ) * @ORM\Table(name="question") * @ORM\Entity(repositoryClass="App\Repository\QuestionRepository") */ @@ -60,6 +55,11 @@ class Question return $question; }*/ + public function __toString() + { + return $this->getText(); + } + public function getId() { return $this->id; diff --git a/src/Entity/Response.php b/src/Entity/Response.php index 7c46d9e..0e56362 100644 --- a/src/Entity/Response.php +++ b/src/Entity/Response.php @@ -9,7 +9,10 @@ use Symfony\Component\Validator\Constraints as Assert; use ApiPlatform\Core\Annotation\ApiSubresource; /** - * @ApiResource() + * @ApiResource( + * collectionOperations={"get"={"method"="GET"}}, + * itemOperations={"get"={"method"="GET"}} + * ) * @ORM\Table(name="response") * @ORM\Entity(repositoryClass="App\Repository\ResponseRepository") */ @@ -36,6 +39,7 @@ class Response /** * @ORM\OneToOne(targetEntity="App\Entity\Question", cascade={"persist", "remove"}) * @ORM\JoinTable(name="question") + * @ApiSubresource() */ private $child; diff --git a/src/Repository/QuestionRepository.php b/src/Repository/QuestionRepository.php index 28bf82e..f82c08c 100644 --- a/src/Repository/QuestionRepository.php +++ b/src/Repository/QuestionRepository.php @@ -19,22 +19,19 @@ class QuestionRepository extends ServiceEntityRepository parent::__construct($registry, Question::class); } -// /** -// * @return Question[] Returns an array of Question objects -// */ - /* - public function findByExampleField($value) + /** + * @return Question[] Returns an array of Question objects + */ + public function findByToken($token) { return $this->createQueryBuilder('q') - ->andWhere('q.exampleField = :val') - ->setParameter('val', $value) - ->orderBy('q.id', 'ASC') - ->setMaxResults(10) + ->andWhere('q.token = :token') + ->setParameter('token', $token) + ->setMaxResults(1) ->getQuery() ->getResult() ; } - */ /* public function findOneBySomeField($value): ?Question From 6f91d1209beffeb582a0d631713a760b5fc9d43e Mon Sep 17 00:00:00 2001 From: root Date: Tue, 5 Jun 2018 17:09:52 +0200 Subject: [PATCH 7/7] Question & response controllers --- config/bundles.php | 1 + src/Controller/QuestionController.php | 90 +++++++++++++++++++++++ src/Controller/ResponseController.php | 90 +++++++++++++++++++++++ src/Form/QuestionType.php | 25 +++++++ src/Form/ResponseType.php | 27 +++++++ templates/question/_delete_form.html.twig | 5 ++ templates/question/_form.html.twig | 4 + templates/question/edit.html.twig | 13 ++++ templates/question/index.html.twig | 37 ++++++++++ templates/question/new.html.twig | 11 +++ templates/question/show.html.twig | 30 ++++++++ templates/response/_delete_form.html.twig | 5 ++ templates/response/_form.html.twig | 4 + templates/response/edit.html.twig | 13 ++++ templates/response/index.html.twig | 35 +++++++++ templates/response/new.html.twig | 11 +++ templates/response/show.html.twig | 26 +++++++ 17 files changed, 427 insertions(+) create mode 100644 src/Controller/QuestionController.php create mode 100644 src/Controller/ResponseController.php create mode 100644 src/Form/QuestionType.php create mode 100644 src/Form/ResponseType.php create mode 100644 templates/question/_delete_form.html.twig create mode 100644 templates/question/_form.html.twig create mode 100644 templates/question/edit.html.twig create mode 100644 templates/question/index.html.twig create mode 100644 templates/question/new.html.twig create mode 100644 templates/question/show.html.twig create mode 100644 templates/response/_delete_form.html.twig create mode 100644 templates/response/_form.html.twig create mode 100644 templates/response/edit.html.twig create mode 100644 templates/response/index.html.twig create mode 100644 templates/response/new.html.twig create mode 100644 templates/response/show.html.twig diff --git a/config/bundles.php b/config/bundles.php index 02a81b8..b0b48d6 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -13,4 +13,5 @@ return [ ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], + Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], ]; diff --git a/src/Controller/QuestionController.php b/src/Controller/QuestionController.php new file mode 100644 index 0000000..c4cbd7b --- /dev/null +++ b/src/Controller/QuestionController.php @@ -0,0 +1,90 @@ +render('question/index.html.twig', ['questions' => $questionRepository->findAll()]); + } + + /** + * @Route("/new", name="question_new", methods="GET|POST") + */ + public function new(Request $request): Response + { + $question = new Question(); + $form = $this->createForm(QuestionType::class, $question); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $em->persist($question); + $em->flush(); + + return $this->redirectToRoute('question_index'); + } + + return $this->render('question/new.html.twig', [ + 'question' => $question, + 'form' => $form->createView(), + ]); + } + + /** + * @Route("/{id}", name="question_show", methods="GET") + */ + public function show(Question $question): Response + { + return $this->render('question/show.html.twig', ['question' => $question]); + } + + /** + * @Route("/{id}/edit", name="question_edit", methods="GET|POST") + */ + public function edit(Request $request, Question $question): Response + { + $form = $this->createForm(QuestionType::class, $question); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->getDoctrine()->getManager()->flush(); + + return $this->redirectToRoute('question_edit', ['id' => $question->getId()]); + } + + return $this->render('question/edit.html.twig', [ + 'question' => $question, + 'form' => $form->createView(), + ]); + } + + /** + * @Route("/{id}", name="question_delete", methods="DELETE") + */ + public function delete(Request $request, Question $question): Response + { + if ($this->isCsrfTokenValid('delete'.$question->getId(), $request->request->get('_token'))) { + $em = $this->getDoctrine()->getManager(); + $em->remove($question); + $em->flush(); + } + + return $this->redirectToRoute('question_index'); + } +} diff --git a/src/Controller/ResponseController.php b/src/Controller/ResponseController.php new file mode 100644 index 0000000..445971c --- /dev/null +++ b/src/Controller/ResponseController.php @@ -0,0 +1,90 @@ +render('response/index.html.twig', ['responses' => $responseRepository->findAll()]); + } + + /** + * @Route("/new", name="response_new", methods="GET|POST") + */ + public function new(Request $request): HttpResponse + { + $response = new Response(); + $form = $this->createForm(ResponseType::class, $response); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $em->persist($response); + $em->flush(); + + return $this->redirectToRoute('response_index'); + } + + return $this->render('response/new.html.twig', [ + 'response' => $response, + 'form' => $form->createView(), + ]); + } + + /** + * @Route("/{id}", name="response_show", methods="GET") + */ + public function show(Response $response): HttpResponse + { + return $this->render('response/show.html.twig', ['response' => $response]); + } + + /** + * @Route("/{id}/edit", name="response_edit", methods="GET|POST") + */ + public function edit(Request $request, Response $response): HttpResponse + { + $form = $this->createForm(ResponseType::class, $response); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->getDoctrine()->getManager()->flush(); + + return $this->redirectToRoute('response_edit', ['id' => $response->getId()]); + } + + return $this->render('response/edit.html.twig', [ + 'response' => $response, + 'form' => $form->createView(), + ]); + } + + /** + * @Route("/{id}", name="response_delete", methods="DELETE") + */ + public function delete(Request $request, Response $response): HttpResponse + { + if ($this->isCsrfTokenValid('delete'.$response->getId(), $request->request->get('_token'))) { + $em = $this->getDoctrine()->getManager(); + $em->remove($response); + $em->flush(); + } + + return $this->redirectToRoute('response_index'); + } +} diff --git a/src/Form/QuestionType.php b/src/Form/QuestionType.php new file mode 100644 index 0000000..0f9ec19 --- /dev/null +++ b/src/Form/QuestionType.php @@ -0,0 +1,25 @@ +add('text') + ; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => Question::class, + ]); + } +} diff --git a/src/Form/ResponseType.php b/src/Form/ResponseType.php new file mode 100644 index 0000000..0887001 --- /dev/null +++ b/src/Form/ResponseType.php @@ -0,0 +1,27 @@ +add('text') + ->add('question') + ->add('child') + ; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => Response::class, + ]); + } +} diff --git a/templates/question/_delete_form.html.twig b/templates/question/_delete_form.html.twig new file mode 100644 index 0000000..466f65f --- /dev/null +++ b/templates/question/_delete_form.html.twig @@ -0,0 +1,5 @@ +
+ + + +
\ No newline at end of file diff --git a/templates/question/_form.html.twig b/templates/question/_form.html.twig new file mode 100644 index 0000000..92948ad --- /dev/null +++ b/templates/question/_form.html.twig @@ -0,0 +1,4 @@ +{{ form_start(form) }} + {{ form_widget(form) }} + +{{ form_end(form) }} \ No newline at end of file diff --git a/templates/question/edit.html.twig b/templates/question/edit.html.twig new file mode 100644 index 0000000..d13e9d8 --- /dev/null +++ b/templates/question/edit.html.twig @@ -0,0 +1,13 @@ +{% extends 'base.html.twig' %} + +{% block title %}Edit Question{% endblock %} + +{% block body %} +

Edit Question

+ + {{ include('question/_form.html.twig', {'button_label': 'Update'}) }} + + back to list + + {{ include('question/_delete_form.html.twig') }} +{% endblock %} \ No newline at end of file diff --git a/templates/question/index.html.twig b/templates/question/index.html.twig new file mode 100644 index 0000000..b12bed8 --- /dev/null +++ b/templates/question/index.html.twig @@ -0,0 +1,37 @@ +{% extends 'base.html.twig' %} + +{% block title %}Question index{% endblock %} + +{% block body %} +

Question index

+ + + + + + + + + + + + {% for question in questions %} + + + + + + + {% else %} + + + + {% endfor %} + +
IdTextDateactions
{{ question.id }}{{ question.text }}{{ question.date ? question.date|date('Y-m-d H:i:s') : '' }} + show + edit +
no records found
+ + Create new +{% endblock %} \ No newline at end of file diff --git a/templates/question/new.html.twig b/templates/question/new.html.twig new file mode 100644 index 0000000..9039702 --- /dev/null +++ b/templates/question/new.html.twig @@ -0,0 +1,11 @@ +{% extends 'base.html.twig' %} + +{% block title %}New Question{% endblock %} + +{% block body %} +

Create new Question

+ + {{ include('question/_form.html.twig') }} + + back to list +{% endblock %} \ No newline at end of file diff --git a/templates/question/show.html.twig b/templates/question/show.html.twig new file mode 100644 index 0000000..bb6d3a2 --- /dev/null +++ b/templates/question/show.html.twig @@ -0,0 +1,30 @@ +{% extends 'base.html.twig' %} + +{% block title %}Question{% endblock %} + +{% block body %} +

Question

+ + + + + + + + + + + + + + + + +
Id{{ question.id }}
Text{{ question.text }}
Date{{ question.date ? question.date|date('Y-m-d H:i:s') : '' }}
+ + back to list + + edit + + {{ include('question/_delete_form.html.twig') }} +{% endblock %} \ No newline at end of file diff --git a/templates/response/_delete_form.html.twig b/templates/response/_delete_form.html.twig new file mode 100644 index 0000000..162e88d --- /dev/null +++ b/templates/response/_delete_form.html.twig @@ -0,0 +1,5 @@ +
+ + + +
\ No newline at end of file diff --git a/templates/response/_form.html.twig b/templates/response/_form.html.twig new file mode 100644 index 0000000..92948ad --- /dev/null +++ b/templates/response/_form.html.twig @@ -0,0 +1,4 @@ +{{ form_start(form) }} + {{ form_widget(form) }} + +{{ form_end(form) }} \ No newline at end of file diff --git a/templates/response/edit.html.twig b/templates/response/edit.html.twig new file mode 100644 index 0000000..361f9bb --- /dev/null +++ b/templates/response/edit.html.twig @@ -0,0 +1,13 @@ +{% extends 'base.html.twig' %} + +{% block title %}Edit Response{% endblock %} + +{% block body %} +

Edit Response

+ + {{ include('response/_form.html.twig', {'button_label': 'Update'}) }} + + back to list + + {{ include('response/_delete_form.html.twig') }} +{% endblock %} \ No newline at end of file diff --git a/templates/response/index.html.twig b/templates/response/index.html.twig new file mode 100644 index 0000000..dd086be --- /dev/null +++ b/templates/response/index.html.twig @@ -0,0 +1,35 @@ +{% extends 'base.html.twig' %} + +{% block title %}Response index{% endblock %} + +{% block body %} +

Response index

+ + + + + + + + + + + {% for response in responses %} + + + + + + {% else %} + + + + {% endfor %} + +
IdTextactions
{{ response.id }}{{ response.text }} + show + edit +
no records found
+ + Create new +{% endblock %} \ No newline at end of file diff --git a/templates/response/new.html.twig b/templates/response/new.html.twig new file mode 100644 index 0000000..e4eb105 --- /dev/null +++ b/templates/response/new.html.twig @@ -0,0 +1,11 @@ +{% extends 'base.html.twig' %} + +{% block title %}New Response{% endblock %} + +{% block body %} +

Create new Response

+ + {{ include('response/_form.html.twig') }} + + back to list +{% endblock %} \ No newline at end of file diff --git a/templates/response/show.html.twig b/templates/response/show.html.twig new file mode 100644 index 0000000..b7fa5d3 --- /dev/null +++ b/templates/response/show.html.twig @@ -0,0 +1,26 @@ +{% extends 'base.html.twig' %} + +{% block title %}Response{% endblock %} + +{% block body %} +

Response

+ + + + + + + + + + + + +
Id{{ response.id }}
Text{{ response.text }}
+ + back to list + + edit + + {{ include('response/_delete_form.html.twig') }} +{% endblock %} \ No newline at end of file