├── assets
└── .gitignore
├── tests
└── .gitignore
├── translations
└── .gitignore
├── src
├── Domain
│ └── Model
│ │ ├── .gitignore
│ │ └── Article
│ │ ├── ArticleRepositoryInterface.php
│ │ └── Article.php
├── Application
│ ├── Service
│ │ ├── .gitignore
│ │ └── ArticleService.php
│ └── DTO
│ │ ├── ArticleDTO.php
│ │ └── ArticleAssembler.php
├── Infrastructure
│ ├── Migrations
│ │ └── .gitignore
│ ├── Repository
│ │ ├── .gitignore
│ │ └── ArticleRepository.php
│ └── Http
│ │ ├── Rest
│ │ └── Controller
│ │ │ ├── .gitignore
│ │ │ └── ArticleController.php
│ │ └── Web
│ │ └── Controller
│ │ └── .gitignore
└── Kernel.php
├── config
├── packages
│ ├── test
│ │ ├── swiftmailer.yaml
│ │ ├── framework.yaml
│ │ ├── web_profiler.yaml
│ │ └── monolog.yaml
│ ├── routing.yaml
│ ├── dev
│ │ ├── routing.yaml
│ │ ├── web_profiler.yaml
│ │ ├── swiftmailer.yaml
│ │ ├── easy_log_handler.yaml
│ │ └── monolog.yaml
│ ├── swiftmailer.yaml
│ ├── twig.yaml
│ ├── translation.yaml
│ ├── doctrine_migrations.yaml
│ ├── fos_rest.yaml
│ ├── prod
│ │ ├── monolog.yaml
│ │ └── doctrine.yaml
│ ├── security.yaml
│ ├── framework.yaml
│ └── doctrine.yaml
├── routes.yaml
├── routes
│ ├── dev
│ │ ├── twig.yaml
│ │ └── web_profiler.yaml
│ └── annotations.yaml
├── services_test.yaml
├── bundles.php
└── services.yaml
├── readme.txt
├── package.json
├── templates
└── base.html.twig
├── .gitignore
├── bin
├── phpunit
└── console
├── webpack.config.js
├── phpunit.xml.dist
├── .env.dist
├── public
└── index.php
├── composer.json
└── symfony.lock
/assets/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/translations/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Domain/Model/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Application/Service/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Infrastructure/Migrations/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Infrastructure/Repository/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Infrastructure/Http/Rest/Controller/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Infrastructure/Http/Web/Controller/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/config/packages/test/swiftmailer.yaml:
--------------------------------------------------------------------------------
1 | swiftmailer:
2 | disable_delivery: true
3 |
--------------------------------------------------------------------------------
/config/packages/routing.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | router:
3 | strict_requirements: ~
4 |
--------------------------------------------------------------------------------
/config/packages/dev/routing.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | router:
3 | strict_requirements: true
4 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | * Enter database credentials in env file
2 | * Run 'php bin/console doctrine:schema:create'
--------------------------------------------------------------------------------
/config/routes.yaml:
--------------------------------------------------------------------------------
1 | #index:
2 | # path: /
3 | # controller: App\Controller\DefaultController::index
4 |
--------------------------------------------------------------------------------
/config/packages/swiftmailer.yaml:
--------------------------------------------------------------------------------
1 | swiftmailer:
2 | url: '%env(MAILER_URL)%'
3 | spool: { type: 'memory' }
4 |
--------------------------------------------------------------------------------
/config/routes/dev/twig.yaml:
--------------------------------------------------------------------------------
1 | _errors:
2 | resource: '@TwigBundle/Resources/config/routing/errors.xml'
3 | prefix: /_error
4 |
--------------------------------------------------------------------------------
/config/packages/test/framework.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | test: true
3 | session:
4 | storage_id: session.storage.mock_file
5 |
--------------------------------------------------------------------------------
/config/packages/twig.yaml:
--------------------------------------------------------------------------------
1 | twig:
2 | paths: ['%kernel.project_dir%/templates']
3 | debug: '%kernel.debug%'
4 | strict_variables: '%kernel.debug%'
5 |
--------------------------------------------------------------------------------
/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/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/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/translation.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | default_locale: '%locale%'
3 | translator:
4 | paths:
5 | - '%kernel.project_dir%/translations'
6 | fallbacks:
7 | - '%locale%'
8 |
--------------------------------------------------------------------------------
/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/routes/annotations.yaml:
--------------------------------------------------------------------------------
1 | web_controller:
2 | resource: ../../src/Infrastructure/Http/Web/Controller/
3 | type: annotation
4 |
5 | rest_controller:
6 | resource: ../../src/Infrastructure/Http/Rest/Controller/
7 | type: annotation
8 | prefix: /api
--------------------------------------------------------------------------------
/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/packages/doctrine_migrations.yaml:
--------------------------------------------------------------------------------
1 | doctrine_migrations:
2 | dir_name: '%kernel.project_dir%/src/Infrastructure/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/services_test.yaml:
--------------------------------------------------------------------------------
1 | services:
2 | _defaults:
3 | public: true
4 |
5 | # If you need to access services in a test, create an alias
6 | # and then fetch that alias from the container. As a convention,
7 | # aliases are prefixed with test. For example:
8 | #
9 | # test.App\Service\MyService: '@App\Service\MyService'
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "@symfony/webpack-encore": "^0.17.0"
4 | },
5 | "license": "UNLICENSED",
6 | "private": true,
7 | "scripts": {
8 | "dev-server": "encore dev-server",
9 | "dev": "encore dev",
10 | "watch": "encore dev --watch",
11 | "build": "encore production"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/templates/base.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {% block title %}Welcome!{% endblock %}
6 | {% block stylesheets %}{% endblock %}
7 |
8 |
9 | {% block body %}{% endblock %}
10 | {% block javascripts %}{% endblock %}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/config/packages/fos_rest.yaml:
--------------------------------------------------------------------------------
1 | fos_rest:
2 | view:
3 | view_response_listener: true
4 | exception:
5 | exception_controller: 'fos_rest.exception.controller:showAction'
6 | codes:
7 | Doctrine\ORM\EntityNotFoundException: 404
8 | format_listener:
9 | rules:
10 | - { path: ^/api, prefer_extension: true, fallback_format: json, priorities: [ json ] }
11 | body_converter:
12 | enabled: true
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | ###> symfony/framework-bundle ###
3 | /.env
4 | /public/bundles/
5 | /var/
6 | /vendor/
7 | ###< symfony/framework-bundle ###
8 |
9 | ###> symfony/webpack-encore-pack ###
10 | /node_modules/
11 | /public/build/
12 | ###< symfony/webpack-encore-pack ###
13 |
14 | ###> symfony/phpunit-bridge ###
15 | .phpunit
16 | /phpunit.xml
17 | ###< symfony/phpunit-bridge ###
18 |
19 | ###> symfony/web-server-bundle ###
20 | /.web-server-pid
21 | ###< symfony/web-server-bundle ###
22 | .idea/
23 | phpdocker/
24 | docker-compose.yml
25 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/bin/phpunit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | title = $title;
30 | $this->content = $content;
31 | }
32 |
33 | /**
34 | * @return string
35 | */
36 | public function getTitle(): string
37 | {
38 | return $this->title;
39 | }
40 |
41 | /**
42 | * @return string
43 | */
44 | public function getContent(): string
45 | {
46 | return $this->content;
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/config/packages/prod/doctrine.yaml:
--------------------------------------------------------------------------------
1 | doctrine:
2 | orm:
3 | metadata_cache_driver:
4 | type: service
5 | id: doctrine.system_cache_provider
6 | query_cache_driver:
7 | type: service
8 | id: doctrine.system_cache_provider
9 | result_cache_driver:
10 | type: service
11 | id: doctrine.result_cache_provider
12 |
13 | services:
14 | doctrine.result_cache_provider:
15 | class: Symfony\Component\Cache\DoctrineProvider
16 | public: false
17 | arguments:
18 | - '@doctrine.result_cache_pool'
19 | doctrine.system_cache_provider:
20 | class: Symfony\Component\Cache\DoctrineProvider
21 | public: false
22 | arguments:
23 | - '@doctrine.system_cache_pool'
24 |
25 | framework:
26 | cache:
27 | pools:
28 | doctrine.result_cache_pool:
29 | adapter: cache.app
30 | doctrine.system_cache_pool:
31 | adapter: cache.system
32 |
--------------------------------------------------------------------------------
/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/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 | # With Symfony 3.3, remove the `resolve:` prefix
19 | url: '%env(resolve:DATABASE_URL)%'
20 | orm:
21 | auto_generate_proxy_classes: '%kernel.debug%'
22 | naming_strategy: doctrine.orm.naming_strategy.underscore
23 | auto_mapping: true
24 | mappings:
25 | Domain:
26 | is_bundle: false
27 | type: annotation
28 | dir: '%kernel.project_dir%/src/Domain/Model'
29 | prefix: 'App\Domain\Model'
30 | alias: Domain\Model
31 |
--------------------------------------------------------------------------------
/config/bundles.php:
--------------------------------------------------------------------------------
1 | ['all' => true],
5 | Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true],
6 | Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
7 | Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true],
8 | Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
9 | Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
10 | Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
11 | Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
12 | Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
13 | Symfony\Bundle\TwigBundle\TwigBundle::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 | FOS\RestBundle\FOSRestBundle::class => ['all' => true],
18 | ];
19 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | tests/
23 |
24 |
25 |
26 |
27 |
28 | ./src/
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/.env.dist:
--------------------------------------------------------------------------------
1 | # This file is a "template" of which env vars need to be defined for your application
2 | # Copy this file to .env file for development, create environment variables when deploying to production
3 | # https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration
4 |
5 | ###> symfony/framework-bundle ###
6 | APP_ENV=dev
7 | APP_SECRET=dcec9205a79267e18a6a65fb24ce97e0
8 | #TRUSTED_PROXIES=127.0.0.1,127.0.0.2
9 | #TRUSTED_HOSTS=localhost,example.com
10 | ###< symfony/framework-bundle ###
11 |
12 | ###> symfony/swiftmailer-bundle ###
13 | # For Gmail as a transport, use: "gmail://username:password@localhost"
14 | # For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode="
15 | # Delivery is disabled by default via "null://localhost"
16 | MAILER_URL=null://localhost
17 | ###< symfony/swiftmailer-bundle ###
18 |
19 | ###> doctrine/doctrine-bundle ###
20 | # Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
21 | # For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
22 | # Configure your db driver and server_version in config/packages/doctrine.yaml
23 | DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name
24 | ###< doctrine/doctrine-bundle ###
25 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | load(__DIR__.'/../.env');
23 | }
24 |
25 | $input = new ArgvInput();
26 | $env = $input->getParameterOption(['--env', '-e'], $_SERVER['APP_ENV'] ?? 'dev', true);
27 | $debug = (bool) ($_SERVER['APP_DEBUG'] ?? ('prod' !== $env)) && !$input->hasParameterOption('--no-debug', true);
28 |
29 | if ($debug) {
30 | umask(0000);
31 |
32 | if (class_exists(Debug::class)) {
33 | Debug::enable();
34 | }
35 | }
36 |
37 | $kernel = new Kernel($env, $debug);
38 | $application = new Application($kernel);
39 | $application->run($input);
40 |
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | load(__DIR__.'/../.env');
16 | }
17 |
18 | $env = $_SERVER['APP_ENV'] ?? 'dev';
19 | $debug = (bool) ($_SERVER['APP_DEBUG'] ?? ('prod' !== $env));
20 |
21 | if ($debug) {
22 | umask(0000);
23 |
24 | Debug::enable();
25 | }
26 |
27 | if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? false) {
28 | Request::setTrustedProxies(explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOST);
29 | }
30 |
31 | if ($trustedHosts = $_SERVER['TRUSTED_HOSTS'] ?? false) {
32 | Request::setTrustedHosts(explode(',', $trustedHosts));
33 | }
34 |
35 | $kernel = new Kernel($env, $debug);
36 | $request = Request::createFromGlobals();
37 | $response = $kernel->handle($request);
38 | $response->send();
39 | $kernel->terminate($request, $response);
40 |
--------------------------------------------------------------------------------
/src/Application/DTO/ArticleAssembler.php:
--------------------------------------------------------------------------------
1 | setContent($articleDTO->getContent());
28 | $article->setTitle($articleDTO->getTitle());
29 |
30 | return $article;
31 | }
32 |
33 | /**
34 | * @param Article $article
35 | * @param ArticleDTO $articleDTO
36 | * @return Article
37 | */
38 | public function updateArticle(Article $article, ArticleDTO $articleDTO): Article
39 | {
40 | return $this->readDTO($articleDTO, $article);
41 | }
42 |
43 | /**
44 | * @param ArticleDTO $articleDTO
45 | * @return Article
46 | */
47 | public function createArticle(ArticleDTO $articleDTO): Article
48 | {
49 | return $this->readDTO($articleDTO);
50 | }
51 |
52 | /**
53 | * @param Article $article
54 | * @return ArticleDTO
55 | */
56 | public function writeDTO(Article $article)
57 | {
58 | return new ArticleDTO(
59 | $article->getTitle(),
60 | $article->getContent()
61 | );
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/config/services.yaml:
--------------------------------------------------------------------------------
1 | # Put parameters here that don't need to change on each machine where the app is deployed
2 | # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
3 | parameters:
4 | locale: 'en'
5 |
6 | services:
7 | # default configuration for services in *this* file
8 | _defaults:
9 | autowire: true # Automatically injects dependencies in your services.
10 | autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
11 | public: false # Allows optimizing the container by removing unused services; this also means
12 | # fetching services directly from the container via $container->get() won't work.
13 | # The best practice is to be explicit about your dependencies anyway.
14 |
15 | # makes classes in src/ available to be used as services
16 | # this creates a service per class whose id is the fully-qualified class name
17 | App\:
18 | resource: '../src/*'
19 | exclude: '../src/{Entity,Infrastructure/Migrations,Application/DTO/*DTO.php,Tests,Kernel.php}'
20 |
21 | # controllers are imported separately to make sure services can be injected
22 | # as action arguments even if you don't extend any base controller class
23 | App\Infrastructure\Http\Web\Controller\:
24 | resource: '../src/Infrastructure/Http/Web/Controller'
25 | tags: ['controller.service_arguments']
26 |
27 | App\Infrastructure\Http\Rest\Controller\:
28 | resource: '../src/Infrastructure/Http/Rest/Controller'
29 | tags: ['controller.service_arguments']
30 | # add more service definitions when explicit configuration is needed
31 | # please note that last definitions always *replace* previous ones
32 |
--------------------------------------------------------------------------------
/src/Domain/Model/Article/Article.php:
--------------------------------------------------------------------------------
1 | id;
41 | }
42 |
43 | /**
44 | * @param int $id
45 | */
46 | public function setId(int $id): void
47 | {
48 | $this->id = $id;
49 | }
50 |
51 | /**
52 | * @return string
53 | */
54 | public function getTitle(): string
55 | {
56 | return $this->title;
57 | }
58 |
59 | /**
60 | * @param string $title
61 | * @throws \InvalidArgumentException
62 | */
63 | public function setTitle(string $title): void
64 | {
65 | if (\strlen($title) < 5) {
66 | throw new \InvalidArgumentException('Title needs to have 5 or more characters.');
67 | }
68 |
69 | $this->title = $title;
70 | }
71 |
72 | /**
73 | * @return string
74 | */
75 | public function getContent(): string
76 | {
77 | return $this->content;
78 | }
79 |
80 | /**
81 | * @param string $content
82 | */
83 | public function setContent(string $content): void
84 | {
85 | $this->content = $content;
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/Infrastructure/Repository/ArticleRepository.php:
--------------------------------------------------------------------------------
1 | entityManager = $entityManager;
34 | $this->objectRepository = $this->entityManager->getRepository(Article::class);
35 | }
36 |
37 | /**
38 | * @param int $articleId
39 | * @return Article
40 | */
41 | public function findById(int $articleId): ?Article
42 | {
43 | return $this->objectRepository->find($articleId);
44 | }
45 |
46 | /**
47 | * @return array
48 | */
49 | public function findAll(): array
50 | {
51 | return $this->objectRepository->findAll();
52 | }
53 |
54 | /**
55 | * @param Article $article
56 | */
57 | public function save(Article $article): void
58 | {
59 | $this->entityManager->persist($article);
60 | $this->entityManager->flush();
61 | }
62 |
63 | /**
64 | * @param Article $article
65 | */
66 | public function delete(Article $article): void
67 | {
68 | $this->entityManager->remove($article);
69 | $this->entityManager->flush();
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/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 (isset($envs['all']) || isset($envs[$this->environment])) {
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 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "project",
3 | "license": "proprietary",
4 | "require": {
5 | "php": "^7.1.3",
6 | "ext-iconv": "*",
7 | "friendsofsymfony/rest-bundle": "^2.3",
8 | "sensio/framework-extra-bundle": "^5.1",
9 | "symfony/asset": "^4.0",
10 | "symfony/console": "^4.0",
11 | "symfony/expression-language": "^4.0",
12 | "symfony/flex": "^1.0",
13 | "symfony/form": "^4.0",
14 | "symfony/framework-bundle": "^4.0",
15 | "symfony/lts": "^4@dev",
16 | "symfony/monolog-bundle": "^3.1",
17 | "symfony/orm-pack": "*",
18 | "symfony/process": "^4.0",
19 | "symfony/security-bundle": "^4.0",
20 | "symfony/serializer": "^4.0",
21 | "symfony/serializer-pack": "*",
22 | "symfony/swiftmailer-bundle": "^3.1",
23 | "symfony/validator": "^4.0",
24 | "symfony/web-link": "^4.0",
25 | "symfony/webpack-encore-pack": "*",
26 | "symfony/yaml": "^4.0"
27 | },
28 | "require-dev": {
29 | "symfony/browser-kit": "^4.0",
30 | "symfony/css-selector": "^4.0",
31 | "symfony/debug-pack": "*",
32 | "symfony/dotenv": "^4.0",
33 | "symfony/maker-bundle": "^1.0",
34 | "symfony/phpunit-bridge": "^4.0",
35 | "symfony/profiler-pack": "*",
36 | "symfony/web-server-bundle": "^4.0"
37 | },
38 | "config": {
39 | "preferred-install": {
40 | "*": "dist"
41 | },
42 | "sort-packages": true
43 | },
44 | "autoload": {
45 | "psr-4": {
46 | "App\\": "src/"
47 | }
48 | },
49 | "autoload-dev": {
50 | "psr-4": {
51 | "App\\Tests\\": "tests/"
52 | }
53 | },
54 | "replace": {
55 | "symfony/polyfill-iconv": "*",
56 | "symfony/polyfill-php71": "*",
57 | "symfony/polyfill-php70": "*",
58 | "symfony/polyfill-php56": "*"
59 | },
60 | "scripts": {
61 | "auto-scripts": {
62 | "cache:clear": "symfony-cmd",
63 | "assets:install --symlink --relative %PUBLIC_DIR%": "symfony-cmd"
64 | },
65 | "post-install-cmd": [
66 | "@auto-scripts"
67 | ],
68 | "post-update-cmd": [
69 | "@auto-scripts"
70 | ]
71 | },
72 | "conflict": {
73 | "symfony/symfony": "*"
74 | },
75 | "extra": {
76 | "symfony": {
77 | "id": "01C9DWJGWB5JQ10AEF8HV3J2FJ",
78 | "allow-contrib": false
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/Application/Service/ArticleService.php:
--------------------------------------------------------------------------------
1 | articleRepository = $articleRepository;
39 | $this->articleAssembler = $articleAssembler;
40 | }
41 |
42 | /**
43 | * @param int $articleId
44 | * @return Article
45 | * @throws EntityNotFoundException
46 | */
47 | public function getArticle(int $articleId): Article
48 | {
49 | $article = $this->articleRepository->findById($articleId);
50 | if (!$article) {
51 | throw new EntityNotFoundException('Article with id '.$articleId.' does not exist!');
52 | }
53 | return $article;
54 | }
55 |
56 | /**
57 | * @return array|null
58 | */
59 | public function getAllArticles(): ?array
60 | {
61 | return $this->articleRepository->findAll();
62 | }
63 |
64 | /**
65 | * @param ArticleDTO $articleDTO
66 | * @return Article
67 | */
68 | public function addArticle(ArticleDTO $articleDTO): Article
69 | {
70 | $article = $this->articleAssembler->createArticle($articleDTO);
71 | $this->articleRepository->save($article);
72 |
73 | return $article;
74 | }
75 |
76 | /**
77 | * @param int $articleId
78 | * @param ArticleDTO $articleDTO
79 | * @return Article
80 | * @throws EntityNotFoundException
81 | */
82 | public function updateArticle(int $articleId, ArticleDTO $articleDTO): Article
83 | {
84 | $article = $this->articleRepository->findById($articleId);
85 | if (!$article) {
86 | throw new EntityNotFoundException('Article with id '.$articleId.' does not exist!');
87 | }
88 | $article = $this->articleAssembler->updateArticle($article, $articleDTO);
89 | $this->articleRepository->save($article);
90 |
91 | return $article;
92 | }
93 |
94 | /**
95 | * @param int $articleId
96 | * @throws EntityNotFoundException
97 | */
98 | public function deleteArticle(int $articleId): void
99 | {
100 | $article = $this->articleRepository->findById($articleId);
101 | if (!$article) {
102 | throw new EntityNotFoundException('Article with id '.$articleId.' does not exist!');
103 | }
104 |
105 | $this->articleRepository->delete($article);
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/src/Infrastructure/Http/Rest/Controller/ArticleController.php:
--------------------------------------------------------------------------------
1 | articleService = $articleService;
34 | }
35 |
36 | /**
37 | * Creates an Article resource
38 | * @Rest\Post("/articles")
39 | * @ParamConverter("articleDTO", converter="fos_rest.request_body")
40 | * @param ArticleDTO $articleDTO
41 | * @return View
42 | */
43 | public function postArticle(ArticleDTO $articleDTO): View
44 | {
45 | $article = $this->articleService->addArticle($articleDTO);
46 |
47 | // In case our POST was a success we need to return a 201 HTTP CREATED response with the created object
48 | return View::create($article, Response::HTTP_CREATED);
49 | }
50 |
51 | /**
52 | * Retrieves an Article resource
53 | * @Rest\Get("/articles/{articleId}")
54 | * @param int $articleId
55 | * @return View
56 | * @throws \Doctrine\ORM\EntityNotFoundException
57 | */
58 | public function getArticle(int $articleId): View
59 | {
60 | $article = $this->articleService->getArticle($articleId);
61 |
62 | // In case our GET was a success we need to return a 200 HTTP OK response with the request object
63 | return View::create($article, Response::HTTP_OK);
64 | }
65 |
66 | /**
67 | * Retrieves a collection of Article resource
68 | * @Rest\Get("/articles")
69 | * @return View
70 | */
71 | public function getArticles(): View
72 | {
73 | $articles = $this->articleService->getAllArticles();
74 |
75 | // In case our GET was a success we need to return a 200 HTTP OK response with the collection of article object
76 | return View::create($articles, Response::HTTP_OK);
77 | }
78 |
79 | /**
80 | * Replaces Article resource
81 | * @Rest\Put("/articles/{id}")
82 | * @ParamConverter("articleDTO", converter="fos_rest.request_body")
83 | * @param int $articleId
84 | * @param ArticleDTO $articleDTO
85 | * @return View
86 | * @throws \Doctrine\ORM\EntityNotFoundException
87 | */
88 | public function putArticle(int $articleId, ArticleDTO $articleDTO): View
89 | {
90 | $article = $this->articleService->updateArticle($articleId, $articleDTO);
91 |
92 | // In case our PUT was a success we need to return a 200 HTTP OK response with the object as a result of PUT
93 | return View::create($article, Response::HTTP_OK);
94 | }
95 |
96 | /**
97 | * Removes the Article resource
98 | * @Rest\Delete("/articles/{articleId}")
99 | * @param int $articleId
100 | * @return View
101 | * @throws \Doctrine\ORM\EntityNotFoundException
102 | */
103 | public function deleteArticle(int $articleId): View
104 | {
105 | $this->articleService->deleteArticle($articleId);
106 |
107 | // In case our DELETE was a success we need to return a 204 HTTP NO CONTENT response. The object is deleted.
108 | return View::create([], Response::HTTP_NO_CONTENT);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/symfony.lock:
--------------------------------------------------------------------------------
1 | {
2 | "doctrine/annotations": {
3 | "version": "1.0",
4 | "recipe": {
5 | "repo": "github.com/symfony/recipes",
6 | "branch": "master",
7 | "version": "1.0",
8 | "ref": "cb4152ebcadbe620ea2261da1a1c5a9b8cea7672"
9 | }
10 | },
11 | "doctrine/cache": {
12 | "version": "v1.7.1"
13 | },
14 | "doctrine/collections": {
15 | "version": "v1.5.0"
16 | },
17 | "doctrine/common": {
18 | "version": "v2.8.1"
19 | },
20 | "doctrine/dbal": {
21 | "version": "v2.6.3"
22 | },
23 | "doctrine/doctrine-bundle": {
24 | "version": "1.6",
25 | "recipe": {
26 | "repo": "github.com/symfony/recipes",
27 | "branch": "master",
28 | "version": "1.6",
29 | "ref": "c745b67e4dec2771d4d57a60efd224faf445c929"
30 | }
31 | },
32 | "doctrine/doctrine-cache-bundle": {
33 | "version": "1.3.2"
34 | },
35 | "doctrine/doctrine-migrations-bundle": {
36 | "version": "1.2",
37 | "recipe": {
38 | "repo": "github.com/symfony/recipes",
39 | "branch": "master",
40 | "version": "1.2",
41 | "ref": "c1431086fec31f17fbcfe6d6d7e92059458facc1"
42 | }
43 | },
44 | "doctrine/inflector": {
45 | "version": "v1.3.0"
46 | },
47 | "doctrine/instantiator": {
48 | "version": "1.1.0"
49 | },
50 | "doctrine/lexer": {
51 | "version": "v1.0.1"
52 | },
53 | "doctrine/migrations": {
54 | "version": "v1.6.2"
55 | },
56 | "doctrine/orm": {
57 | "version": "v2.6.1"
58 | },
59 | "easycorp/easy-log-handler": {
60 | "version": "1.0",
61 | "recipe": {
62 | "repo": "github.com/symfony/recipes",
63 | "branch": "master",
64 | "version": "1.0",
65 | "ref": "70062abc2cd58794d2a90274502f81b55cd9951b"
66 | }
67 | },
68 | "egulias/email-validator": {
69 | "version": "2.1.3"
70 | },
71 | "fig/link-util": {
72 | "version": "1.0.0"
73 | },
74 | "friendsofsymfony/rest-bundle": {
75 | "version": "2.2",
76 | "recipe": {
77 | "repo": "github.com/symfony/recipes-contrib",
78 | "branch": "master",
79 | "version": "2.2",
80 | "ref": "258300d52be6ad59b32a888d5ddafbf9638540ff"
81 | }
82 | },
83 | "jdorn/sql-formatter": {
84 | "version": "v1.2.17"
85 | },
86 | "monolog/monolog": {
87 | "version": "1.23.0"
88 | },
89 | "nikic/php-parser": {
90 | "version": "v4.0.0"
91 | },
92 | "ocramius/proxy-manager": {
93 | "version": "2.2.0"
94 | },
95 | "phpdocumentor/reflection-common": {
96 | "version": "1.0.1"
97 | },
98 | "phpdocumentor/reflection-docblock": {
99 | "version": "4.3.0"
100 | },
101 | "phpdocumentor/type-resolver": {
102 | "version": "0.4.0"
103 | },
104 | "psr/cache": {
105 | "version": "1.0.1"
106 | },
107 | "psr/container": {
108 | "version": "1.0.0"
109 | },
110 | "psr/link": {
111 | "version": "1.0.0"
112 | },
113 | "psr/log": {
114 | "version": "1.0.2"
115 | },
116 | "psr/simple-cache": {
117 | "version": "1.0.1"
118 | },
119 | "sensio/framework-extra-bundle": {
120 | "version": "4.0",
121 | "recipe": {
122 | "repo": "github.com/symfony/recipes",
123 | "branch": "master",
124 | "version": "4.0",
125 | "ref": "aaddfdf43cdecd4cf91f992052d76c2cadc04543"
126 | }
127 | },
128 | "swiftmailer/swiftmailer": {
129 | "version": "v6.0.2"
130 | },
131 | "symfony/asset": {
132 | "version": "v4.0.6"
133 | },
134 | "symfony/browser-kit": {
135 | "version": "v4.0.6"
136 | },
137 | "symfony/cache": {
138 | "version": "v4.0.6"
139 | },
140 | "symfony/config": {
141 | "version": "v4.0.6"
142 | },
143 | "symfony/console": {
144 | "version": "3.3",
145 | "recipe": {
146 | "repo": "github.com/symfony/recipes",
147 | "branch": "master",
148 | "version": "3.3",
149 | "ref": "e3868d2f4a5104f19f844fe551099a00c6562527"
150 | }
151 | },
152 | "symfony/css-selector": {
153 | "version": "v4.0.6"
154 | },
155 | "symfony/debug": {
156 | "version": "v4.0.6"
157 | },
158 | "symfony/debug-bundle": {
159 | "version": "3.3",
160 | "recipe": {
161 | "repo": "github.com/symfony/recipes",
162 | "branch": "master",
163 | "version": "3.3",
164 | "ref": "71d29aaf710fd59cd3abff2b1ade907ed73103c6"
165 | }
166 | },
167 | "symfony/debug-pack": {
168 | "version": "v1.0.4"
169 | },
170 | "symfony/dependency-injection": {
171 | "version": "v4.0.6"
172 | },
173 | "symfony/doctrine-bridge": {
174 | "version": "v4.0.6"
175 | },
176 | "symfony/dom-crawler": {
177 | "version": "v4.0.6"
178 | },
179 | "symfony/dotenv": {
180 | "version": "v4.0.6"
181 | },
182 | "symfony/event-dispatcher": {
183 | "version": "v4.0.6"
184 | },
185 | "symfony/expression-language": {
186 | "version": "v4.0.6"
187 | },
188 | "symfony/filesystem": {
189 | "version": "v4.0.6"
190 | },
191 | "symfony/finder": {
192 | "version": "v4.0.6"
193 | },
194 | "symfony/flex": {
195 | "version": "1.0",
196 | "recipe": {
197 | "repo": "github.com/symfony/recipes",
198 | "branch": "master",
199 | "version": "1.0",
200 | "ref": "cc1afd81841db36fbef982fe56b48ade6716fac4"
201 | }
202 | },
203 | "symfony/form": {
204 | "version": "v4.0.6"
205 | },
206 | "symfony/framework-bundle": {
207 | "version": "3.3",
208 | "recipe": {
209 | "repo": "github.com/symfony/recipes",
210 | "branch": "master",
211 | "version": "3.3",
212 | "ref": "8a2f7fa50a528f0aad1d7a87ae3730c981b367ce"
213 | }
214 | },
215 | "symfony/http-foundation": {
216 | "version": "v4.0.6"
217 | },
218 | "symfony/http-kernel": {
219 | "version": "v4.0.6"
220 | },
221 | "symfony/inflector": {
222 | "version": "v4.0.6"
223 | },
224 | "symfony/intl": {
225 | "version": "v4.0.6"
226 | },
227 | "symfony/lts": {
228 | "version": "4-dev"
229 | },
230 | "symfony/maker-bundle": {
231 | "version": "1.0",
232 | "recipe": {
233 | "repo": "github.com/symfony/recipes",
234 | "branch": "master",
235 | "version": "1.0",
236 | "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
237 | }
238 | },
239 | "symfony/monolog-bridge": {
240 | "version": "v4.0.6"
241 | },
242 | "symfony/monolog-bundle": {
243 | "version": "3.1",
244 | "recipe": {
245 | "repo": "github.com/symfony/recipes",
246 | "branch": "master",
247 | "version": "3.1",
248 | "ref": "371d1a2b69984710646b09a1182ef1d4308c904f"
249 | }
250 | },
251 | "symfony/options-resolver": {
252 | "version": "v4.0.6"
253 | },
254 | "symfony/orm-pack": {
255 | "version": "v1.0.5"
256 | },
257 | "symfony/phpunit-bridge": {
258 | "version": "3.3",
259 | "recipe": {
260 | "repo": "github.com/symfony/recipes",
261 | "branch": "master",
262 | "version": "3.3",
263 | "ref": "179470cb6492db92dffee208cfdb436f175c93b4"
264 | }
265 | },
266 | "symfony/polyfill-intl-icu": {
267 | "version": "v1.7.0"
268 | },
269 | "symfony/polyfill-mbstring": {
270 | "version": "v1.7.0"
271 | },
272 | "symfony/polyfill-php72": {
273 | "version": "v1.7.0"
274 | },
275 | "symfony/process": {
276 | "version": "v4.0.6"
277 | },
278 | "symfony/profiler-pack": {
279 | "version": "v1.0.3"
280 | },
281 | "symfony/property-access": {
282 | "version": "v4.0.6"
283 | },
284 | "symfony/property-info": {
285 | "version": "v4.0.6"
286 | },
287 | "symfony/routing": {
288 | "version": "4.0",
289 | "recipe": {
290 | "repo": "github.com/symfony/recipes",
291 | "branch": "master",
292 | "version": "4.0",
293 | "ref": "cda8b550123383d25827705d05a42acf6819fe4e"
294 | }
295 | },
296 | "symfony/security": {
297 | "version": "v4.0.6"
298 | },
299 | "symfony/security-bundle": {
300 | "version": "3.3",
301 | "recipe": {
302 | "repo": "github.com/symfony/recipes",
303 | "branch": "master",
304 | "version": "3.3",
305 | "ref": "f8a63faa0d9521526499c0a8f403c9964ecb0527"
306 | }
307 | },
308 | "symfony/serializer": {
309 | "version": "v4.0.6"
310 | },
311 | "symfony/serializer-pack": {
312 | "version": "v1.0.1"
313 | },
314 | "symfony/stopwatch": {
315 | "version": "v4.0.6"
316 | },
317 | "symfony/swiftmailer-bundle": {
318 | "version": "2.5",
319 | "recipe": {
320 | "repo": "github.com/symfony/recipes",
321 | "branch": "master",
322 | "version": "2.5",
323 | "ref": "3db029c03e452b4a23f7fc45cec7c922c2247eb8"
324 | }
325 | },
326 | "symfony/templating": {
327 | "version": "v4.0.6"
328 | },
329 | "symfony/translation": {
330 | "version": "3.3",
331 | "recipe": {
332 | "repo": "github.com/symfony/recipes",
333 | "branch": "master",
334 | "version": "3.3",
335 | "ref": "6bcd6c570c017ea6ae5a7a6a027c929fd3542cd8"
336 | }
337 | },
338 | "symfony/twig-bridge": {
339 | "version": "v4.0.6"
340 | },
341 | "symfony/twig-bundle": {
342 | "version": "3.3",
343 | "recipe": {
344 | "repo": "github.com/symfony/recipes",
345 | "branch": "master",
346 | "version": "3.3",
347 | "ref": "f75ac166398e107796ca94cc57fa1edaa06ec47f"
348 | }
349 | },
350 | "symfony/validator": {
351 | "version": "v4.0.6"
352 | },
353 | "symfony/var-dumper": {
354 | "version": "v4.0.6"
355 | },
356 | "symfony/web-link": {
357 | "version": "v4.0.6"
358 | },
359 | "symfony/web-profiler-bundle": {
360 | "version": "3.3",
361 | "recipe": {
362 | "repo": "github.com/symfony/recipes",
363 | "branch": "master",
364 | "version": "3.3",
365 | "ref": "6bdfa1a95f6b2e677ab985cd1af2eae35d62e0f6"
366 | }
367 | },
368 | "symfony/web-server-bundle": {
369 | "version": "3.3",
370 | "recipe": {
371 | "repo": "github.com/symfony/recipes",
372 | "branch": "master",
373 | "version": "3.3",
374 | "ref": "dae9b39fd6717970be7601101ce5aa960bf53d9a"
375 | }
376 | },
377 | "symfony/webpack-encore-pack": {
378 | "version": "1.0",
379 | "recipe": {
380 | "repo": "github.com/symfony/recipes",
381 | "branch": "master",
382 | "version": "1.0",
383 | "ref": "db7185a4e6193a81c70d51e8b8ac0e656fea7ad3"
384 | }
385 | },
386 | "symfony/yaml": {
387 | "version": "v4.0.6"
388 | },
389 | "twig/twig": {
390 | "version": "v2.4.7"
391 | },
392 | "webmozart/assert": {
393 | "version": "1.3.0"
394 | },
395 | "willdurand/jsonp-callback-validator": {
396 | "version": "v1.1.0"
397 | },
398 | "willdurand/negotiation": {
399 | "version": "v2.3.1"
400 | },
401 | "zendframework/zend-code": {
402 | "version": "3.3.0"
403 | },
404 | "zendframework/zend-eventmanager": {
405 | "version": "3.2.0"
406 | }
407 | }
408 |
--------------------------------------------------------------------------------