Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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.

Sarah KHALIL

October 29, 2015
Tweet

More Decks by Sarah KHALIL

Other Decks in Technology

Transcript

  1. OVERRIDING SYMFONY IN A MILLION WAYS
    Sarah Khalil

    View Slide

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

    View Slide

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

    View Slide

  4. ANY PLAN TODAY?
    ➤ Symfony Full
    Stack
    ➤ Override the
    structure
    ➤ Override
    everything in a
    bundle

    View Slide

  5. GLOBALLY
    Override the Symfony default directory
    structure (and a little more)

    View Slide

  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/

    View Slide

  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/

    View Slide

  8. «
    Override "app"

    View Slide

  9. «
    Override "app"

    View Slide

  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

    View Slide

  11. CHANGE THE NAME THE KERNEL
    Change the prefix of the original appKernel class.
    2/4

    View Slide

  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"
    }
    }
    }

    View Slide

  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/

    View Slide

  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;
    }

    View Slide

  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/

    View Slide

  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';
    }

    View Slide

  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/

    View Slide

  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

    View Slide

  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

    View Slide

  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!

    View Slide

  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/

    View Slide

  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';

    View Slide

  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

    View Slide

  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/

    View Slide

  25. CHANGE BUNDLES’ FOLDER
    Originally:
    Override:
    {
    "autoload": {
    "psr-0": {
    "": "src/"
    }
    }
    }
    {
    "autoload": {
    "psr-4": {
    "": "src/"
    }
    }
    }
    {
    "autoload": {
    "psr-0": {
    "": "newFolder/"
    }
    }
    }
    {
    "autoload": {
    "psr-4": {
    "": "newFolder/"
    }
    }
    }
    $ composer dumpautoload

    View Slide

  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/

    View Slide

  27. CHANGE THE BIN FOLDER
    Originally:
    Override:
    {
    "config": {
    "bin-dir": "bin"
    }
    }
    {
    "config": {
    "bin-dir": "exec"
    }
    }

    View Slide

  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/

    View Slide

  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;
    }

    View Slide

  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/

    View Slide

  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';
    }

    View Slide

  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/

    View Slide

  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"

    View Slide

  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/

    View Slide

  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');
    }

    View Slide

  36. View Slide

  37. View Slide

  38. View Slide

  39. View Slide

  40. OVERRIDE
    BUNDLE
    Override everything you can find in a
    bundle.

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  45. «
    You have 2 options.

    View Slide

  46. LET’S SAY YOU NEED TO OVERRIDE THE
    ROUTES OF THE FOSUSERBUNDLE
    FOR YOUR OWN BUNDLE

    View Slide

  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

    View Slide

  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.

    View Slide

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

    View Slide

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

    View Slide

  51. OVERRIDE A TEMPLATE
    Let’s say you need to override the following file:
    vendor/Symfony/…/TwigBundle/Resources/
    views/Exception/error.html.twig

    View Slide

  52. «
    You have 2 options.

    View Slide

  53. OVERRIDE A TEMPLATE
    You need to create the following file:
    app/Resources/TwigBundle/views/Exception/
    error.html.twig

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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
    }

    View Slide

  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';
    }

    View Slide

  61. To be able to override the controller that
    way, it must be referenced as the following:
    TwigBundle:Exception:show

    View Slide

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

    View Slide

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

    View Slide

  64. 4 OPTIONS…

    View Slide

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

    View Slide

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

    View Slide

  67. DECORATION OF SERVICE
    2

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  71. «
    You have 2 options.

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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';
    }
    }

    View Slide

  78. Routes
    Templates
    Controllers
    Services
    Configuration
    Forms
    Validation
    ➤ Translation

    View Slide

  79. Routes
    Templates
    Controllers
    Services
    Configuration
    Forms
    Validation
    ➤ Translation

    View Slide

  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

    View Slide

  81. OVERRIDE A VALIDATION CONFIGURATION
    Add new validation constraint in the bundle that is
    overriding the original with the same validation group.

    View Slide

  82. FOS\UserBundle\Model\User:
    properties:
    plainPassword:
    - NotBlank:
    groups: [Registration]
    - Length:
    min: 6
    minMessage: fos_user.password.short
    groups: [Registration]

    View Slide

  83. Routes
    Templates
    Controllers
    Services
    Configuration
    Forms
    Validation
    Translation

    View Slide

  84. Routes
    Templates
    Controllers
    Services
    Configuration
    Forms
    Validation
    Translation

    View Slide

  85. «
    You have 2 options.

    View Slide

  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

    View Slide

  87. OVERRIDE A TRANSLATION FILE
    Translation files are resources!
    Create the same file in the app/Resources/translations
    folder.
    Option 2

    View Slide

  88. View Slide

  89. REMARKS

    View Slide

  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…

    View Slide

  91. PARICON2015.SYMFONY.COM

    View Slide

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

    View Slide