SymfonyLive San Francisco 2015 - Overriding Symfony in a million ways

SymfonyLive San Francisco 2015 - Overriding Symfony in a million ways

During this session, we will have a look at how easy it is to override everything in a Symfony application. We'll see how the directory structure can be adapted to your needs, but more importantly, we will learn how to override any Symfony feature by writing some custom event listeners or by tweaking the Dependency Container configuration.

34ade09dd3d11004ca8ee4174fd3d6a2?s=128

Sarah KHALIL

October 29, 2015
Tweet

Transcript

  1. OVERRIDING SYMFONY IN A MILLION WAYS Sarah Khalil 

  2. WHO AM I? ➤ Head of the ➤ Trainer and

    developer ➤ Enjoying sharer ➤ @saro0h
  3. WHO AM I? ➤ Head of the ➤ Trainer and

    developer ➤ Enjoying sharer ➤ @saro0h @catlannister
  4. ANY PLAN TODAY? ➤ Symfony Full Stack ➤ Override the

    structure ➤ Override everything in a bundle
  5. GLOBALLY Override the Symfony default directory structure (and a little

    more)
  6. ➤ AppKernel ➤ Cache directory ➤ Log directory ➤ Web

    directory ➤ Vendor directory ➤ src directory ➤ bin directory ➤ environment parameters ➤ charset ➤ session folder ➤ config files https://www.flickr.com/photos/rotscher/
  7. AppKernel ➤ Cache directory ➤ Log directory ➤ Web directory

    ➤ Vendor directory ➤ src directory ➤ bin directory ➤ environment parameters ➤ charset ➤ session folder ➤ config files https://www.flickr.com/photos/rotscher/
  8. « Override "app"

  9. « Override "app"

  10. CHANGE THE NAME THE KERNEL // Symfony\Component\HttpKernel\Kernel public function getName()

    { if (null === $this->name) { $this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir)); } return $this->name; } Change the name of the original app directory + requires statements in the front controllers. 1/4
  11. CHANGE THE NAME THE KERNEL Change the prefix of the

    original appKernel class. 2/4
  12. CHANGE THE NAME THE KERNEL In the composer.son file: 4/4

    { "extra": { "symfony-app-dir": "ezpublish", "incenteev-parameters": { "file": "ezpublish/config/parameters.yml" } } }
  13. AppKernel Cache directory ➤ Log directory ➤ Web directory ➤

    Vendor directory ➤ src directory ➤ bin directory ➤ environment parameters ➤ charset ➤ session folder ➤ config files https://www.flickr.com/photos/rotscher/
  14. OVERRIDE THE CACHE DIRECTORY The cache sits in the app/cache

    folder. In the app/AppKernel class, you need to implement the getCacheDir(). // Symfony\Component\HttpKernel\Kernel public function getCacheDir() { return $this->rootDir.'/cache/'.$this->environment; }
  15. AppKernel Cache directory Log directory ➤ Web directory ➤ Vendor

    directory ➤ src directory ➤ bin directory ➤ environment parameters ➤ charset ➤ session folder ➤ config files https://www.flickr.com/photos/rotscher/
  16. OVERRIDE THE LOG DIRECTORY The cache sits in the app/logs

    folder. In the app/AppKernel class, you need to implement the getLogDir(). // Symfony\Component\HttpKernel\Kernel public function getLogDir() { return $this->rootDir.'/logs'; }
  17. AppKernel Cache directory Log directory Web directory ➤ Vendor directory

    ➤ src directory ➤ bin directory ➤ environment parameters ➤ charset ➤ session folder ➤ config files https://www.flickr.com/photos/rotscher/
  18. OVERRIDE THE WEB DIRECTORY 1. Make sure that the path

    to the app/ directory of your application is correct for the required files in your front controller (app.php|app_dev.php) 2. Change extra.symfony-web-dir in the composer.son file your application: 1/3 $loader = require_once __DIR__.'/../app/bootstrap.php.cache'; require_once __DIR__.'/../app/AppKernel.php'; { ... "extra": { ... "symfony-web-dir": "my_new_web_dir" } } http://symfony.com/doc/current/cookbook/configuration/override_dir_structure.html#override-the-web-directory
  19. OVERRIDE THE WEB DIRECTORY (WITH A SHARED HOST) Option a:

    Override the web folder of your application to the one that is exposed by the shared host (www | public_html folder). Option b : Create a symbolic link from the web directory to the directory that is « exposed » by the shared host (www | public_html folder). 2/3 http://symfony.com/doc/current/cookbook/configuration/override_dir_structure.html#override-the-web-directory
  20. OVERRIDE THE WEB DIRECTORY (WITH ASCETIC USED) You also need

    to override the assetic.read_from configuration parameter to target the new web directory you changed: 3/3 assetic: # ... read_from: "%kernel.root_dir%/../../public_html" Don’t forget to clear the cache!
  21. AppKernel Cache directory Log directory Web directory Vendor directory ➤

    src directory ➤ bin directory ➤ environment parameters ➤ charset ➤ session folder ➤ config files https://www.flickr.com/photos/rotscher/
  22. OVERRIDE THE VENDOR DIRECTORY 1. Override the config.vendor-dir in the

    composer.json file. 2.Override the path to the vendor directory in the app/autoload.php: { ... "config": { ... "vendor-dir": "/some/dir/vendor" }, ... } $loader = require '/some/dir/vendor/autoload.php';
  23. « This modification can be of interest if you are

    working in a virtual environment and cannot use NFS. Ex: if you're running a Symfony application using Vagrant/VirtualBox in a guest operating system. -Symfony documentation
  24. AppKernel Cache directory Log directory Web directory Vendor directory src

    directory ➤ bin directory ➤ environment parameters ➤ charset ➤ session folder ➤ config files https://www.flickr.com/photos/rotscher/
  25. CHANGE BUNDLES’ FOLDER Originally: Override: { "autoload": { "psr-0": {

    "": "src/" } } } { "autoload": { "psr-4": { "": "src/" } } } { "autoload": { "psr-0": { "": "newFolder/" } } } { "autoload": { "psr-4": { "": "newFolder/" } } } $ composer dumpautoload
  26. AppKernel Cache directory Log directory Web directory Vendor directory src

    directory bin directory ➤ environment parameters ➤ charset ➤ session folder ➤ config files https://www.flickr.com/photos/rotscher/
  27. CHANGE THE BIN FOLDER Originally: Override: { "config": { "bin-dir":

    "bin" } } { "config": { "bin-dir": "exec" } }
  28. AppKernel Cache directory Log directory Web directory Vendor directory src

    directory bin directory Environment parameters ➤ charset ➤ session folder ➤ config files https://www.flickr.com/photos/rotscher/
  29. OVERRIDE THE ENVIRONMENT VARIABLES ➤ All environment variables begin with

    SYMFONY__ ➤ In the app/AppKernel class, implement getEnvParameters (call the parent first). // Symfony\Component\HttpKernel\Kernel protected function getEnvParameters() { $parameters = array(); foreach ($_SERVER as $key => $value) { if (0 === strpos($key, 'SYMFONY__')) { $parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value; } } return $parameters; }
  30. AppKernel Cache directory Log directory Web directory Vendor directory src

    directory bin directory Environment parameters Charset ➤ session folder ➤ config files https://www.flickr.com/photos/rotscher/
  31. CHANGE THE CHARSET ➤ In the app/AppKernel class, you need

    to implement the getCharset(). // Symfony\Component\HttpKernel\Kernel public function getCharset() { return 'UTF-8'; }
  32. AppKernel Cache directory Log directory Web directory Vendor directory src

    directory bin directory Environment parameters Charset Session folder ➤ config files https://www.flickr.com/photos/rotscher/
  33. OVERRIDE THE SESSION STORAGE FOLDER Originally: Override: framework: session: handler_id:

    session.handler.native_file save_path: "%kernel.cache_dir%/sessions" framework: session: handler_id: session.handler.native_file save_path: "%kernel.root_dir%/sessions"
  34. AppKernel Cache directory Log directory Web directory Vendor directory src

    directory bin directory Environment parameters Charset Session folder Config files https://www.flickr.com/photos/rotscher/
  35. OVERRIDE THE CONFIGURATION FILE Override the folder, the name of

    the file… // app/config/AppKernel.php public function registerContainerConfiguration(LoaderInterface $loader) { $loader->load($this->getRootDir().'/config/config_'.$this->getEnvironment().'.yml'); }
  36. None
  37. None
  38. None
  39. None
  40. OVERRIDE BUNDLE Override everything you can find in a bundle.

  41. ➤ Routes ➤ Templates ➤ Controllers ➤ Services ➤ Configuration

    ➤ Forms ➤ Validation ➤ Translation
  42. ➤ Routes ➤ Templates ➤ Controllers ➤ Services ➤ Configuration

    ➤ Forms ➤ Validation ➤ Translation
  43. Routes ➤ Templates ➤ Controllers ➤ Services ➤ Configuration ➤

    Forms ➤ Validation ➤ Translation
  44. Routes ➤ Templates ➤ Controllers ➤ Services ➤ Configuration ➤

    Forms ➤ Validation ➤ Translation
  45. « You have 2 options.

  46. LET’S SAY YOU NEED TO OVERRIDE THE ROUTES OF THE

    FOSUSERBUNDLE FOR YOUR OWN BUNDLE
  47. EASIEST WAY ➤Don’t import the routes of the FOSUserBundle in

    your app/ config/routing.yml file. ➤Copy / Paste all the routes from FOSUserBundle in your app/config/routing.yml file and modify if directly. Option 1 http://symfony.com/doc/current/cookbook/bundles/override.html#routing
  48. « WORKING WITH RESOURCES » WAY 1. Import the routes

    of the FOSUserBundle in your app/ config/routing.yml file. 2. Create a new routing file following this pattern: •app/Resources/BundleName/path/to/routing/file/to/override.extension •exemple : in app/Resources/FosUserBundle/config/routing/all.xml 3. In there, override the routes you need to. Option 2 # app/config/routing.yml fos_user: resource: "@FOSUserBundle/Resources/config/routing/all.xml" If you refer to resources without using the @BundleName shortcut, they can't be overridden in this way.
  49. Routes Templates ➤ Controllers ➤ Services ➤ Configuration ➤ Forms

    ➤ Validation ➤ Translation
  50. Routes Templates ➤ Controllers ➤ Services ➤ Configuration ➤ Forms

    ➤ Validation ➤ Translation
  51. OVERRIDE A TEMPLATE Let’s say you need to override the

    following file: vendor/Symfony/…/TwigBundle/Resources/ views/Exception/error.html.twig
  52. « You have 2 options.

  53. OVERRIDE A TEMPLATE You need to create the following file:

    app/Resources/TwigBundle/views/Exception/ error.html.twig
  54. HOW IT WORKS? (« WORKING WITH RESOURCES WAY ») Create:

    app/Resources/TwigBundle/views/Exception/error.html.twig Option 1
  55. HOW IT WORKS? (« WORKING WITH RESOURCES WAY ») When

    calling the AcmeBlogBundle:Blog:index.html.twig logical path, Symfony looks in the following places (respectively): 1.app/Resources/TwigBundle/views/Exception/error.html.twig 2.[…]/TwigBundle/Resources/Exception/Exceptions/ error.html.twig Option 1 http://symfony.com/doc/current/book/templating.html#overriding-bundle-templates
  56. OVERRIDE A TEMPLATE (USING BUNDLE INHERITANCE) 1. Implement the method

    getParent() in the bundle class. 2. Recreate the file under the same arborescence: Option 2 // src/AppBundle/AppBundle.php public function getParent() { return 'TwigBundle'; } src/AppBundle/Resources/views/Exception/error.html.twig http://symfony.com/doc/current/cookbook/bundles/inheritance.html#overriding-resources-templates-routing-etc
  57. Routes Templates Controllers ➤ Services ➤ Configuration ➤ Forms ➤

    Validation ➤ Translation
  58. Routes Templates Controllers ➤ Services ➤ Configuration ➤ Forms ➤

    Validation ➤ Translation
  59. OVERRIDE A CONTROLLER ➤ Let’s say you need to override

    the showAction of the TwigBundle: // Symfony\Bundle\TwigBundle\Controller\ExceptionController public function showAction( Request $request, FlattenException $exception, DebugLoggerInterface $logger = null ){ // Some logic and returns a response }
  60. OVERRIDE A CONTROLLER (USING THE BUNDLE INHERITANCE) 1. Implement the

    method getParent() in your bundle class. 2. Create the src/AppBundle/Controller/ExceptionController.php with the showAction. 3. (Optional) Extends from the ExceptionController to be able to call the parent::showAction(), get the response and modify it as you need. // src/AppBundle/AppBundle.php public function getParent() { return 'TwigBundle'; }
  61. To be able to override the controller that way, it

    must be referenced as the following: TwigBundle:Exception:show
  62. Routes Templates Controllers Services ➤ Configuration ➤ Forms ➤ Validation

    ➤ Translation
  63. Routes Templates Controllers Services ➤ Configuration ➤ Forms ➤ Validation

    ➤ Translation
  64. 4 OPTIONS…

  65. 4 OPTIONS… And no, I won’t show you all of

    them ;)
  66. COMPILER PASS http://symfony.com/doc/current/cookbook/service_container/compiler_passes.html 1

  67. DECORATION OF SERVICE 2

  68. DECORATION OF SERVICE bar: public: false class: stdClass decorates: foo

    arguments: ["@bar.inner"] http://symfony.com/doc/current/components/dependency_injection/advanced.html#decorating-services
  69. Routes Templates Controllers Services Configuration ➤ Forms ➤ Validation ➤

    Translation
  70. Routes Templates Controllers Services Configuration ➤ Forms ➤ Validation ➤

    Translation
  71. « You have 2 options.

  72. OVERRIDE THE CONFIGURATION The config_[your_env].yml is imported in the registerContainerConfiguration()

    method in the app/appKernel class. It is read from top to bottom. If a configuration variable is declared, you can override it after it. That’s basically what it is done with the config.yml file and the config_[your_env].yml Option 1
  73. OVERRIDE THE CONFIGURATION 1. Create a compiler pass. 2. As

    you get the containerBuilder, you can use the method setParameter($name, $value). Option 2
  74. Routes Templates Controllers Services Configuration Forms ➤ Validation ➤ Translation

  75. Routes Templates Controllers Services Configuration Forms ➤ Validation ➤ Translation

  76. OVERRIDE A TYPE (REPLACE IT) You need to declare the

    original type as a service, that way, you can override it as explained in the service chapter. Option 1
  77. OVERRIDE A TYPE (DYNAMIQUE INHERITANCE) Option 2 namespace AppBundle\Form\Type; use

    Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; class RegistrationFormType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('name'); $builder->remove('surname'); } public function getParent() { return 'fos_user_registration'; } public function getName() { return 'app_user_registration'; } }
  78. Routes Templates Controllers Services Configuration Forms Validation ➤ Translation

  79. Routes Templates Controllers Services Configuration Forms Validation ➤ Translation

  80. OVERRIDE THE VALIDATION OF A FIELD ➤Symfony loads all validation

    configuration files from every bundle and combines them into one validation metadata tree. ➤This means you are able to add new constraints to a property, but you cannot override them. http://symfony.com/doc/2.3/cookbook/bundles/override.html#validation-metadata
  81. OVERRIDE A VALIDATION CONFIGURATION Add new validation constraint in the

    bundle that is overriding the original with the same validation group.
  82. FOS\UserBundle\Model\User: properties: plainPassword: - NotBlank: groups: [Registration] - Length: min:

    6 minMessage: fos_user.password.short groups: [Registration]
  83. Routes Templates Controllers Services Configuration Forms Validation Translation

  84. Routes Templates Controllers Services Configuration Forms Validation Translation

  85. « You have 2 options.

  86. OVERRIDE A TRANSLATION FILE It’s all about domains! (messages.en.xlf) 1.All

    you need to do is to recreate the translation file in YourBundle/Resources/translations. 2.Make sure that your bundle is loaded before the original bundle (no bundle inheritance). Option 1
  87. OVERRIDE A TRANSLATION FILE Translation files are resources! Create the

    same file in the app/Resources/translations folder. Option 2
  88. None
  89. REMARKS

  90. ➤You can use event listeners|subscribers to override controllers (kernel.controller). ➤You

    can use the app/autoload.php file to override classes. ➤You could use other options according to the thing you need to override…
  91. PARICON2015.SYMFONY.COM

  92. Thank you! @saro0h speakerdeck.com/saro0h/ This is a zero guys! saro0h