Symfony without the framework

Symfony without the framework

Many of us do not know the world outside frameworks - you simply have to use one to be regarded a well-rounded engineer. Symfony is one of them, a quite interesting choice if you look for very popular, actively maintained and well-engineered foundation for years to come. But there is one advantage that not many of us are aware of - the level of reusability of its components. I want to show how to build fully-featured solutions using Symfony components without relying on virtually anything that would make you think that it is a stock framework. I will show how I maintained several projects in various companies I worked for.

Bb29f6afb2ea244a12c25e04d46af19c?s=128

Tomasz Kowalczyk

June 30, 2018
Tweet

Transcript

  1. symfony

  2. None
  3. None
  4. None
  5. None
  6. ➔ ➔

  7. ➔ ➔ ➔

  8. None
  9. None
  10. None
  11. None
  12. None
  13. None
  14. None
  15. None
  16. None
  17. None
  18. None
  19. None
  20. None
  21. None
  22. None
  23. None
  24. None
  25. None
  26. None
  27. None
  28. None
  29. None
  30. None
  31. <?php declare(strict_types=1); namespace X; use Symfony\Component\HttpFoundation\Request; use Thunder\Example\Application\Example\Kernel\ExampleKernel; require __DIR__.'/../../../vendor/autoload.php';

    require __DIR__.'/../../../var/cache/ExampleContainer.php'; $request = Request::createFromGlobals(); $kernel = new ExampleKernel(new \ExampleContainer()); $response = $kernel->handle($request); $response->send();
  32. None
  33. services: _defaults: autowire: false autoconfigure: false public: false index.controller: class:

    Thunder\Example\Application\Example\Controller\IndexController arguments: ['@routing'] public: true routing: class: Symfony\Component\Routing\Router arguments: ['@routing.loader', 'routing.yaml', { cache_dir: '../var/cache' }] public: true routing.loader: class: Symfony\Component\Routing\Loader\YamlFileLoader arguments: ['@routing.locator'] routing.locator: class: Symfony\Component\Config\FileLocator arguments: [['../etc']]
  34. <?php declare(strict_types=1); namespace Thunder\Example\Application\Console\Command; use …; final class ContainerGenerateCommand extends

    Command { protected function configure(): void { /* … */ } protected function execute(InputInterface $input, OutputInterface $output): void { $yaml = $input->getOption('services'); $target = $input->getOption('target'); $builder = new ContainerBuilder(); $loader = new YamlFileLoader($builder, new FileLocator(\dirname($yaml))); $loader->load(basename($yaml)); $builder->compile(); $dumper = new PhpDumper($builder); file_put_contents($target, $dumper->dump([ 'class' => str_replace('.php', '', basename($target)), ])); } }
  35. <?php declare(strict_types=1); namespace Thunder\Example\Application\Console\Command; use …; final class RoutingGenerateCommand extends

    Command { protected function configure(): void { /* … */ } protected function execute(InputInterface $input, OutputInterface $output): void { $resource = $input->getOption('resource'); $target = $input->getOption('target'); $loader = new YamlFileLoader(new FileLocator([\dirname($resource)])); $routing = new Router($loader, basename($resource), [ 'cache_dir' => $target, ]); $routing->match('/'); } }
  36. apps/console/bin/example container:generate --services=apps/example/etc/services.yaml --target=apps/example/var/cache/ExampleContainer.php apps/console/bin/example routing:generate --resource=apps/example/etc/routing.yaml --target=apps/example/var/cache

  37. class ExampleContainer extends Container { public function __construct() { $this->services

    = $this->privates = array(); $this->methodMap = array( 'index.controller' => 'getIndex_ControllerService', 'routing' => 'getRoutingService', ); $this->aliases = array(); } protected function getIndex_ControllerService() { return $this->services['index.controller'] = new \Thunder\Example\Application\Example\Controller\IndexController( ($this->services['routing'] ?? $this->getRoutingService() ); } protected function getRoutingService() { return $this->services['routing'] = new \Symfony\Component\Routing\Router( new \Symfony\Component\Routing\Loader\YamlFileLoader( new \Symfony\Component\Config\FileLocator(array(0 => '../etc')) ), 'routing.yaml', array('cache_dir' => '../var/cache') ); } }
  38. None
  39. final class ExampleKernel implements HttpKernelInterface { private $container; public function

    __construct(\AppContainer $container) { $this->container = $container; } public function handle(Request $request, $type, $catch) { // to be continued... } }
  40. $routing = $this->container->get('routing'); $context = new RequestContext(); $context->fromRequest($request); $routing->setContext($context); $route

    = $routing->matchRequest($request); $request->attributes->replace($route); [$ctrl, $action] = $route['controller']; return $container->get($ctrl)->{$action}($request);
  41. None
  42. services: user.controller: class: Application\Web\Controller\UserController arguments: ['@user.repository'] user.repository: alias: user.repository.doctrine user.repository.doctrine:

    class: Application\Web\Repository\DoctrineUserRepository arguments: ['@doctrine.em'] user.repository.memory: class: Application\Web\Repository\MemoryUserRepository
  43. index.index: path: / methods: [GET] defaults: controller: [index.controller, indexAction] _controller:

    index.controller:indexAction user.view: path: /user/{id} methods: [GET] defaults: controller: [user.controller, viewAction] _controller: user.controller:viewAction requirements: id: \d+
  44. final class UserController { private $users; public function __construct(UserRepositoryInterface $users)

    { $this->users = $users; } public function viewAction(Request $request) { // to be continued... } }
  45. $id = $request->attributes->get('id'); try { $user = $this->users->findById(new UserId($id)); }

    catch(UserNotFoundException $e) { return new JsonResponse(null, Response::HTTP_NOT_FOUND); } return new JsonResponse($user->toArray(), Response::HTTP_OK);
  46. None
  47. interface UserRepositoryInterface { public function findById(UserId $id): User; public function

    add(User $user): void }
  48. final class MemoryUserRepository implements UserRepositoryInterface { private $users = [];

    public function findById(UserId $id): User { if(false === array_key_exists($id->toString(), $this->users)) { throw new UserNotFoundException(); } return $this->users[$id->toString()]; } public function add(User $user): void { if(array_key_exists($id->toString(), $this->users)) { throw new DuplicateUserException(); } $this->users[$user->getId()->toString()] = $user; } }
  49. final class DoctrineOrmUserRepository implements UserRepositoryInterface { private $em; public function

    __construct(EntityManagerInterface $em) { $this->em = $em; } public function findById(UserId $id): User { return $this->em->find(User::class, $id->toString()); } public function add(User $user): void { $this->em->persist($user); } }
  50. None
  51. Application ﹂src ﹂User ﹂Application ﹂Command ﹂CreateUserCommand.php ﹂Domain ﹂User.php ﹂UserId.php ﹂UserRepositoryInterface.php

    ﹂Infrastructure ﹂Persistence ﹂MemoryUserRepository.php ﹂DoctrineOrmUserRepository.php ﹂tests ﹂User ﹂Domain ﹂UserTest.php
  52. Application ﹂apps ﹂example ﹂etc ﹂routing.yaml ﹂services.yaml ﹂src ﹂Controller ﹂UserController.php ﹂Service

    ﹂TokenService.php ﹂Kernel ﹂ExampleKernel.php ﹂web ﹂app.php
  53. Application ﹂apps ﹂console ﹂bin ﹂example ﹂src ﹂Command ﹂ContainerGenerateCommand.php ﹂RoutingGenerateCommand.php

  54. Application ﹂apps ﹂example ﹂features ﹂user-auth.feature ﹂tests ﹂UserTest.php ﹂var ﹂cache ﹂router.php

    ﹂container.php ﹂logs ﹂error.log
  55. None
  56. None
  57. Library ﹂src ﹂Provider ﹂ProviderInterface.php ﹂AgnosticProvider.php ﹂Agnostic.php ﹂tests ﹂AgnosticTest.php

  58. None
  59. Library ﹂bundle ﹂etc ﹂services.yaml ﹂src ﹂DependencyInjection ﹂Configuration.php ﹂AgnosticExtension.php ﹂AgnosticBundle.php ﹂tests

    ﹂AgnosticBundleTest.php
  60. { // ... "autoload": { "psr-4": { "Thunder\\Agnostic\\": "src", "Thunder\\Agnostic\\Tests\\":

    "tests", "Thunder\\AgnosticBundle\\": "bundle" } } // ... }
  61. None
  62. None
  63. None
  64. None
  65. None
  66. None
  67. None
  68. None
  69. None
  70. None
  71. Resources Images

  72. None